mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-12 19:49:59 +05:00
36400 lines
1.8 MiB
36400 lines
1.8 MiB
(function(global, factory) {
|
|
typeof exports === "object" && typeof module !== "undefined" ? factory(exports) : typeof define === "function" && define.amd ? define([ "exports" ], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self,
|
|
factory(global["cryptpad-worker"] = {}));
|
|
})(this, (function(exports) {
|
|
"use strict";
|
|
function _mergeNamespaces(n, m) {
|
|
m.forEach((function(e) {
|
|
e && typeof e !== "string" && !Array.isArray(e) && Object.keys(e).forEach((function(k) {
|
|
if (k !== "default" && !(k in n)) {
|
|
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
Object.defineProperty(n, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function() {
|
|
return e[k];
|
|
}
|
|
});
|
|
}
|
|
}));
|
|
}));
|
|
return Object.freeze(n);
|
|
}
|
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
function getDefaultExportFromCjs(x) {
|
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
}
|
|
function getAugmentedNamespace(n) {
|
|
if (n.__esModule) return n;
|
|
var f = n.default;
|
|
if (typeof f == "function") {
|
|
var a = function a() {
|
|
if (this instanceof a) {
|
|
return Reflect.construct(f, arguments, this.constructor);
|
|
}
|
|
return f.apply(this, arguments);
|
|
};
|
|
a.prototype = f.prototype;
|
|
} else a = {};
|
|
Object.defineProperty(a, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.keys(n).forEach((function(k) {
|
|
var d = Object.getOwnPropertyDescriptor(n, k);
|
|
Object.defineProperty(a, k, d.get ? d : {
|
|
enumerable: true,
|
|
get: function() {
|
|
return n[k];
|
|
}
|
|
});
|
|
}));
|
|
return a;
|
|
}
|
|
var chainpadListmap = {
|
|
exports: {}
|
|
};
|
|
var chainpadNetflux = {
|
|
exports: {}
|
|
};
|
|
var netfluxClient = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredNetfluxClient;
|
|
function requireNetfluxClient() {
|
|
if (hasRequiredNetfluxClient) return netfluxClient.exports;
|
|
hasRequiredNetfluxClient = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function() {
|
|
var MAX_LAG_BEFORE_PING = 15e3;
|
|
var MAX_LAG_BEFORE_DISCONNECT = 6e4;
|
|
var PING_CYCLE = 5e3;
|
|
var REQUEST_TIMEOUT = 3e4;
|
|
var FORCE_CLOSE_TIMEOUT = 1e4;
|
|
var RECONNECT_LOOP_CYCLE = 7e3;
|
|
var NOFUNC = function() {};
|
|
var now = function now() {
|
|
return (new Date).getTime();
|
|
};
|
|
var getLag = function(ctx) {
|
|
if (!ctx.ws) {
|
|
return null;
|
|
}
|
|
if (!ctx.pingOutstanding) {
|
|
return ctx.lastObservedLag;
|
|
}
|
|
return Math.max(now() - ctx.timeOfLastPingSent, ctx.lastObservedLag);
|
|
};
|
|
var setTimeoutX = function(ctx, func, time) {
|
|
ctx.timeouts.push(setTimeout(func, time));
|
|
};
|
|
var closeWebsocket = function(ctx, offline) {
|
|
if (!ctx.ws) {
|
|
return;
|
|
}
|
|
ctx.ws.onmessage = NOFUNC;
|
|
ctx.ws.onopen = NOFUNC;
|
|
ctx.ws.close();
|
|
if (offline) {
|
|
ctx.ws.onclose({
|
|
reason: "offline"
|
|
});
|
|
return;
|
|
}
|
|
setTimeoutX(ctx, (function() {
|
|
if (!ctx.ws) {
|
|
return;
|
|
}
|
|
ctx.ws.onclose({
|
|
reason: "forced closed because websocket failed to close"
|
|
});
|
|
}), FORCE_CLOSE_TIMEOUT);
|
|
};
|
|
var send = function(ctx, content) {
|
|
if (!ctx.ws) {
|
|
return false;
|
|
}
|
|
ctx.ws.send(JSON.stringify(content));
|
|
return true;
|
|
};
|
|
var networkSendTo = function networkSendTo(ctx, peerId, content) {
|
|
var seq = ctx.seq++;
|
|
var message = [ seq, "MSG", peerId, content ];
|
|
var sent = send(ctx, message);
|
|
return new Promise((function(res, rej) {
|
|
if (!sent) {
|
|
return void rej({
|
|
type: "DISCONNECTED",
|
|
message: JSON.stringify(message)
|
|
});
|
|
}
|
|
ctx.requests[seq] = {
|
|
reject: rej,
|
|
resolve: res,
|
|
time: now()
|
|
};
|
|
}));
|
|
};
|
|
var channelBcast = function channelBcast(ctx, chanId, content) {
|
|
var chan = ctx.channels[chanId];
|
|
var seq = ctx.seq++;
|
|
var message = [ seq, "MSG", chanId, content ];
|
|
if (!chan) {
|
|
return new Promise((function(res, rej) {
|
|
rej({
|
|
type: "NO_SUCH_CHANNEL",
|
|
message: JSON.stringify(message)
|
|
});
|
|
}));
|
|
}
|
|
var sent = send(ctx, message);
|
|
return new Promise((function(res, rej) {
|
|
if (!sent) {
|
|
return void rej({
|
|
type: "DISCONNECTED",
|
|
message: JSON.stringify(message)
|
|
});
|
|
}
|
|
ctx.requests[seq] = {
|
|
reject: rej,
|
|
resolve: res,
|
|
time: now()
|
|
};
|
|
}));
|
|
};
|
|
var channelLeave = function channelLeave(ctx, chanId, reason) {
|
|
var chan = ctx.channels[chanId];
|
|
if (!chan) {
|
|
return void console.debug("no such channel", chanId);
|
|
}
|
|
delete ctx.channels[chanId];
|
|
if (!ctx.ws || ctx.ws.readyState !== 1) {
|
|
return;
|
|
}
|
|
var seq = ctx.seq++;
|
|
send(ctx, [ seq, "LEAVE", chanId, reason ]);
|
|
var emptyFunction = function() {};
|
|
ctx.requests[seq] = {
|
|
reject: emptyFunction,
|
|
resolve: emptyFunction,
|
|
time: now()
|
|
};
|
|
};
|
|
var makeEventHandlers = function makeEventHandlers(ctx, mappings) {
|
|
return function(name, handler) {
|
|
var handlers = mappings[name];
|
|
if (!handlers) {
|
|
throw new Error("no such event " + name);
|
|
}
|
|
handlers.push(handler);
|
|
};
|
|
};
|
|
var removeEventHandler = function(ctx, name, handler, mappings) {
|
|
var handlers = mappings[name];
|
|
if (!handlers) {
|
|
throw new Error("no such event " + name);
|
|
}
|
|
var idx = handlers.indexOf(handler);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
handlers.splice(idx, 1);
|
|
};
|
|
var mkChannel = function mkChannel(ctx, id, priority) {
|
|
if (ctx.channels[id]) {
|
|
return new Promise((function(res) {
|
|
res(ctx.channels[id]);
|
|
}));
|
|
}
|
|
var q = ctx.queues.p2;
|
|
if (priority === 1) {
|
|
q = ctx.queues.p1;
|
|
} else if (priority === 3) {
|
|
q = ctx.queues.p3;
|
|
}
|
|
var internal = {
|
|
queue: q,
|
|
onMessage: [],
|
|
onJoin: [],
|
|
onLeave: [],
|
|
members: [],
|
|
jSeq: ctx.seq++
|
|
};
|
|
var mappings = {
|
|
message: internal.onMessage,
|
|
join: internal.onJoin,
|
|
leave: internal.onLeave
|
|
};
|
|
var chan = {
|
|
_: internal,
|
|
time: now(),
|
|
id,
|
|
members: internal.members,
|
|
bcast: function bcast(msg) {
|
|
return channelBcast(ctx, chan.id, msg);
|
|
},
|
|
leave: function leave(reason) {
|
|
return channelLeave(ctx, chan.id, reason);
|
|
},
|
|
on: makeEventHandlers(ctx, mappings),
|
|
off: function(name, handler) {
|
|
removeEventHandler(ctx, name, handler, mappings);
|
|
}
|
|
};
|
|
ctx.requests[internal.jSeq] = chan;
|
|
var message = [ internal.jSeq, "JOIN", id ];
|
|
var sent = send(ctx, message);
|
|
return new Promise((function(res, rej) {
|
|
if (!sent) {
|
|
return void rej({
|
|
type: "DISCONNECTED",
|
|
message: JSON.stringify(message)
|
|
});
|
|
}
|
|
chan._.resolve = res;
|
|
chan._.reject = rej;
|
|
}));
|
|
};
|
|
var disconnect = function(ctx) {
|
|
if (ctx.ws) {
|
|
var onclose = ctx.ws.onclose;
|
|
ctx.ws.onclose = NOFUNC;
|
|
ctx.ws.close();
|
|
onclose({
|
|
reason: "network.disconnect() called"
|
|
});
|
|
}
|
|
ctx.timeouts.forEach(clearTimeout);
|
|
ctx.timeouts = [];
|
|
};
|
|
var mkNetwork = function mkNetwork(ctx) {
|
|
var mappings = {
|
|
message: ctx.onMessage,
|
|
disconnect: ctx.onDisconnect,
|
|
reconnect: ctx.onReconnect
|
|
};
|
|
var network = {
|
|
webChannels: ctx.channels,
|
|
getLag: function _getLag() {
|
|
return getLag(ctx);
|
|
},
|
|
sendto: function sendto(peerId, content) {
|
|
return networkSendTo(ctx, peerId, content);
|
|
},
|
|
join: function join(chanId, priority) {
|
|
return mkChannel(ctx, chanId, priority);
|
|
},
|
|
disconnect: function() {
|
|
return disconnect(ctx);
|
|
},
|
|
on: makeEventHandlers(ctx, mappings),
|
|
off: function(name, handler) {
|
|
removeEventHandler(ctx, name, handler, mappings);
|
|
}
|
|
};
|
|
network.__defineGetter__("webChannels", (function() {
|
|
return Object.keys(ctx.channels).map((function(k) {
|
|
return ctx.channels[k];
|
|
}));
|
|
}));
|
|
return network;
|
|
};
|
|
var process = function(ctx) {
|
|
if (ctx.queues.busy) {
|
|
return;
|
|
}
|
|
var next = function() {
|
|
var obj = ctx.queues.p1.shift() || ctx.queues.p2.shift() || ctx.queues.p3.shift();
|
|
if (!obj) {
|
|
ctx.queues.busy = false;
|
|
return;
|
|
}
|
|
ctx.queues.busy = true;
|
|
var handlers = obj.h;
|
|
var msg = obj.msg;
|
|
handlers.forEach((function(h) {
|
|
setTimeout((function() {
|
|
try {
|
|
h(msg[4], msg[1]);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
}));
|
|
setTimeout((function() {
|
|
next();
|
|
}));
|
|
};
|
|
next();
|
|
};
|
|
var onMessage = function onMessage(ctx, evt) {
|
|
var msg = void 0;
|
|
try {
|
|
msg = JSON.parse(evt.data);
|
|
} catch (e) {
|
|
console.log(e.stack);
|
|
return;
|
|
}
|
|
ctx.timeOfLastMsgReceived = now();
|
|
if (msg[0] !== 0) {
|
|
var req = ctx.requests[msg[0]];
|
|
if (!req) {
|
|
console.log("error: " + JSON.stringify(msg));
|
|
return;
|
|
}
|
|
delete ctx.requests[msg[0]];
|
|
if (msg[1] === "ACK") {
|
|
if (req.ping) {
|
|
ctx.lastObservedLag = now() - Number(req.ping);
|
|
ctx.timeOfLastPingReceived = now();
|
|
ctx.pingOutstanding--;
|
|
return;
|
|
}
|
|
req.resolve();
|
|
} else if (msg[1] === "JACK") {
|
|
if (req._) {
|
|
if (!msg[2]) {
|
|
throw new Error("wrong type of ACK for channel join");
|
|
}
|
|
req.id = msg[2];
|
|
ctx.channels[req.id] = req;
|
|
return;
|
|
}
|
|
req.resolve();
|
|
} else if (msg[1] === "ERROR") {
|
|
if (typeof req.reject === "function") {
|
|
req.reject({
|
|
type: msg[2],
|
|
message: msg[3]
|
|
});
|
|
} else if (req._ && typeof req._.reject === "function") {
|
|
if (msg[2] === "EJOINED" && !ctx.channels[msg[3]]) {
|
|
req.id = msg[3];
|
|
ctx.channels[req.id] = req;
|
|
return;
|
|
}
|
|
req._.reject({
|
|
type: msg[2],
|
|
message: msg[3]
|
|
});
|
|
} else {
|
|
console.error(msg);
|
|
}
|
|
} else {
|
|
req.reject({
|
|
type: "UNKNOWN",
|
|
message: JSON.stringify(msg)
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (msg[2] === "IDENT") {
|
|
ctx.uid = msg[3];
|
|
ctx.ws._onident();
|
|
ctx.pingInterval = setInterval((function() {
|
|
if (now() - ctx.timeOfLastPingReceived < MAX_LAG_BEFORE_PING) {
|
|
return;
|
|
}
|
|
if (now() - ctx.timeOfLastMsgReceived > MAX_LAG_BEFORE_DISCONNECT) {
|
|
closeWebsocket(ctx);
|
|
}
|
|
if (ctx.pingOutstanding) {
|
|
return;
|
|
}
|
|
var seq = ctx.seq++;
|
|
var currentDate = now();
|
|
ctx.timeOfLastPingSent = currentDate;
|
|
ctx.pingOutstanding++;
|
|
ctx.requests[seq] = {
|
|
time: currentDate,
|
|
ping: currentDate
|
|
};
|
|
send(ctx, [ seq, "PING" ]);
|
|
}), PING_CYCLE);
|
|
return;
|
|
} else if (!ctx.uid) {
|
|
return;
|
|
}
|
|
if (msg[2] === "PING") {
|
|
msg[2] = "PONG";
|
|
send(ctx, msg);
|
|
return;
|
|
}
|
|
if (msg[2] === "MSG") {
|
|
var handlers = void 0;
|
|
var q = ctx.queues.p2;
|
|
if (msg[3] === ctx.uid) {
|
|
handlers = ctx.onMessage;
|
|
if (typeof msg[5] === "number") {
|
|
if (msg[5] === 1) {
|
|
q = ctx.queues.p1;
|
|
}
|
|
if (msg[5] === 3) {
|
|
q = ctx.queues.p3;
|
|
}
|
|
}
|
|
} else {
|
|
var chan = ctx.channels[msg[3]];
|
|
if (!chan) {
|
|
console.log("message to non-existent chan " + JSON.stringify(msg));
|
|
return;
|
|
}
|
|
handlers = chan._.onMessage;
|
|
if (chan._.queue) {
|
|
q = chan._.queue;
|
|
}
|
|
}
|
|
q.push({
|
|
msg,
|
|
h: handlers
|
|
});
|
|
process(ctx);
|
|
}
|
|
if (msg[2] === "LEAVE") {
|
|
var _chan = ctx.channels[msg[3]];
|
|
if (!_chan) {
|
|
if (msg[1] !== ctx.uid) {
|
|
console.log("leaving non-existent chan " + JSON.stringify(msg));
|
|
}
|
|
return;
|
|
}
|
|
var leaveIdx = _chan._.members.indexOf(msg[1]);
|
|
if (leaveIdx !== -1) {
|
|
_chan._.members.splice(leaveIdx, 1);
|
|
}
|
|
_chan._.onLeave.forEach((function(h) {
|
|
try {
|
|
h(msg[1], msg[4]);
|
|
} catch (e) {
|
|
console.log(e.stack);
|
|
}
|
|
}));
|
|
}
|
|
if (msg[2] === "JOIN") {
|
|
var _chan2 = ctx.channels[msg[3]];
|
|
if (!_chan2) {
|
|
console.log("ERROR: join to non-existent chan " + JSON.stringify(msg));
|
|
return;
|
|
}
|
|
if (_chan2._.members.indexOf(msg[1]) !== -1) {
|
|
return;
|
|
}
|
|
var synced = _chan2._.members.indexOf(ctx.uid) !== -1;
|
|
_chan2._.members.push(msg[1]);
|
|
if (!synced && msg[1] === ctx.uid) {
|
|
_chan2.myID = ctx.uid;
|
|
_chan2._.resolve(_chan2);
|
|
}
|
|
if (synced) {
|
|
_chan2._.onJoin.forEach((function(h) {
|
|
try {
|
|
h(msg[1]);
|
|
} catch (e) {
|
|
console.log(e.stack);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
};
|
|
var connect = function connect(websocketURL, makeWebsocket) {
|
|
makeWebsocket = makeWebsocket || function(url) {
|
|
return new window.WebSocket(url);
|
|
};
|
|
var ctx = {
|
|
ws: null,
|
|
seq: 1,
|
|
uid: null,
|
|
network: null,
|
|
channels: {},
|
|
onMessage: [],
|
|
onDisconnect: [],
|
|
onReconnect: [],
|
|
timeouts: [],
|
|
requests: {},
|
|
pingInterval: null,
|
|
queues: {
|
|
p1: [],
|
|
p2: [],
|
|
p3: []
|
|
},
|
|
timeOfLastPingSent: -1,
|
|
timeOfLastPingReceived: -1,
|
|
timeOfLastMsgReceived: -1,
|
|
lastObservedLag: 0,
|
|
pingOutstanding: 0
|
|
};
|
|
ctx.network = mkNetwork(ctx);
|
|
var promiseResolve = NOFUNC;
|
|
var promiseReject = NOFUNC;
|
|
if (typeof window !== "undefined") {
|
|
window.addEventListener("offline", (function() {
|
|
if ([ "localhost", "127.0.0.1", "" ].indexOf(window.location.hostname) !== -1) {
|
|
return;
|
|
}
|
|
closeWebsocket(ctx, true);
|
|
}));
|
|
}
|
|
var connectWs = function() {
|
|
var ws = ctx.ws = makeWebsocket(websocketURL);
|
|
ctx.timeOfLastPingSent = ctx.timeOfLastPingReceived = now();
|
|
ctx.timeOfLastMsgReceived = now();
|
|
ws.onmessage = function(msg) {
|
|
return onMessage(ctx, msg);
|
|
};
|
|
ws.onclose = function(evt) {
|
|
ws.onclose = NOFUNC;
|
|
clearInterval(ctx.pingInterval);
|
|
ctx.timeouts.forEach(clearTimeout);
|
|
ctx.ws = null;
|
|
if (ctx.uid) {
|
|
ctx.uid = null;
|
|
ctx.onDisconnect.forEach((function(h) {
|
|
try {
|
|
h(evt.reason);
|
|
} catch (e) {
|
|
console.log(e.stack);
|
|
}
|
|
}));
|
|
}
|
|
setTimeoutX(ctx, connectWs, ctx.uid ? 0 : RECONNECT_LOOP_CYCLE);
|
|
};
|
|
ws.onopen = function() {
|
|
setTimeoutX(ctx, (function() {
|
|
if (ctx.uid) {
|
|
return;
|
|
}
|
|
promiseReject({
|
|
type: "TIMEOUT",
|
|
message: "waited " + REQUEST_TIMEOUT + "ms"
|
|
});
|
|
promiseResolve = promiseReject = NOFUNC;
|
|
closeWebsocket(ctx);
|
|
}), REQUEST_TIMEOUT);
|
|
};
|
|
ctx.ws._onident = function() {
|
|
ctx.timeOfLastPingReceived = now();
|
|
ctx.timeOfLastMsgReceived = now();
|
|
ctx.lastObservedLag = now() - ctx.timeOfLastPingSent;
|
|
if (promiseResolve !== NOFUNC) {
|
|
promiseResolve(ctx.network);
|
|
promiseResolve = promiseReject = NOFUNC;
|
|
} else {
|
|
ctx.channels = {};
|
|
ctx.requests = {};
|
|
ctx.pingOutstanding = 0;
|
|
ctx.onReconnect.forEach((function(h) {
|
|
try {
|
|
h(ctx.uid);
|
|
} catch (e) {
|
|
console.log(e.stack);
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
};
|
|
return new Promise((function(resolve, reject) {
|
|
promiseResolve = resolve;
|
|
promiseReject = reject;
|
|
connectWs();
|
|
}));
|
|
};
|
|
return {
|
|
connect
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
} else {
|
|
window.netflux_websocket = factory();
|
|
}
|
|
})();
|
|
})(netfluxClient);
|
|
return netfluxClient.exports;
|
|
}
|
|
var hasRequiredChainpadNetflux;
|
|
function requireChainpadNetflux() {
|
|
if (hasRequiredChainpadNetflux) return chainpadNetflux.exports;
|
|
hasRequiredChainpadNetflux = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Netflux) {
|
|
var USE_HISTORY = true;
|
|
var CPNF = {};
|
|
var verbose = function(x) {
|
|
console.log(x);
|
|
};
|
|
verbose = function() {};
|
|
var unBencode = function(str) {
|
|
return str.replace(/^\d+:/, "");
|
|
};
|
|
var removeCp = CPNF.removeCp = function(str) {
|
|
return str.replace(/^cp\|([A-Za-z0-9+\/=]{0,20}\|)?/, "");
|
|
};
|
|
CPNF.start = function(config) {
|
|
config = config || {};
|
|
var network = config.network;
|
|
var websocketUrl = config.websocketURL;
|
|
var userName = config.userName;
|
|
var channel = config.channel;
|
|
var Crypto = config.crypto;
|
|
var readOnly = config.readOnly || false;
|
|
var ChainPad = !config.noChainPad && (config.ChainPad || window.ChainPad);
|
|
var useHistory = typeof config.useHistory === "undefined" ? USE_HISTORY : !!config.useHistory;
|
|
var stopped = false;
|
|
var lastKnownHash = config.lastKnownHash;
|
|
var lastSent = {};
|
|
var messagesQueue = [];
|
|
var priority = config.priority || 2;
|
|
var Cache = config.Cache;
|
|
var channelCache;
|
|
var initialCache = false;
|
|
var txid = Math.floor(Math.random() * 1e6);
|
|
var metadata = config.metadata || {};
|
|
var validateKey = metadata.validateKey = config.validateKey || metadata.validateKey;
|
|
metadata.owners = config.owners || metadata.owners;
|
|
metadata.expire = config.expire || metadata.expire;
|
|
var initializing = true;
|
|
var toReturn = {
|
|
setReadOnly: function(state, crypto) {
|
|
readOnly = state;
|
|
if (crypto) {
|
|
Crypto = crypto;
|
|
}
|
|
}
|
|
};
|
|
var joinSession = function() {};
|
|
var realtime;
|
|
var lastKnownHistoryKeeper;
|
|
var historyKeeperChange = [];
|
|
var wcObject = {
|
|
send: function() {}
|
|
};
|
|
var createRealtime = function() {
|
|
if (realtime && typeof realtime.abort === "function") {
|
|
realtime.abort();
|
|
}
|
|
var chainpad = ChainPad.create({
|
|
userName,
|
|
initialState: config.initialState,
|
|
transformFunction: config.transformFunction,
|
|
patchTransformer: config.patchTransformer,
|
|
validateContent: config.validateContent,
|
|
avgSyncMilliseconds: config.avgSyncMilliseconds,
|
|
logLevel: typeof config.logLevel !== "undefined" ? config.logLevel : 1
|
|
});
|
|
if (Cache && Array.isArray(channelCache) && channelCache.length) {
|
|
channelCache.forEach((function(obj) {
|
|
var patch = obj.patch;
|
|
if (!/^\[/.test(patch)) {
|
|
try {
|
|
patch = Crypto.decrypt(patch, validateKey, true);
|
|
} catch (err) {
|
|
console.error(patch, validateKey, channel);
|
|
console.error(err);
|
|
}
|
|
patch = unBencode(patch);
|
|
}
|
|
chainpad.message(patch);
|
|
}));
|
|
var doc = chainpad.getUserDoc();
|
|
if (doc === "") {
|
|
chainpad.abort();
|
|
channelCache = [];
|
|
Cache.clearChannel(channel);
|
|
return createRealtime();
|
|
}
|
|
}
|
|
chainpad.onMessage((function(msg, cb, curve) {
|
|
wcObject.send(msg, cb, curve);
|
|
}));
|
|
chainpad.onPatch((function(patch) {
|
|
if (config.onRemote) {
|
|
config.onRemote({
|
|
realtime: chainpad,
|
|
patch
|
|
});
|
|
}
|
|
}));
|
|
return chainpad;
|
|
};
|
|
var userList = {
|
|
change: [],
|
|
onChange: function(newData) {
|
|
userList.change.forEach((function(el) {
|
|
el(newData);
|
|
}));
|
|
},
|
|
users: []
|
|
};
|
|
var onJoining = function(peer) {
|
|
if (config.onJoin) {
|
|
config.onJoin(peer);
|
|
}
|
|
if (peer.length !== 32) {
|
|
return;
|
|
}
|
|
var list = userList.users;
|
|
var index = list.indexOf(peer);
|
|
if (index === -1) {
|
|
userList.users.push(peer);
|
|
}
|
|
userList.onChange();
|
|
};
|
|
var connectTo = function() {};
|
|
var onLeaving = function(peer) {
|
|
if (config.onLeave) {
|
|
config.onLeave(peer);
|
|
}
|
|
var list = userList.users;
|
|
var index = list.indexOf(peer);
|
|
if (index !== -1) {
|
|
userList.users.splice(index, 1);
|
|
}
|
|
userList.onChange();
|
|
};
|
|
var onReady = function(wc, network) {
|
|
if (!initializing) {
|
|
return;
|
|
}
|
|
try {
|
|
var doc = realtime && realtime.getUserDoc();
|
|
if (initialCache && realtime && (doc === "" || doc === config.initialState)) {
|
|
return void toReturn.resetCache();
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
if (realtime) {
|
|
realtime.start();
|
|
}
|
|
if (config.setMyID) {
|
|
config.setMyID({
|
|
myID: wc.myID
|
|
});
|
|
}
|
|
initializing = false;
|
|
if (config.onReady) {
|
|
config.onReady({
|
|
realtime,
|
|
network,
|
|
userList,
|
|
myId: wc.myID,
|
|
leave: wc.leave,
|
|
metadata
|
|
});
|
|
}
|
|
if (messagesQueue.length) {
|
|
messagesQueue.forEach((function(f) {
|
|
if (typeof f !== "function") {
|
|
return;
|
|
}
|
|
f();
|
|
}));
|
|
messagesQueue = [];
|
|
}
|
|
};
|
|
var onChannelError = function(parsed, wc) {
|
|
if (config.onChannelError) {
|
|
config.onChannelError({
|
|
channel: wc.id,
|
|
type: parsed.error,
|
|
loaded: !initializing,
|
|
error: parsed.error,
|
|
message: parsed.message
|
|
});
|
|
}
|
|
if (parsed.error === "EUNKNOWN") {
|
|
lastKnownHash = undefined;
|
|
if (wc) {
|
|
wc.leave();
|
|
}
|
|
if (Cache) {
|
|
channelCache = [];
|
|
Cache.clearChannel(channel, (function() {
|
|
if (ChainPad) {
|
|
toReturn.realtime = realtime = createRealtime();
|
|
}
|
|
joinSession(network, connectTo);
|
|
}));
|
|
return;
|
|
}
|
|
joinSession(network, connectTo);
|
|
return;
|
|
}
|
|
if (typeof toReturn.stop === "function") {
|
|
try {
|
|
toReturn.stop();
|
|
} catch (e) {}
|
|
}
|
|
};
|
|
var firstFillCache = true;
|
|
var fillCache = function(obj) {
|
|
if (!Cache) {
|
|
return;
|
|
}
|
|
if (channelCache.length && channelCache[channelCache.length - 1].hash === obj.hash) {
|
|
return;
|
|
}
|
|
if (!channelCache.length && firstFillCache) {
|
|
obj.isCheckpoint = true;
|
|
firstFillCache = false;
|
|
}
|
|
if (!channelCache.length && !obj.isCheckpoint) {
|
|
return;
|
|
}
|
|
channelCache.push(obj);
|
|
Cache.storeCache(channel, validateKey, channelCache, (function(err) {
|
|
if (err) {
|
|
Cache.clearChannel(channel, (function() {
|
|
console.warn("Cache cleared", channel);
|
|
}));
|
|
var _cache = channelCache;
|
|
channelCache = [];
|
|
return void console.error(err, channel, _cache, validateKey);
|
|
}
|
|
}));
|
|
};
|
|
var onMessage = function(peer, msg, wc, network, direct) {
|
|
var hk = network.historyKeeper;
|
|
var isHk = peer === hk;
|
|
if (wc && (msg === 0 || msg === "0")) {
|
|
return void onReady(wc, network);
|
|
}
|
|
if (direct && peer !== hk) {
|
|
return;
|
|
}
|
|
if (direct) {
|
|
var parsed = JSON.parse(msg);
|
|
if (parsed.txid && parsed.txid !== txid) {
|
|
return;
|
|
}
|
|
if (parsed.validateKey && parsed.channel) {
|
|
if (parsed.channel === wc.id && !validateKey) {
|
|
validateKey = parsed.validateKey;
|
|
}
|
|
if (parsed.channel === wc.id) {
|
|
metadata = parsed;
|
|
if (config.onMetadataUpdate && !initializing) {
|
|
config.onMetadataUpdate(metadata);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
|
if (parsed.channel === wc.id) {
|
|
Object.keys(lastSent).forEach((function(h) {
|
|
if (typeof lastSent[h] === "function") {
|
|
lastSent[h]("FAILED");
|
|
}
|
|
}));
|
|
lastSent = {};
|
|
onReady(wc, network);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
if (isHk) {
|
|
var parsed1 = JSON.parse(msg);
|
|
if (Array.isArray(parsed1) && parsed1[0] && parsed1[0] !== txid) {
|
|
return;
|
|
}
|
|
if (parsed1.channel === wc.id && parsed1.error) {
|
|
onChannelError(parsed1, wc);
|
|
return;
|
|
}
|
|
msg = parsed1[4];
|
|
if (parsed1[3] !== wc.id) {
|
|
return;
|
|
}
|
|
}
|
|
lastKnownHash = msg.slice(0, 64);
|
|
if (typeof lastSent[lastKnownHash] === "function") {
|
|
lastSent[lastKnownHash](null, lastKnownHash);
|
|
delete lastSent[lastKnownHash];
|
|
return;
|
|
}
|
|
var isCp = /^cp\|/.test(msg);
|
|
msg = removeCp(msg);
|
|
try {
|
|
msg = Crypto.decrypt(msg, validateKey, isHk);
|
|
} catch (err) {
|
|
console.error(msg, validateKey, channel);
|
|
console.error(err);
|
|
}
|
|
var senderCurve;
|
|
var isString = typeof msg === "string";
|
|
if (!isString && msg.content) {
|
|
senderCurve = msg.author;
|
|
msg = msg.content;
|
|
}
|
|
verbose(msg);
|
|
if (!initializing) {
|
|
if (config.onLocal) {
|
|
config.onLocal(true);
|
|
}
|
|
}
|
|
var message = isString ? unBencode(msg) : msg;
|
|
var apply = function() {
|
|
try {
|
|
if (realtime && isString) {
|
|
realtime.message(message);
|
|
}
|
|
if (config.onMessage) {
|
|
var obj = {
|
|
time: parsed1 ? parsed1[5] : +new Date
|
|
};
|
|
config.onMessage(message, peer, validateKey, isCp, lastKnownHash, senderCurve, obj);
|
|
}
|
|
var isCacheCp = isCp;
|
|
if (config.isCacheCheckpoint) {
|
|
isCacheCp = config.isCacheCheckpoint(message, senderCurve);
|
|
}
|
|
fillCache({
|
|
patch: message,
|
|
hash: lastKnownHash,
|
|
isCheckpoint: isCacheCp,
|
|
author: senderCurve,
|
|
time: parsed1 ? parsed1[5] : +new Date
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
if (initializing && peer !== hk) {
|
|
messagesQueue.push(apply);
|
|
return;
|
|
}
|
|
apply();
|
|
};
|
|
var msgOut = function(msg, curvePublic) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
try {
|
|
var cmsg = Crypto.encrypt(msg, curvePublic);
|
|
if (msg.indexOf("[4") === 0) {
|
|
var id = "";
|
|
if (window.nacl) {
|
|
var hash = window.nacl.hash(window.nacl.util.decodeUTF8(msg));
|
|
id = window.nacl.util.encodeBase64(hash.slice(0, 8)) + "|";
|
|
} else {
|
|
console.log("Checkpoint sent without an ID. Nacl is missing.");
|
|
}
|
|
cmsg = "cp|" + id + cmsg;
|
|
}
|
|
return cmsg;
|
|
} catch (err) {
|
|
console.log(msg);
|
|
throw err;
|
|
}
|
|
};
|
|
var onOpen = function(wc, network, firstConnection) {
|
|
wcObject.wc = wc;
|
|
channel = wc.id;
|
|
wc.members.forEach(onJoining);
|
|
var onMessageHandler = function(msg, sender) {
|
|
onMessage(sender, msg, wc, network);
|
|
};
|
|
wc.on("message", onMessageHandler);
|
|
wc.on("join", onJoining);
|
|
wc.on("leave", onLeaving);
|
|
wcObject.stop = function() {
|
|
wc.off("message", onMessageHandler);
|
|
wc.off("join", onJoining);
|
|
wc.off("leave", onLeaving);
|
|
wc.leave();
|
|
if (realtime) {
|
|
realtime.abort();
|
|
}
|
|
};
|
|
if (firstConnection) {
|
|
wcObject.send = function(_message, cb, curvePublic) {
|
|
var message = msgOut(_message, curvePublic);
|
|
if (message) {
|
|
var hash = message.slice(0, 64);
|
|
var isCacheCp = /^cp\|/.test(message);
|
|
if (config.isCacheCheckpoint) {
|
|
isCacheCp = config.isCacheCheckpoint(_message, curvePublic);
|
|
}
|
|
lastSent[hash] = function(err, _hash) {
|
|
if (!err && _hash) {
|
|
fillCache({
|
|
patch: removeCp(_message),
|
|
hash,
|
|
isCheckpoint: isCacheCp,
|
|
author: curvePublic,
|
|
time: +new Date
|
|
});
|
|
}
|
|
cb(err, _hash);
|
|
};
|
|
wcObject.wc.bcast(message).then((function() {
|
|
lastKnownHash = hash;
|
|
delete lastSent[hash];
|
|
fillCache({
|
|
patch: removeCp(_message),
|
|
hash,
|
|
isCheckpoint: isCacheCp,
|
|
author: curvePublic,
|
|
time: +new Date
|
|
});
|
|
cb(null, hash);
|
|
}), (function(err) {
|
|
console.error(err);
|
|
if (err && (err.type === "enoent" || err.type === "ENOENT")) {
|
|
wcObject.wc.leave();
|
|
if (stopped) {
|
|
delete lastSent[hash];
|
|
return void cb("STOPPED");
|
|
}
|
|
initializing = true;
|
|
network.join(channel, priority).then((function(wc) {
|
|
onOpen(wc, network, false);
|
|
wcObject.send(_message, cb, curvePublic);
|
|
}));
|
|
} else {
|
|
delete lastSent[hash];
|
|
cb(err && err.type || err);
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
if (ChainPad && !realtime) {
|
|
toReturn.realtime = realtime = createRealtime();
|
|
}
|
|
if (config.onInit) {
|
|
config.onInit({
|
|
myID: wc.myID,
|
|
realtime,
|
|
getLag: network.getLag,
|
|
userList,
|
|
network,
|
|
channel
|
|
});
|
|
}
|
|
}
|
|
if (config.onConnect) {
|
|
config.onConnect(wc, wcObject.send);
|
|
}
|
|
if (useHistory) {
|
|
var hk;
|
|
wc.members.forEach((function(p) {
|
|
if (p.length === 16) {
|
|
hk = p;
|
|
}
|
|
}));
|
|
if (lastKnownHistoryKeeper !== hk) {
|
|
network.historyKeeper = hk;
|
|
lastKnownHistoryKeeper = hk;
|
|
historyKeeperChange.forEach((function(f) {
|
|
f(hk);
|
|
}));
|
|
}
|
|
var sendGetHistory = function() {
|
|
var cfg = {
|
|
txid,
|
|
priority,
|
|
lastKnownHash,
|
|
metadata
|
|
};
|
|
if (Cache && Array.isArray(channelCache) && channelCache.length) {
|
|
cfg.lastKnownHash = channelCache[channelCache.length - 1].hash;
|
|
}
|
|
messagesQueue = [];
|
|
var msg = [ "GET_HISTORY", wc.id, cfg ];
|
|
if (hk) {
|
|
network.sendto(hk, JSON.stringify(msg));
|
|
}
|
|
};
|
|
sendGetHistory();
|
|
toReturn.resetCache = function() {
|
|
initializing = true;
|
|
channelCache = [];
|
|
if (Cache) {
|
|
Cache.clearChannel(channel);
|
|
}
|
|
txid = Math.floor(Math.random() * 1e6);
|
|
onChannelError({
|
|
error: "EUNKNOWN",
|
|
message: "Corrupted cache"
|
|
}, wc);
|
|
};
|
|
} else {
|
|
onReady(wc, network);
|
|
}
|
|
};
|
|
var isIntentionallyLeaving = false;
|
|
if (typeof window !== "undefined") {
|
|
window.addEventListener("beforeunload", (function() {
|
|
isIntentionallyLeaving = true;
|
|
}));
|
|
}
|
|
var findChannelById = function(webChannels, channelId) {
|
|
var webChannel;
|
|
webChannels.some((function(chan) {
|
|
if (chan.id === channelId) {
|
|
webChannel = chan;
|
|
return true;
|
|
}
|
|
}));
|
|
return webChannel;
|
|
};
|
|
var onConnectError = function(err) {
|
|
if (config.onError) {
|
|
config.onError({
|
|
type: err.type || err,
|
|
message: err.message,
|
|
error: err.type,
|
|
loaded: !initializing
|
|
});
|
|
}
|
|
};
|
|
joinSession = function(endPoint, cb) {
|
|
var promise;
|
|
if (typeof endPoint === "string") {
|
|
promise = Netflux.connect(endPoint);
|
|
}
|
|
var join = function() {
|
|
if (typeof endPoint === "string") {
|
|
promise.then(cb, onConnectError);
|
|
} else if (typeof endPoint.then === "function") {
|
|
endPoint.then(cb, onConnectError);
|
|
} else {
|
|
cb(endPoint);
|
|
}
|
|
};
|
|
if (Cache) {
|
|
Cache.getChannelCache(channel, (function(err, cache) {
|
|
validateKey = cache ? cache.k : undefined;
|
|
channelCache = cache ? cache.c : [];
|
|
if (!channelCache.length) {
|
|
return void join();
|
|
}
|
|
initialCache = true;
|
|
if (config.onCacheStart) {
|
|
config.onCacheStart();
|
|
}
|
|
if (ChainPad) {
|
|
toReturn.realtime = realtime = createRealtime();
|
|
}
|
|
if (!channelCache.length) {
|
|
return void join();
|
|
}
|
|
if (config.onMessage) {
|
|
channelCache.forEach((function(obj) {
|
|
var _obj = {
|
|
time: obj.time
|
|
};
|
|
config.onMessage(obj.patch, "cache", validateKey, obj.isCheckpoint, obj.hash, obj.author, _obj);
|
|
}));
|
|
}
|
|
if (config.onCacheReady) {
|
|
config.onCacheReady({
|
|
id: channel,
|
|
realtime,
|
|
networkPromise: promise
|
|
});
|
|
}
|
|
join();
|
|
}));
|
|
toReturn.resetCache = function() {
|
|
if (Cache) {
|
|
Cache.clearChannel(channel);
|
|
}
|
|
channelCache = [];
|
|
};
|
|
return;
|
|
}
|
|
join();
|
|
};
|
|
var firstConnection = true;
|
|
connectTo = function(network, first) {
|
|
if (stopped) {
|
|
return;
|
|
}
|
|
network.join(channel || null, priority).then((function(wc) {
|
|
if (stopped) {
|
|
try {
|
|
wc.leave();
|
|
} catch (e) {}
|
|
return;
|
|
}
|
|
onOpen(wc, network, first);
|
|
}), (function(error) {
|
|
if (error && error.type === "ERESTRICTED" && Array.isArray(error.message)) {
|
|
if (config.onRejected) {
|
|
return void config.onRejected(error.message, (function(err) {
|
|
if (err) {
|
|
return void onConnectError(error);
|
|
}
|
|
connectTo(network, first);
|
|
}));
|
|
}
|
|
}
|
|
onConnectError(error);
|
|
}));
|
|
};
|
|
toReturn.stop = function() {
|
|
stopped = true;
|
|
};
|
|
joinSession(network || websocketUrl, (function(_network) {
|
|
network = _network;
|
|
if (firstConnection) {
|
|
firstConnection = false;
|
|
if (stopped) {
|
|
return;
|
|
}
|
|
var onDisconnectHandler = function(reason) {
|
|
if (isIntentionallyLeaving) {
|
|
return;
|
|
}
|
|
if (reason === "network.disconnect() called") {
|
|
return;
|
|
}
|
|
if (config.onConnectionChange) {
|
|
config.onConnectionChange({
|
|
state: false
|
|
});
|
|
return;
|
|
}
|
|
if (config.onAbort) {
|
|
config.onAbort({
|
|
reason
|
|
});
|
|
}
|
|
};
|
|
var onReconnectHandler = function(uid) {
|
|
if (config.onConnectionChange) {
|
|
config.onConnectionChange({
|
|
state: true,
|
|
myId: uid
|
|
});
|
|
var afterReconnecting = function() {
|
|
initializing = true;
|
|
userList.users = [];
|
|
joinSession(network, connectTo);
|
|
};
|
|
if (config.beforeReconnecting) {
|
|
config.beforeReconnecting((function(newKey, newContent) {
|
|
channel = newKey;
|
|
config.initialState = newContent;
|
|
afterReconnecting();
|
|
}));
|
|
return;
|
|
}
|
|
afterReconnecting();
|
|
}
|
|
};
|
|
var onMessageHandler = function(msg, sender) {
|
|
var wchan = findChannelById(network.webChannels, channel);
|
|
if (wchan) {
|
|
onMessage(sender, msg, wchan, network, true);
|
|
}
|
|
};
|
|
network.on("disconnect", onDisconnectHandler);
|
|
network.on("reconnect", onReconnectHandler);
|
|
network.on("message", onMessageHandler);
|
|
toReturn.network = network;
|
|
toReturn.stop = function() {
|
|
if (wcObject && wcObject.stop) {
|
|
wcObject.stop();
|
|
}
|
|
var wchan = findChannelById(network.webChannels, channel);
|
|
if (wchan) {
|
|
try {
|
|
wchan.leave("");
|
|
} catch (e) {}
|
|
}
|
|
network.off("disconnect", onDisconnectHandler);
|
|
network.off("reconnect", onReconnectHandler);
|
|
network.off("message", onMessageHandler);
|
|
stopped = true;
|
|
};
|
|
network.onHistoryKeeperChange = function(todo) {
|
|
historyKeeperChange.push(todo);
|
|
};
|
|
}
|
|
connectTo(network, true);
|
|
}));
|
|
return toReturn;
|
|
};
|
|
return CPNF;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireNetfluxClient());
|
|
}
|
|
})();
|
|
})(chainpadNetflux);
|
|
return chainpadNetflux.exports;
|
|
}
|
|
/*!
|
|
* Copyright 2015-2016 Thomas Rosenau
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/ var lib;
|
|
var hasRequiredLib;
|
|
function requireLib() {
|
|
if (hasRequiredLib) return lib;
|
|
hasRequiredLib = 1;
|
|
const sortKeys = o => {
|
|
if (Array.isArray(o)) {
|
|
return o.map(sortKeys);
|
|
} else if (o instanceof Object) {
|
|
let numeric = [];
|
|
let nonNumeric = [];
|
|
Object.keys(o).forEach((key => {
|
|
if (/^(0|[1-9][0-9]*)$/.test(key)) {
|
|
numeric.push(+key);
|
|
} else {
|
|
nonNumeric.push(key);
|
|
}
|
|
}));
|
|
return numeric.sort((function(a, b) {
|
|
return a - b;
|
|
})).concat(nonNumeric.sort()).reduce(((result, key) => {
|
|
result[key] = sortKeys(o[key]);
|
|
return result;
|
|
}), {});
|
|
}
|
|
return o;
|
|
};
|
|
const jsonStringify = JSON.stringify.bind(JSON);
|
|
const sortify = (value, replacer, space) => {
|
|
let native = jsonStringify(value, replacer, 0);
|
|
if (!native || native[0] !== "{" && native[0] !== "[") {
|
|
return native;
|
|
}
|
|
let cleanObj = JSON.parse(native);
|
|
return jsonStringify(sortKeys(cleanObj), null, space);
|
|
};
|
|
lib = sortify;
|
|
return lib;
|
|
}
|
|
var json_sortify;
|
|
var hasRequiredJson_sortify;
|
|
function requireJson_sortify() {
|
|
if (hasRequiredJson_sortify) return json_sortify;
|
|
hasRequiredJson_sortify = 1;
|
|
json_sortify = requireLib();
|
|
return json_sortify;
|
|
}
|
|
var hasRequiredChainpadListmap;
|
|
function requireChainpadListmap() {
|
|
if (hasRequiredChainpadListmap) return chainpadListmap.exports;
|
|
hasRequiredChainpadListmap = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Realtime, Sortify) {
|
|
const window = globalThis;
|
|
var api = {};
|
|
var ChainPad;
|
|
var isFakeProxy = typeof window.Proxy === "undefined";
|
|
var DeepProxy = api.DeepProxy = function() {
|
|
var deepProxy = {};
|
|
var isArray = deepProxy.isArray = Array.isArray || function(obj) {
|
|
return Object.toString(obj) === "[object Array]";
|
|
};
|
|
var type = deepProxy.type = function(dat) {
|
|
return dat === null ? "null" : isArray(dat) ? "array" : typeof dat;
|
|
};
|
|
var isProxyable = deepProxy.isProxyable = function(obj, forceCheck) {
|
|
if (typeof forceCheck === "undefined" && isFakeProxy) {
|
|
return false;
|
|
}
|
|
return [ "object", "array" ].indexOf(type(obj)) !== -1;
|
|
};
|
|
var setter = deepProxy.set = function(cb) {
|
|
return function(obj, prop, value) {
|
|
if (prop === "on") {
|
|
throw new Error("'on' is a reserved attribute name for realtime lists and maps");
|
|
}
|
|
if (isProxyable(value)) {
|
|
obj[prop] = deepProxy.create(value, cb);
|
|
} else {
|
|
obj[prop] = value;
|
|
}
|
|
cb();
|
|
return obj[prop] || true;
|
|
};
|
|
};
|
|
var pathMatches = deepProxy.pathMatches = function(path, pattern) {
|
|
return !pattern.some((function(x, i) {
|
|
return x !== path[i];
|
|
}));
|
|
};
|
|
var lengthDescending = function(a, b) {
|
|
return b.pattern.length - a.pattern.length;
|
|
};
|
|
var on = function(events) {
|
|
return function(evt, pattern, f) {
|
|
switch (evt) {
|
|
case "change":
|
|
pattern = type(pattern) === "array" ? pattern : [ pattern ];
|
|
events.change.push({
|
|
cb: function(oldval, newval, path, root) {
|
|
if (pathMatches(path, pattern)) {
|
|
return f(oldval, newval, path, root);
|
|
}
|
|
},
|
|
pattern
|
|
});
|
|
events.change.sort(lengthDescending);
|
|
break;
|
|
|
|
case "remove":
|
|
pattern = type(pattern) === "array" ? pattern : [ pattern ];
|
|
events.remove.push({
|
|
cb: function(oldval, path, root) {
|
|
if (pathMatches(path, pattern)) {
|
|
return f(oldval, path, root);
|
|
}
|
|
},
|
|
pattern
|
|
});
|
|
events.remove.sort(lengthDescending);
|
|
break;
|
|
|
|
case "ready":
|
|
events.ready.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "cacheready":
|
|
events.cacheready.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "disconnect":
|
|
events.disconnect.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "reconnect":
|
|
events.reconnect.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "create":
|
|
events.create.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
|
|
case "error":
|
|
events.error.push({
|
|
cb: function(info) {
|
|
pattern(info);
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
return this;
|
|
};
|
|
};
|
|
var getter = deepProxy.get = function() {
|
|
var events = {
|
|
cacheready: [],
|
|
disconnect: [],
|
|
reconnect: [],
|
|
change: [],
|
|
ready: [],
|
|
remove: [],
|
|
create: [],
|
|
error: []
|
|
};
|
|
return function(obj, prop) {
|
|
if (prop === "on") {
|
|
return on(events);
|
|
} else if (prop === "_isProxy") {
|
|
return true;
|
|
} else if (prop === "_events") {
|
|
return events;
|
|
}
|
|
return obj[prop];
|
|
};
|
|
};
|
|
var deleter = deepProxy.delete = function(cb) {
|
|
return function(obj, prop) {
|
|
if (typeof obj[prop] === "undefined") {
|
|
return true;
|
|
}
|
|
delete obj[prop];
|
|
cb();
|
|
return true;
|
|
};
|
|
};
|
|
var handlers = deepProxy.handlers = function(cb, isRoot) {
|
|
if (!isRoot) {
|
|
return {
|
|
set: setter(cb),
|
|
get: function(obj, prop) {
|
|
if (prop === "_isProxy") {
|
|
return true;
|
|
}
|
|
return obj[prop];
|
|
},
|
|
deleteProperty: deleter(cb)
|
|
};
|
|
}
|
|
return {
|
|
set: setter(cb),
|
|
get: getter(cb),
|
|
deleteProperty: deleter(cb)
|
|
};
|
|
};
|
|
var remoteChangeFlag = deepProxy.remoteChangeFlag = false;
|
|
var stringifyFakeProxy = deepProxy.stringifyFakeProxy = function(proxy) {
|
|
var copy = JSON.parse(Sortify(proxy));
|
|
delete copy._events;
|
|
delete copy._isProxy;
|
|
return Sortify(copy);
|
|
};
|
|
deepProxy.checkLocalChange = function(obj, cb) {
|
|
if (!isFakeProxy) {
|
|
return;
|
|
}
|
|
if (deepProxy.interval) {
|
|
return;
|
|
}
|
|
var oldObj = stringifyFakeProxy(obj);
|
|
deepProxy.interval = window.setInterval((function() {
|
|
var newObj = stringifyFakeProxy(obj);
|
|
if (newObj !== oldObj) {
|
|
oldObj = newObj;
|
|
if (remoteChangeFlag) {
|
|
remoteChangeFlag = false;
|
|
} else {
|
|
cb();
|
|
}
|
|
}
|
|
}), 300);
|
|
};
|
|
var create = deepProxy.create = function(obj, opt, isRoot) {
|
|
var methods = type(opt) === "function" ? handlers(opt, isRoot) : opt;
|
|
switch (type(obj)) {
|
|
case "object":
|
|
var keys = Object.keys(obj);
|
|
keys.forEach((function(k) {
|
|
if (isProxyable(obj[k]) && !obj[k]._isProxy) {
|
|
obj[k] = create(obj[k], opt);
|
|
}
|
|
}));
|
|
break;
|
|
|
|
case "array":
|
|
obj.forEach((function(o, i) {
|
|
if (isProxyable(o) && !o._isProxy) {
|
|
obj[i] = create(obj[i], opt);
|
|
}
|
|
}));
|
|
break;
|
|
|
|
default:
|
|
throw new Error("attempted to make a proxy of an unproxyable object");
|
|
}
|
|
if (!isFakeProxy) {
|
|
if (obj._isProxy) {
|
|
return obj;
|
|
}
|
|
return new window.Proxy(obj, methods);
|
|
}
|
|
var proxy = JSON.parse(JSON.stringify(obj));
|
|
if (isRoot) {
|
|
var events = {
|
|
cacheready: [],
|
|
disconnect: [],
|
|
reconnect: [],
|
|
change: [],
|
|
ready: [],
|
|
remove: [],
|
|
create: [],
|
|
error: []
|
|
};
|
|
proxy.on = on(events);
|
|
proxy._events = events;
|
|
}
|
|
return proxy;
|
|
};
|
|
var onChange = function(path, key, root, oldval, newval) {
|
|
var P = path.slice(0);
|
|
P.push(key);
|
|
root._events.change.some((function(handler) {
|
|
return handler.cb(oldval, newval, P, root) === false;
|
|
}));
|
|
};
|
|
var find = deepProxy.find = function(map, path) {
|
|
var l = path.length;
|
|
for (var i = 0; i < l; i++) {
|
|
if (typeof map[path[i]] === "undefined") {
|
|
return;
|
|
}
|
|
map = map[path[i]];
|
|
}
|
|
return map;
|
|
};
|
|
var onRemove = function(path, key, root, old, top) {
|
|
var newpath = path.concat(key);
|
|
var X = find(root, newpath);
|
|
var t_X = type(X);
|
|
switch (t_X) {
|
|
case "array":
|
|
if (top) {
|
|
onChange(path, key, root, old, undefined);
|
|
} else {
|
|
root._events.remove.forEach((function(handler) {
|
|
return handler.cb(X, newpath, root);
|
|
}));
|
|
}
|
|
X.forEach((function(x, i) {
|
|
onRemove(newpath, i, root);
|
|
}));
|
|
break;
|
|
|
|
case "object":
|
|
if (top) {
|
|
onChange(path, key, root, old, undefined);
|
|
} else {
|
|
root._events.remove.forEach((function(handler) {
|
|
return handler.cb(X, newpath, root, old, false);
|
|
}));
|
|
}
|
|
Object.keys(X).forEach((function(key) {
|
|
onRemove(newpath, key, root, X[key], false);
|
|
}));
|
|
break;
|
|
|
|
default:
|
|
root._events.remove.forEach((function(handler) {
|
|
return handler.cb(X, newpath, root);
|
|
}));
|
|
break;
|
|
}
|
|
};
|
|
var objects = deepProxy.objects = function(A, B, f, path, root) {
|
|
var Akeys = Object.keys(A);
|
|
var Bkeys = Object.keys(B);
|
|
Bkeys.forEach((function(b) {
|
|
var t_b = type(B[b]);
|
|
var old = A[b];
|
|
if (Akeys.indexOf(b) === -1) {
|
|
switch (t_b) {
|
|
case "undefined":
|
|
throw new Error("undefined type has key. this shouldn't happen?");
|
|
|
|
case "array":
|
|
case "object":
|
|
A[b] = f(B[b]);
|
|
break;
|
|
|
|
default:
|
|
A[b] = B[b];
|
|
}
|
|
onChange(path, b, root, old, B[b]);
|
|
return;
|
|
}
|
|
var t_a = type(A[b]);
|
|
if (t_a !== t_b) {
|
|
console.log("type changed from [%s] to [%s]", t_a, t_b);
|
|
switch (t_b) {
|
|
case "undefined":
|
|
throw new Error("first pass should never reveal undefined keys");
|
|
|
|
case "array":
|
|
A[b] = f(B[b]);
|
|
break;
|
|
|
|
case "object":
|
|
A[b] = f(B[b]);
|
|
break;
|
|
|
|
default:
|
|
A[b] = B[b];
|
|
break;
|
|
}
|
|
onChange(path, b, root, old, B[b]);
|
|
return;
|
|
}
|
|
if ([ "array", "object" ].indexOf(t_a) === -1) {
|
|
if (A[b] !== B[b]) {
|
|
A[b] = B[b];
|
|
onChange(path, b, root, old, B[b]);
|
|
}
|
|
return;
|
|
}
|
|
var nextPath = path.slice(0).concat(b);
|
|
if (t_a === "object") {
|
|
objects.call(root, A[b], B[b], f, nextPath, root);
|
|
} else {
|
|
deepProxy.arrays.call(root, A[b], B[b], f, nextPath, root);
|
|
}
|
|
}));
|
|
Akeys.forEach((function(a) {
|
|
var old = A[a];
|
|
if (a === "on" || a === "_events") {
|
|
return;
|
|
}
|
|
if (Bkeys.indexOf(a) === -1 || type(B[a]) === "undefined") {
|
|
onRemove(path, a, root, old, true);
|
|
delete A[a];
|
|
}
|
|
}));
|
|
return;
|
|
};
|
|
var arrays = deepProxy.arrays = function(A, B, f, path, root) {
|
|
var l_A = A.length;
|
|
var l_B = B.length;
|
|
if (l_A !== l_B) {
|
|
B.forEach((function(b, i) {
|
|
var t_a = type(A[i]);
|
|
var t_b = type(b);
|
|
var old = A[i];
|
|
if (t_a !== t_b) {
|
|
switch (t_b) {
|
|
case "undefined":
|
|
throw new Error("this should never happen");
|
|
|
|
case "object":
|
|
A[i] = f(b);
|
|
break;
|
|
|
|
case "array":
|
|
A[i] = f(b);
|
|
break;
|
|
|
|
default:
|
|
A[i] = b;
|
|
break;
|
|
}
|
|
onChange(path, i, root, old, b);
|
|
} else {
|
|
var nextPath = path.slice(0).concat(i);
|
|
switch (t_b) {
|
|
case "object":
|
|
objects.call(root, A[i], b, f, nextPath, root);
|
|
break;
|
|
|
|
case "array":
|
|
if (arrays.call(root, A[i], b, f, nextPath, root)) {
|
|
onChange(path, i, root, old, b);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (b !== A[i]) {
|
|
A[i] = b;
|
|
onChange(path, i, root, old, b);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}));
|
|
if (l_A > l_B) {
|
|
var i = l_B;
|
|
var old;
|
|
for (;i <= l_B; i++) {
|
|
old = A[i];
|
|
onRemove(path, i, root, old, true);
|
|
}
|
|
}
|
|
A.length = l_B;
|
|
return;
|
|
}
|
|
A.forEach((function(a, i) {
|
|
var t_a = type(a);
|
|
var t_b = type(B[i]);
|
|
var old = a;
|
|
if (t_a !== t_b) {
|
|
switch (t_b) {
|
|
case "undefined":
|
|
onRemove(path, i, root, old, true);
|
|
break;
|
|
|
|
case "object":
|
|
case "array":
|
|
A[i] = f(B[i]);
|
|
break;
|
|
|
|
default:
|
|
A[i] = B[i];
|
|
break;
|
|
}
|
|
onChange(path, i, root, old, B[i]);
|
|
return;
|
|
}
|
|
var nextPath = path.slice(0).concat(i);
|
|
switch (t_b) {
|
|
case "undefined":
|
|
throw new Error("existing key had type `undefined`. this should never happen");
|
|
|
|
case "object":
|
|
if (objects.call(root, A[i], B[i], f, nextPath, root)) {
|
|
onChange(path, i, root, old, B[i]);
|
|
}
|
|
break;
|
|
|
|
case "array":
|
|
if (arrays.call(root, A[i], B[i], f, nextPath, root)) {
|
|
onChange(path, i, root, old, B[i]);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (A[i] !== B[i]) {
|
|
A[i] = B[i];
|
|
onChange(path, i, root, old, B[i]);
|
|
}
|
|
break;
|
|
}
|
|
}));
|
|
return;
|
|
};
|
|
deepProxy.update = function(A, B, cb) {
|
|
var t_A = type(A);
|
|
var t_B = type(B);
|
|
if (t_A !== t_B) {
|
|
throw new Error("Proxy updates can't result in type changes");
|
|
}
|
|
switch (t_B) {
|
|
case "array":
|
|
arrays.call(A, A, B, (function(obj) {
|
|
return create(obj, cb);
|
|
}), [], A);
|
|
break;
|
|
|
|
case "object":
|
|
objects.call(A, A, B, (function(obj) {
|
|
return create(obj, cb);
|
|
}), [], A);
|
|
break;
|
|
|
|
default:
|
|
throw new Error("unsupported realtime datatype:" + t_B);
|
|
}
|
|
};
|
|
return deepProxy;
|
|
}();
|
|
api.create = function(cfg) {
|
|
if (!DeepProxy.isProxyable(cfg.data, true)) {
|
|
throw new Error("unsupported datatype: " + DeepProxy.type(cfg.data));
|
|
}
|
|
ChainPad = cfg.ChainPad || window.ChainPad;
|
|
if (typeof ChainPad.SmartJSONTransformer !== "function") {
|
|
throw new Error("Please update ChainPad");
|
|
}
|
|
if (cfg.classic && !cfg.crypto) {
|
|
console.error("[chainpad-listmap] no crypto module provided. messages will not be encrypted");
|
|
cfg.crypto = {
|
|
encrypt: function(msg) {
|
|
return msg;
|
|
},
|
|
decrypt: function(msg) {
|
|
return msg;
|
|
}
|
|
};
|
|
}
|
|
var readOnly = cfg.readOnly;
|
|
var config = {
|
|
initialState: Sortify(cfg.data),
|
|
patchTransformer: ChainPad.SmartJSONTransformer,
|
|
validateContent: cfg.validateContent || function(content) {
|
|
try {
|
|
JSON.parse(content);
|
|
return true;
|
|
} catch (e) {
|
|
console.log(content);
|
|
console.error("Failed to parse, rejecting patch");
|
|
return false;
|
|
}
|
|
},
|
|
readOnly: cfg.readOnly,
|
|
userName: cfg.userName || "listmap",
|
|
Cache: cfg.Cache,
|
|
logLevel: typeof cfg.logLevel === "undefined" ? 0 : cfg.logLevel
|
|
};
|
|
if (cfg.classic) {
|
|
config.channel = cfg.channel;
|
|
config.crypto = cfg.crypto;
|
|
config.network = cfg.network;
|
|
config.websocketURL = cfg.websocketURL;
|
|
config.metadata = cfg.metadata || {
|
|
validateKey: cfg.validateKey,
|
|
owners: cfg.owners,
|
|
expire: cfg.expire
|
|
};
|
|
config.onRejected = cfg.onRejected;
|
|
}
|
|
var rt = {};
|
|
rt.metadata = {};
|
|
var realtime;
|
|
var proxy;
|
|
var initializing = true;
|
|
var ready = false;
|
|
var localTo;
|
|
var stringifier = isFakeProxy ? DeepProxy.stringifyFakeProxy : Sortify;
|
|
var onLocalFollowUp = function() {
|
|
var strung = stringifier(proxy);
|
|
try {
|
|
realtime.contentUpdate(strung);
|
|
} catch (e) {
|
|
proxy._events.error.forEach((function(handler) {
|
|
handler.cb({
|
|
type: "CHAINPAD",
|
|
error: e.message
|
|
});
|
|
}));
|
|
}
|
|
if (cfg.onLocal) {
|
|
cfg.onLocal();
|
|
}
|
|
};
|
|
var onLocal = config.onLocal = function(remote) {
|
|
if (initializing) {
|
|
return;
|
|
}
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
clearTimeout(localTo);
|
|
if (remote) {
|
|
return void onLocalFollowUp();
|
|
}
|
|
localTo = setTimeout(onLocalFollowUp);
|
|
};
|
|
var setterCb = function() {
|
|
if (!DeepProxy.remoteChangeFlag) {
|
|
onLocal();
|
|
}
|
|
};
|
|
proxy = DeepProxy.create(cfg.data, setterCb, true);
|
|
config.onInit = function(info) {
|
|
proxy._events.create.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
config.onCacheReady = function(info) {
|
|
if (!realtime || realtime !== info.realtime) {
|
|
realtime = rt.realtime = info.realtime;
|
|
}
|
|
var userDoc = realtime.getUserDoc();
|
|
var parsed = JSON.parse(userDoc);
|
|
DeepProxy.update(proxy, parsed, setterCb);
|
|
DeepProxy.checkLocalChange(proxy, onLocal);
|
|
if (ready) {
|
|
return;
|
|
}
|
|
proxy._events.cacheready.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
var idx = 0;
|
|
config.onReady = function(info) {
|
|
idx = -1;
|
|
if (ready) {
|
|
initializing = false;
|
|
config.onRemote();
|
|
proxy._events.reconnect.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
return;
|
|
}
|
|
if (!realtime || realtime !== info.realtime) {
|
|
realtime = rt.realtime = info.realtime;
|
|
}
|
|
rt.metadata = info.metadata;
|
|
var userDoc = realtime.getUserDoc();
|
|
var parsed = JSON.parse(userDoc);
|
|
DeepProxy.update(proxy, parsed, setterCb);
|
|
DeepProxy.checkLocalChange(proxy, onLocal);
|
|
initializing = false;
|
|
ready = true;
|
|
proxy._events.ready.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
config.onRemote = function() {
|
|
if (initializing) {
|
|
return;
|
|
}
|
|
var userDoc = realtime.getUserDoc();
|
|
var parsed = JSON.parse(userDoc);
|
|
DeepProxy.remoteChangeFlag = true;
|
|
DeepProxy.update(proxy, parsed, setterCb);
|
|
DeepProxy.remoteChangeFlag = false;
|
|
};
|
|
config.onMessage = function() {
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
if (cfg.updateProgress) {
|
|
cfg.updateProgress({
|
|
progress: idx++
|
|
});
|
|
}
|
|
};
|
|
config.onAbort = function(info) {
|
|
proxy._events.disconnect.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
config.onConnectionChange = function(info) {
|
|
if (info.state) {
|
|
initializing = true;
|
|
return;
|
|
}
|
|
proxy._events.disconnect.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
config.onMetadataUpdate = function(metadata) {
|
|
rt.metadata = metadata;
|
|
if (typeof cfg.onMetadataUpdate === "function") {
|
|
cfg.onMetadataUpdate(metadata);
|
|
}
|
|
};
|
|
config.onError = function(info) {
|
|
proxy._events.error.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
config.onChannelError = function(info) {
|
|
proxy._events.error.forEach((function(handler) {
|
|
handler.cb(info);
|
|
}));
|
|
};
|
|
if (cfg.common && typeof cfg.common.startRealtime === "function") {
|
|
realtime = rt.cpCnInner = cfg.common.startRealtime(config);
|
|
} else {
|
|
rt = Realtime.start(config);
|
|
}
|
|
rt.proxy = proxy;
|
|
rt.realtime = realtime;
|
|
var _setReadOnly = rt.setReadOnly;
|
|
rt.setReadOnly = function(state, crypto) {
|
|
readOnly = state;
|
|
if (_setReadOnly) {
|
|
_setReadOnly(state, crypto);
|
|
}
|
|
};
|
|
return rt;
|
|
};
|
|
return api;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireChainpadNetflux(), requireJson_sortify());
|
|
}
|
|
})();
|
|
})(chainpadListmap);
|
|
return chainpadListmap.exports;
|
|
}
|
|
var chainpadListmapExports = requireChainpadListmap();
|
|
function commonjsRequire(path) {
|
|
throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
|
|
}
|
|
var chainpad_dist$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredChainpad_dist;
|
|
function requireChainpad_dist() {
|
|
if (hasRequiredChainpad_dist) return chainpad_dist$1.exports;
|
|
hasRequiredChainpad_dist = 1;
|
|
(function(module, exports) {
|
|
(function() {
|
|
var r = function() {
|
|
var e = "function" == typeof commonjsRequire && commonjsRequire, r = function(i, o, u) {
|
|
o || (o = 0);
|
|
var n = r.resolve(i, o), t = r.m[o][n];
|
|
if (!t && e) {
|
|
if (t = e(n)) return t;
|
|
} else if (t && t.c && (o = t.c, n = t.m, t = r.m[o][t.m], !t)) throw new Error('failed to require "' + n + '" from ' + o);
|
|
if (!t) throw new Error('failed to require "' + i + '" from ' + u);
|
|
return t.exports || (t.exports = {}, t.call(t.exports, t, t.exports, r.relative(n, o))),
|
|
t.exports;
|
|
};
|
|
return r.resolve = function(e, n) {
|
|
var i = e, t = e + ".js", o = e + "/index.js";
|
|
return r.m[n][t] && t ? t : r.m[n][o] && o ? o : i;
|
|
}, r.relative = function(e, t) {
|
|
return function(n) {
|
|
if ("." != n.charAt(0)) return r(n, t, e);
|
|
var o = e.split("/"), f = n.split("/");
|
|
o.pop();
|
|
for (var i = 0; i < f.length; i++) {
|
|
var u = f[i];
|
|
".." == u ? o.pop() : "." != u && o.push(u);
|
|
}
|
|
return r(o.join("/"), t, e);
|
|
};
|
|
}, r;
|
|
}();
|
|
r.m = [];
|
|
r.m[0] = {
|
|
"json.sortify": {
|
|
c: 1,
|
|
m: "dist/JSON.sortify.js"
|
|
},
|
|
"fast-diff": {
|
|
c: 2,
|
|
m: "diff.js"
|
|
},
|
|
"Diff.js": function(module, exports, require) {
|
|
var FastDiff = require("fast-diff");
|
|
var transform = function(matches) {
|
|
var out = [];
|
|
var offset = 0;
|
|
var first = true;
|
|
var current = {
|
|
offset: 0,
|
|
toInsert: "",
|
|
toRemove: 0,
|
|
type: "Operation"
|
|
};
|
|
matches.forEach((function(el, i) {
|
|
if (el[0] === 0) {
|
|
if (!first) {
|
|
out.push(current);
|
|
offset = current.offset + current.toRemove;
|
|
current = {
|
|
offset,
|
|
toInsert: "",
|
|
toRemove: 0,
|
|
type: "Operation"
|
|
};
|
|
}
|
|
offset += el[1].length;
|
|
current.offset = offset;
|
|
} else if (el[0] === 1) {
|
|
current.toInsert = el[1];
|
|
} else {
|
|
current.toRemove = el[1].length;
|
|
}
|
|
if (i === matches.length - 1 && el[0] !== 0) {
|
|
out.push(current);
|
|
}
|
|
if (first) {
|
|
first = false;
|
|
}
|
|
}));
|
|
return out;
|
|
};
|
|
module.exports.diff = function(oldS, newS) {
|
|
return transform(FastDiff(oldS, newS));
|
|
};
|
|
},
|
|
"Patch.js": function(module, exports, require) {
|
|
var Common = require("./Common");
|
|
var Operation = require("./Operation");
|
|
var Sha = require("./sha256");
|
|
var Patch = module.exports;
|
|
var create = Patch.create = function(parentHash, isCheckpoint) {
|
|
var out = Object.freeze({
|
|
type: "Patch",
|
|
operations: [],
|
|
parentHash,
|
|
isCheckpoint: !!isCheckpoint,
|
|
mut: {
|
|
inverseOf: undefined
|
|
}
|
|
});
|
|
if (isCheckpoint) {
|
|
out.mut.inverseOf = out;
|
|
}
|
|
return out;
|
|
};
|
|
var check = Patch.check = function(patch, docLength_opt) {
|
|
Common.assert(patch.type === "Patch");
|
|
Common.assert(Array.isArray(patch.operations));
|
|
Common.assert(/^[0-9a-f]{64}$/.test(patch.parentHash));
|
|
for (var i = patch.operations.length - 1; i >= 0; i--) {
|
|
Operation.check(patch.operations[i], docLength_opt);
|
|
if (i > 0) {
|
|
Common.assert(!Operation.shouldMerge(patch.operations[i], patch.operations[i - 1]));
|
|
}
|
|
if (typeof docLength_opt === "number") {
|
|
docLength_opt += Operation.lengthChange(patch.operations[i]);
|
|
}
|
|
}
|
|
if (patch.isCheckpoint) {
|
|
Common.assert(patch.operations.length === 1);
|
|
Common.assert(patch.operations[0].offset === 0);
|
|
if (typeof docLength_opt === "number") {
|
|
Common.assert(!docLength_opt || patch.operations[0].toRemove === docLength_opt);
|
|
}
|
|
}
|
|
return patch;
|
|
};
|
|
Patch.toObj = function(patch) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
}
|
|
var out = new Array(patch.operations.length + 1);
|
|
var i;
|
|
for (i = 0; i < patch.operations.length; i++) {
|
|
out[i] = Operation.toObj(patch.operations[i]);
|
|
}
|
|
out[i] = patch.parentHash;
|
|
return out;
|
|
};
|
|
Patch.fromObj = function(obj, isCheckpoint) {
|
|
Common.assert(Array.isArray(obj) && obj.length > 0);
|
|
var patch = create(Sha.check(obj[obj.length - 1]), isCheckpoint);
|
|
var i;
|
|
for (i = 0; i < obj.length - 1; i++) {
|
|
patch.operations[i] = Operation.fromObj(obj[i]);
|
|
}
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
}
|
|
return patch;
|
|
};
|
|
var hash = function(text) {
|
|
return Sha.hex_sha256(text);
|
|
};
|
|
var addOperation = Patch.addOperation = function(patch, op) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
Operation.check(op);
|
|
}
|
|
for (var i = 0; i < patch.operations.length; i++) {
|
|
if (Operation.shouldMerge(patch.operations[i], op)) {
|
|
var maybeOp = Operation.merge(patch.operations[i], op);
|
|
patch.operations.splice(i, 1);
|
|
if (maybeOp === null) {
|
|
return;
|
|
}
|
|
op = maybeOp;
|
|
i--;
|
|
} else {
|
|
var out = Operation.rebase(patch.operations[i], op);
|
|
if (out === op) {
|
|
patch.operations.splice(i, 0, op);
|
|
return;
|
|
} else {
|
|
op = out;
|
|
}
|
|
}
|
|
}
|
|
patch.operations.push(op);
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
}
|
|
};
|
|
Patch.createCheckpoint = function(parentContent, checkpointContent, parentContentHash_opt) {
|
|
var op = Operation.create(0, parentContent.length, checkpointContent);
|
|
if (Common.PARANOIA && parentContentHash_opt) {
|
|
Common.assert(parentContentHash_opt === hash(parentContent));
|
|
}
|
|
parentContentHash_opt = parentContentHash_opt || hash(parentContent);
|
|
var out = create(parentContentHash_opt, true);
|
|
out.operations[0] = op;
|
|
return out;
|
|
};
|
|
var clone = Patch.clone = function(patch) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
}
|
|
var out = create(patch.parentHash, patch.isCheckpoint);
|
|
for (var i = 0; i < patch.operations.length; i++) {
|
|
out.operations[i] = patch.operations[i];
|
|
}
|
|
return out;
|
|
};
|
|
Patch.merge = function(oldPatch, newPatch) {
|
|
if (Common.PARANOIA) {
|
|
check(oldPatch);
|
|
check(newPatch);
|
|
}
|
|
if (oldPatch.isCheckpoint) {
|
|
Common.assert(newPatch.parentHash === oldPatch.parentHash);
|
|
if (newPatch.isCheckpoint) {
|
|
return create(oldPatch.parentHash);
|
|
}
|
|
return clone(newPatch);
|
|
} else if (newPatch.isCheckpoint) {
|
|
return clone(oldPatch);
|
|
}
|
|
oldPatch = clone(oldPatch);
|
|
for (var i = newPatch.operations.length - 1; i >= 0; i--) {
|
|
addOperation(oldPatch, newPatch.operations[i]);
|
|
}
|
|
return oldPatch;
|
|
};
|
|
Patch.apply = function(patch, doc) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
Common.assert(typeof doc === "string");
|
|
Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
|
|
}
|
|
var newDoc = doc;
|
|
for (var i = patch.operations.length - 1; i >= 0; i--) {
|
|
newDoc = Operation.apply(patch.operations[i], newDoc);
|
|
}
|
|
return newDoc;
|
|
};
|
|
Patch.lengthChange = function(patch) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
}
|
|
var out = 0;
|
|
for (var i = 0; i < patch.operations.length; i++) {
|
|
out += Operation.lengthChange(patch.operations[i]);
|
|
}
|
|
return out;
|
|
};
|
|
Patch.invert = function(patch, doc) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
Common.assert(typeof doc === "string");
|
|
Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
|
|
}
|
|
var newDoc = doc;
|
|
var operations = new Array(patch.operations.length);
|
|
for (var i = patch.operations.length - 1; i >= 0; i--) {
|
|
operations[i] = Operation.invert(patch.operations[i], newDoc);
|
|
newDoc = Operation.apply(patch.operations[i], newDoc);
|
|
}
|
|
var opOffsets = new Array(patch.operations.length);
|
|
(function() {
|
|
for (var i = operations.length - 1; i >= 0; i--) {
|
|
opOffsets[i] = operations[i].offset;
|
|
for (var j = i - 1; j >= 0; j--) {
|
|
opOffsets[i] += operations[j].toRemove - operations[j].toInsert.length;
|
|
}
|
|
}
|
|
})();
|
|
var rpatch = create(Sha.hex_sha256(newDoc), patch.isCheckpoint);
|
|
rpatch.operations.splice(0, rpatch.operations.length);
|
|
for (var j = 0; j < operations.length; j++) {
|
|
rpatch.operations[j] = Operation.create(opOffsets[j], operations[j].toRemove, operations[j].toInsert);
|
|
}
|
|
if (Common.PARANOIA) {
|
|
check(rpatch);
|
|
}
|
|
return rpatch;
|
|
};
|
|
Patch.simplify = function(patch, doc, operationSimplify) {
|
|
if (Common.PARANOIA) {
|
|
check(patch);
|
|
Common.assert(typeof doc === "string");
|
|
Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
|
|
}
|
|
var spatch = create(patch.parentHash);
|
|
var newDoc = doc;
|
|
var outOps = [];
|
|
var j = 0;
|
|
for (var i = patch.operations.length - 1; i >= 0; i--) {
|
|
var outOp = operationSimplify(patch.operations[i], newDoc, Operation.simplify);
|
|
if (outOp) {
|
|
newDoc = Operation.apply(outOp, newDoc);
|
|
outOps[j++] = outOp;
|
|
}
|
|
}
|
|
Array.prototype.push.apply(spatch.operations, outOps.reverse());
|
|
if (!spatch.operations[0]) {
|
|
spatch.operations.shift();
|
|
}
|
|
if (Common.PARANOIA) {
|
|
check(spatch);
|
|
}
|
|
return spatch;
|
|
};
|
|
Patch.equals = function(patchA, patchB) {
|
|
if (patchA.operations.length !== patchB.operations.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0; i < patchA.operations.length; i++) {
|
|
if (!Operation.equals(patchA.operations[i], patchB.operations[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var isCheckpointOp = function(op, text) {
|
|
return op.offset === 0 && op.toRemove === text.length && op.toInsert === text;
|
|
};
|
|
Patch.transform = function(toTransform, transformBy, doc, patchTransformer) {
|
|
if (Common.PARANOIA) {
|
|
check(toTransform, doc.length);
|
|
check(transformBy, doc.length);
|
|
if (Sha.hex_sha256(doc) !== toTransform.parentHash) {
|
|
throw new Error("wrong hash");
|
|
}
|
|
}
|
|
if (toTransform.parentHash !== transformBy.parentHash) {
|
|
throw new Error;
|
|
}
|
|
var afterTransformBy = Patch.apply(transformBy, doc);
|
|
var out = create(transformBy.mut.inverseOf ? transformBy.mut.inverseOf.parentHash : Sha.hex_sha256(afterTransformBy), toTransform.isCheckpoint);
|
|
if (transformBy.operations.length === 0) {
|
|
return clone(toTransform);
|
|
}
|
|
if (toTransform.operations.length === 0) {
|
|
if (toTransform.isCheckpoint) {
|
|
throw new Error;
|
|
}
|
|
return out;
|
|
}
|
|
if (toTransform.isCheckpoint || toTransform.operations.length === 1 && isCheckpointOp(toTransform.operations[0], doc)) {
|
|
throw new Error("Attempting to transform a checkpoint, this should not happen");
|
|
}
|
|
if (transformBy.operations.length === 1 && isCheckpointOp(transformBy.operations[0], doc)) {
|
|
if (!transformBy.isCheckpoint) {
|
|
throw new Error;
|
|
}
|
|
return toTransform;
|
|
}
|
|
if (transformBy.isCheckpoint) {
|
|
throw new Error;
|
|
}
|
|
var ops = patchTransformer(toTransform.operations, transformBy.operations, doc);
|
|
Array.prototype.push.apply(out.operations, ops);
|
|
if (Common.PARANOIA) {
|
|
check(out, afterTransformBy.length);
|
|
}
|
|
return out;
|
|
};
|
|
Patch.random = function(doc, opCount) {
|
|
Common.assert(typeof doc === "string");
|
|
opCount = opCount || Math.floor(Math.random() * 30) + 1;
|
|
var patch = create(Sha.hex_sha256(doc));
|
|
var docLength = doc.length;
|
|
while (opCount-- > 0) {
|
|
var op = Operation.random(docLength);
|
|
docLength += Operation.lengthChange(op);
|
|
addOperation(patch, op);
|
|
}
|
|
check(patch);
|
|
return patch;
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"SHA256.js": function(module, exports, require) {
|
|
(function() {
|
|
var chrsz = 8;
|
|
function safe_add(x, y) {
|
|
var lsw = (x & 65535) + (y & 65535);
|
|
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
|
return msw << 16 | lsw & 65535;
|
|
}
|
|
function S(X, n) {
|
|
return X >>> n | X << 32 - n;
|
|
}
|
|
function R(X, n) {
|
|
return X >>> n;
|
|
}
|
|
function Ch(x, y, z) {
|
|
return x & y ^ ~x & z;
|
|
}
|
|
function Maj(x, y, z) {
|
|
return x & y ^ x & z ^ y & z;
|
|
}
|
|
function Sigma0256(x) {
|
|
return S(x, 2) ^ S(x, 13) ^ S(x, 22);
|
|
}
|
|
function Sigma1256(x) {
|
|
return S(x, 6) ^ S(x, 11) ^ S(x, 25);
|
|
}
|
|
function Gamma0256(x) {
|
|
return S(x, 7) ^ S(x, 18) ^ R(x, 3);
|
|
}
|
|
function Gamma1256(x) {
|
|
return S(x, 17) ^ S(x, 19) ^ R(x, 10);
|
|
}
|
|
function newArray(n) {
|
|
var a = [];
|
|
for (;n > 0; n--) {
|
|
a.push(undefined);
|
|
}
|
|
return a;
|
|
}
|
|
function core_sha256(m, l) {
|
|
var K = [ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ];
|
|
var HASH = [ 1779033703, 3144134277, 1013904242, 2773480762, 1359893119, 2600822924, 528734635, 1541459225 ];
|
|
var W = newArray(64);
|
|
var a, b, c, d, e, f, g, h, i, j;
|
|
var T1, T2;
|
|
m[l >> 5] |= 128 << 24 - l % 32;
|
|
m[(l + 64 >> 9 << 4) + 15] = l;
|
|
for (var i = 0; i < m.length; i += 16) {
|
|
a = HASH[0];
|
|
b = HASH[1];
|
|
c = HASH[2];
|
|
d = HASH[3];
|
|
e = HASH[4];
|
|
f = HASH[5];
|
|
g = HASH[6];
|
|
h = HASH[7];
|
|
for (var j = 0; j < 64; j++) {
|
|
if (j < 16) {
|
|
W[j] = m[j + i];
|
|
} else {
|
|
W[j] = safe_add(safe_add(safe_add(Gamma1256(W[j - 2]), W[j - 7]), Gamma0256(W[j - 15])), W[j - 16]);
|
|
}
|
|
T1 = safe_add(safe_add(safe_add(safe_add(h, Sigma1256(e)), Ch(e, f, g)), K[j]), W[j]);
|
|
T2 = safe_add(Sigma0256(a), Maj(a, b, c));
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = safe_add(d, T1);
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = safe_add(T1, T2);
|
|
}
|
|
HASH[0] = safe_add(a, HASH[0]);
|
|
HASH[1] = safe_add(b, HASH[1]);
|
|
HASH[2] = safe_add(c, HASH[2]);
|
|
HASH[3] = safe_add(d, HASH[3]);
|
|
HASH[4] = safe_add(e, HASH[4]);
|
|
HASH[5] = safe_add(f, HASH[5]);
|
|
HASH[6] = safe_add(g, HASH[6]);
|
|
HASH[7] = safe_add(h, HASH[7]);
|
|
}
|
|
return HASH;
|
|
}
|
|
function str2binb(str) {
|
|
var bin = Array();
|
|
var mask = (1 << chrsz) - 1;
|
|
for (var i = 0; i < str.length * chrsz; i += chrsz) bin[i >> 5] |= (str.charCodeAt(i / chrsz) & mask) << 24 - i % 32;
|
|
return bin;
|
|
}
|
|
function binb2hex(binarray) {
|
|
var hex_tab = "0123456789abcdef";
|
|
var str = "";
|
|
for (var i = 0; i < binarray.length * 4; i++) {
|
|
str += hex_tab.charAt(binarray[i >> 2] >> (3 - i % 4) * 8 + 4 & 15) + hex_tab.charAt(binarray[i >> 2] >> (3 - i % 4) * 8 & 15);
|
|
}
|
|
return str;
|
|
}
|
|
function hex_sha256(s) {
|
|
return binb2hex(core_sha256(str2binb(s), s.length * chrsz));
|
|
}
|
|
module.exports.hex_sha256 = hex_sha256;
|
|
})();
|
|
},
|
|
"Common.js": function(module, exports, require) {
|
|
module.exports.global = function() {
|
|
if (typeof self !== "undefined") {
|
|
return self;
|
|
}
|
|
if (typeof commonjsGlobal !== "undefined") {
|
|
return commonjsGlobal;
|
|
}
|
|
if (typeof window !== "undefined") {
|
|
return window;
|
|
}
|
|
throw new Error("no self, nor global, nor window");
|
|
}();
|
|
var cfg = function(name) {
|
|
if (typeof localStorage !== "undefined" && localStorage[name]) {
|
|
return localStorage[name];
|
|
}
|
|
return module.exports.global[name];
|
|
};
|
|
var PARANOIA = module.exports.PARANOIA = cfg("ChainPad_PARANOIA");
|
|
module.exports.VALIDATE_ENTIRE_CHAIN_EACH_MSG = cfg("ChainPad_VALIDATE_ENTIRE_CHAIN_EACH_MSG");
|
|
module.exports.TESTING = cfg("ChainPad_TESTING");
|
|
module.exports.assert = function(expr) {
|
|
if (!expr) {
|
|
throw new Error("Failed assertion");
|
|
}
|
|
};
|
|
module.exports.isUint = function(integer) {
|
|
return typeof integer === "number" && Math.floor(integer) === integer && integer >= 0;
|
|
};
|
|
module.exports.randomASCII = function(length) {
|
|
var content = [];
|
|
for (var i = 0; i < length; i++) {
|
|
content[i] = String.fromCharCode(Math.floor(Math.random() * 256) % 57 + 65);
|
|
}
|
|
return content.join("");
|
|
};
|
|
module.exports.strcmp = function(a, b) {
|
|
if (PARANOIA && typeof a !== "string") {
|
|
throw new Error;
|
|
}
|
|
if (PARANOIA && typeof b !== "string") {
|
|
throw new Error;
|
|
}
|
|
return a === b ? 0 : a > b ? 1 : -1;
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"sha256.js": function(module, exports, require) {
|
|
var asm_sha256 = require("./sha256/exports.js");
|
|
var old = require("./SHA256.js");
|
|
var Common = require("./Common");
|
|
var brokenTextEncode = function(str) {
|
|
var out = new Uint8Array(str.length);
|
|
for (var i = 0; i < str.length; i++) {
|
|
out[i] = str.charCodeAt(i) & 255;
|
|
}
|
|
return out;
|
|
};
|
|
module.exports.check = function(hex) {
|
|
if (typeof hex !== "string") {
|
|
throw new Error;
|
|
}
|
|
if (!/[a-f0-9]{64}/.test(hex)) {
|
|
throw new Error;
|
|
}
|
|
return hex;
|
|
};
|
|
module.exports.hex_sha256 = function(d) {
|
|
d = d + "";
|
|
var ret = asm_sha256.hex(brokenTextEncode(d));
|
|
if (Common.PARANOIA) {
|
|
var oldHash = old.hex_sha256(d);
|
|
if (oldHash !== ret) {
|
|
try {
|
|
throw new Error;
|
|
} catch (e) {
|
|
console.log({
|
|
hashErr: e,
|
|
badHash: d,
|
|
asmHasher: asm_sha256.hex,
|
|
oldHasher: old.hex_sha256
|
|
});
|
|
}
|
|
return oldHash;
|
|
}
|
|
}
|
|
return ret;
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"Message.js": function(module, exports, require) {
|
|
var Common = require("./Common");
|
|
var Patch = require("./Patch");
|
|
var Sha = require("./sha256");
|
|
var Message = module.exports;
|
|
var PATCH = Message.PATCH = 2;
|
|
var CHECKPOINT = Message.CHECKPOINT = 4;
|
|
var check = Message.check = function(msg) {
|
|
Common.assert(msg.type === "Message");
|
|
Common.assert(msg.messageType === PATCH || msg.messageType === CHECKPOINT);
|
|
Patch.check(msg.content);
|
|
Common.assert(typeof msg.lastMsgHash === "string");
|
|
return msg;
|
|
};
|
|
var DUMMY_HASH = "";
|
|
var create = Message.create = function(type, content, lastMsgHash) {
|
|
var msg = {
|
|
type: "Message",
|
|
messageType: type,
|
|
content,
|
|
lastMsgHash,
|
|
hashOf: DUMMY_HASH,
|
|
mut: {
|
|
parentCount: undefined,
|
|
isInitialMessage: false,
|
|
isFromMe: false,
|
|
parent: undefined,
|
|
time: undefined,
|
|
author: undefined,
|
|
serverHash: undefined
|
|
}
|
|
};
|
|
msg.hashOf = hashOf(msg);
|
|
if (Common.PARANOIA) {
|
|
check(msg);
|
|
}
|
|
return Object.freeze(msg);
|
|
};
|
|
var toString = Message.toStr = Message.toString = function(msg) {
|
|
if (Common.PARANOIA) {
|
|
check(msg);
|
|
}
|
|
if (msg.messageType === PATCH || msg.messageType === CHECKPOINT) {
|
|
if (!msg.content) {
|
|
throw new Error;
|
|
}
|
|
return JSON.stringify([ msg.messageType, Patch.toObj(msg.content), msg.lastMsgHash ]);
|
|
} else {
|
|
throw new Error;
|
|
}
|
|
};
|
|
Message.fromString = function(str) {
|
|
var obj = {};
|
|
if (typeof str === "object") {
|
|
obj = str;
|
|
str = str.msg;
|
|
}
|
|
var m = JSON.parse(str);
|
|
if (m[0] !== CHECKPOINT && m[0] !== PATCH) {
|
|
throw new Error("invalid message type " + m[0]);
|
|
}
|
|
var msg = create(m[0], Patch.fromObj(m[1], m[0] === CHECKPOINT), m[2]);
|
|
msg.mut.author = obj.author;
|
|
msg.mut.time = obj.time && new Date(obj.time);
|
|
msg.mut.serverHash = obj.serverHash;
|
|
return Object.freeze(msg);
|
|
};
|
|
var hashOf = Message.hashOf = function(msg) {
|
|
if (Common.PARANOIA) {
|
|
check(msg);
|
|
}
|
|
var hash = Sha.hex_sha256(toString(msg));
|
|
return hash;
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"ChainPad.js": function(module, exports, require) {
|
|
var Common = module.exports.Common = require("./Common");
|
|
var Operation = module.exports.Operation = require("./Operation");
|
|
var Patch = module.exports.Patch = require("./Patch");
|
|
var Message = module.exports.Message = require("./Message");
|
|
var Sha = module.exports.Sha = require("./sha256");
|
|
var Diff = module.exports.Diff = require("./Diff");
|
|
var TextTransformer = module.exports.TextTransformer = require("./transform/TextTransformer");
|
|
module.exports.NaiveJSONTransformer = require("./transform/NaiveJSONTransformer");
|
|
module.exports.SmartJSONTransformer = require("./transform/SmartJSONTransformer");
|
|
var EMPTY_STR_HASH = module.exports.EMPTY_STR_HASH = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
|
|
var ZERO = "0000000000000000000000000000000000000000000000000000000000000000";
|
|
var DEFAULT_CHECKPOINT_INTERVAL = 50;
|
|
var DEFAULT_AVERAGE_SYNC_MILLISECONDS = 300;
|
|
var DEFAULT_STRICT_CHECKPOINT_VALIDATION = false;
|
|
var debug = function(realtime, msg) {
|
|
if (realtime.logLevel > 1) {
|
|
console.log("[" + realtime.userName + "] " + msg);
|
|
}
|
|
};
|
|
var warn = function(realtime, msg) {
|
|
if (realtime.logLevel > 0) {
|
|
console.error("[" + realtime.userName + "] " + msg);
|
|
}
|
|
};
|
|
var schedule = function(realtime, func, timeout) {
|
|
if (realtime.aborted) {
|
|
return;
|
|
}
|
|
if (!timeout) {
|
|
timeout = Math.floor(Math.random() * 2 * realtime.config.avgSyncMilliseconds);
|
|
}
|
|
var to = setTimeout((function() {
|
|
realtime.schedules.splice(realtime.schedules.indexOf(to), 1);
|
|
func();
|
|
}), timeout);
|
|
realtime.schedules.push(to);
|
|
return to;
|
|
};
|
|
var unschedule = function(realtime, schedule) {
|
|
var index = realtime.schedules.indexOf(schedule);
|
|
if (index > -1) {
|
|
realtime.schedules.splice(index, 1);
|
|
}
|
|
clearTimeout(schedule);
|
|
};
|
|
var onMessage = function(realtime, message, callback) {
|
|
if (!realtime.messageHandlers.length) {
|
|
callback("no onMessage() handler registered");
|
|
}
|
|
try {
|
|
realtime.messageHandlers.forEach((function(handler) {
|
|
handler(message, (function() {
|
|
callback.apply(null, arguments);
|
|
callback = function() {};
|
|
}));
|
|
}));
|
|
} catch (e) {
|
|
callback(e.stack);
|
|
}
|
|
};
|
|
var sendMessage = function(realtime, msg, callback, timeSent) {
|
|
var strMsg = Message.toStr(msg);
|
|
onMessage(realtime, strMsg, (function(err) {
|
|
if (err) {
|
|
debug(realtime, "Posting to server failed [" + err + "]");
|
|
realtime.pending = null;
|
|
realtime.syncSchedule = schedule(realtime, (function() {
|
|
sync(realtime);
|
|
}));
|
|
} else {
|
|
var pending = realtime.pending;
|
|
realtime.pending = null;
|
|
if (!pending) {
|
|
throw new Error;
|
|
}
|
|
Common.assert(pending.hash === msg.hashOf);
|
|
if (handleMessage(realtime, strMsg, true)) {
|
|
realtime.timeOfLastSuccess = +new Date;
|
|
realtime.lag = +new Date - pending.timeSent;
|
|
} else {
|
|
debug(realtime, "Our message [" + msg.hashOf + "] failed validation");
|
|
}
|
|
pending.callback();
|
|
}
|
|
}));
|
|
if (realtime.pending) {
|
|
throw new Error("there is already a pending message");
|
|
}
|
|
if (realtime.timeOfLastSuccess === -1) {
|
|
realtime.timeOfLastSuccess = +new Date;
|
|
}
|
|
realtime.pending = {
|
|
hash: msg.hashOf,
|
|
timeSent: +new Date,
|
|
callback: function() {
|
|
realtime.syncSchedule = schedule(realtime, (function() {
|
|
sync(realtime);
|
|
}), 0);
|
|
callback();
|
|
}
|
|
};
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
};
|
|
var settle = function(realtime) {
|
|
var onSettle = realtime.onSettle;
|
|
realtime.onSettle = [];
|
|
onSettle.forEach((function(handler) {
|
|
try {
|
|
handler();
|
|
} catch (e) {
|
|
warn(realtime, "Error in onSettle handler [" + e.stack + "]");
|
|
}
|
|
}));
|
|
};
|
|
var inversePatch = function(patch) {
|
|
if (!patch.mut.inverseOf) {
|
|
throw new Error;
|
|
}
|
|
return patch.mut.inverseOf;
|
|
};
|
|
var sync = function(realtime, timeSent) {
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
if (realtime.syncSchedule && !realtime.pending) {
|
|
unschedule(realtime, realtime.syncSchedule);
|
|
realtime.syncSchedule = null;
|
|
} else {
|
|
return;
|
|
}
|
|
realtime.uncommitted = Patch.simplify(realtime.uncommitted, realtime.authDoc, realtime.config.operationSimplify);
|
|
if (realtime.uncommitted.operations.length === 0) {
|
|
settle(realtime);
|
|
realtime.timeOfLastSuccess = +new Date;
|
|
realtime.syncSchedule = schedule(realtime, (function() {
|
|
sync(realtime);
|
|
}));
|
|
return;
|
|
}
|
|
var pc = parentCount(realtime, realtime.best) + 1;
|
|
if (pc % realtime.config.checkpointInterval === 0) {
|
|
var best = realtime.best;
|
|
debug(realtime, "Sending checkpoint (interval [" + realtime.config.checkpointInterval + "]) patch no [" + pc + "]");
|
|
debug(realtime, parentCount(realtime, realtime.best));
|
|
if (!best || !best.content || !inversePatch(best.content)) {
|
|
throw new Error;
|
|
}
|
|
var cpp = Patch.createCheckpoint(realtime.authDoc, realtime.authDoc, inversePatch(best.content).parentHash);
|
|
var cp = Message.create(Message.CHECKPOINT, cpp, best.hashOf);
|
|
sendMessage(realtime, cp, (function() {
|
|
debug(realtime, "Checkpoint sent and accepted");
|
|
}));
|
|
return;
|
|
}
|
|
var msg;
|
|
if (realtime.setContentPatch) {
|
|
msg = realtime.setContentPatch;
|
|
} else {
|
|
msg = Message.create(Message.PATCH, realtime.uncommitted, realtime.best.hashOf);
|
|
}
|
|
sendMessage(realtime, msg, (function() {
|
|
if (realtime.setContentPatch) {
|
|
debug(realtime, "initial Ack received [" + msg.hashOf + "]");
|
|
realtime.setContentPatch = null;
|
|
}
|
|
}));
|
|
};
|
|
var storeMessage = function(realtime, msg) {
|
|
Common.assert(msg.lastMsgHash);
|
|
Common.assert(msg.hashOf);
|
|
realtime.messages[msg.hashOf] = msg;
|
|
(realtime.messagesByParent[msg.lastMsgHash] = realtime.messagesByParent[msg.lastMsgHash] || []).push(msg);
|
|
};
|
|
var forgetMessage = function(realtime, msg) {
|
|
Common.assert(msg.lastMsgHash);
|
|
Common.assert(msg.hashOf);
|
|
delete realtime.messages[msg.hashOf];
|
|
var list = realtime.messagesByParent[msg.lastMsgHash];
|
|
Common.assert(list.indexOf(msg) > -1);
|
|
list.splice(list.indexOf(msg), 1);
|
|
if (list.length === 0) {
|
|
delete realtime.messagesByParent[msg.lastMsgHash];
|
|
}
|
|
var children = realtime.messagesByParent[msg.hashOf];
|
|
if (children) {
|
|
for (var i = 0; i < children.length; i++) {
|
|
delete children[i].mut.parent;
|
|
}
|
|
}
|
|
};
|
|
var create = function(config) {
|
|
var zeroPatch = Patch.create(EMPTY_STR_HASH);
|
|
mkInverse(zeroPatch, "");
|
|
var zeroMsg = Message.create(Message.PATCH, zeroPatch, ZERO);
|
|
zeroMsg.mut.parentCount = 0;
|
|
zeroMsg.mut.isInitialMessage = true;
|
|
var best = zeroMsg;
|
|
var initMsg;
|
|
if (config.initialState !== "") {
|
|
var initPatch = Patch.create(EMPTY_STR_HASH);
|
|
Patch.addOperation(initPatch, Operation.create(0, 0, config.initialState));
|
|
mkInverse(initPatch, "");
|
|
initMsg = Message.create(Message.PATCH, initPatch, zeroMsg.hashOf);
|
|
initMsg.mut.isInitialMessage = true;
|
|
best = initMsg;
|
|
}
|
|
var realtime = {
|
|
type: "ChainPad",
|
|
authDoc: config.initialState,
|
|
config,
|
|
logLevel: config.logLevel,
|
|
uncommitted: Patch.create(inversePatch(best.content).parentHash),
|
|
patchHandlers: [],
|
|
changeHandlers: [],
|
|
messageHandlers: [],
|
|
schedules: [],
|
|
aborted: false,
|
|
syncSchedule: -2,
|
|
userInterfaceContent: config.initialState,
|
|
setContentPatch: initMsg,
|
|
pending: undefined,
|
|
messages: {},
|
|
messagesByParent: {},
|
|
rootMessage: zeroMsg,
|
|
onSettle: [],
|
|
userName: config.userName,
|
|
best,
|
|
lag: 0,
|
|
timeOfLastSuccess: -1
|
|
};
|
|
storeMessage(realtime, zeroMsg);
|
|
if (initMsg) {
|
|
storeMessage(realtime, initMsg);
|
|
}
|
|
return realtime;
|
|
};
|
|
var getParent = function(realtime, message) {
|
|
var parent = message.mut.parent = message.mut.parent || realtime.messages[message.lastMsgHash];
|
|
return parent;
|
|
};
|
|
var check = function(realtime) {
|
|
Common.assert(realtime.type === "ChainPad");
|
|
Common.assert(typeof realtime.authDoc === "string");
|
|
Patch.check(realtime.uncommitted, realtime.authDoc.length);
|
|
var uiDoc = Patch.apply(realtime.uncommitted, realtime.authDoc);
|
|
Common.assert(uiDoc.length === uncommittedDocLength(realtime));
|
|
if (realtime.userInterfaceContent !== "") {
|
|
Common.assert(uiDoc === realtime.userInterfaceContent);
|
|
}
|
|
if (!Common.VALIDATE_ENTIRE_CHAIN_EACH_MSG) {
|
|
return;
|
|
}
|
|
var doc = realtime.authDoc;
|
|
var patchMsg = realtime.best;
|
|
Common.assert(inversePatch(patchMsg.content).parentHash === realtime.uncommitted.parentHash);
|
|
var patches = [];
|
|
do {
|
|
patches.push(patchMsg);
|
|
doc = Patch.apply(inversePatch(patchMsg.content), doc);
|
|
} while (patchMsg = getParent(realtime, patchMsg));
|
|
if (realtime.rootMessage.content.isCheckpoint) {
|
|
if (doc !== realtime.rootMessage.content.operations[0].toInsert) {
|
|
throw new Error;
|
|
}
|
|
} else if (doc !== "") {
|
|
throw new Error;
|
|
}
|
|
while (patchMsg = patches.pop()) {
|
|
doc = Patch.apply(patchMsg.content, doc);
|
|
}
|
|
Common.assert(doc === realtime.authDoc);
|
|
};
|
|
var uncommittedDocLength = function(realtime) {
|
|
return realtime.authDoc.length + Patch.lengthChange(realtime.uncommitted);
|
|
};
|
|
var doOperation = function(realtime, op) {
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
realtime.userInterfaceContent = Operation.apply(op, realtime.userInterfaceContent);
|
|
}
|
|
Operation.check(op, uncommittedDocLength(realtime));
|
|
Patch.addOperation(realtime.uncommitted, op);
|
|
};
|
|
var doPatch = function(realtime, patch) {
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
Common.assert(Patch.invert(realtime.uncommitted, realtime.authDoc).parentHash === patch.parentHash);
|
|
realtime.userInterfaceContent = Patch.apply(patch, realtime.userInterfaceContent);
|
|
}
|
|
Patch.check(patch, uncommittedDocLength(realtime));
|
|
realtime.uncommitted = Patch.merge(realtime.uncommitted, patch);
|
|
};
|
|
var isAncestorOf = function(realtime, ancestor, decendent) {
|
|
if (!ancestor) {
|
|
return false;
|
|
}
|
|
for (;;) {
|
|
if (!decendent) {
|
|
return false;
|
|
}
|
|
if (ancestor === decendent) {
|
|
return true;
|
|
}
|
|
decendent = getParent(realtime, decendent);
|
|
}
|
|
};
|
|
var parentCount = function(realtime, message) {
|
|
if (typeof message.mut.parentCount === "number") {
|
|
return message.mut.parentCount;
|
|
}
|
|
var msgs = [];
|
|
for (;typeof message.mut.parentCount !== "number"; message = getParent(realtime, message)) {
|
|
if (!message) {
|
|
if (message === realtime.rootMessage) {
|
|
throw new Error("root message does not have parent count");
|
|
}
|
|
throw new Error("parentCount called on unlinked message");
|
|
}
|
|
msgs.unshift(message);
|
|
}
|
|
var pc = message.mut.parentCount;
|
|
for (var i = 0; i < msgs.length; i++) {
|
|
msgs[i].mut.parentCount = ++pc;
|
|
}
|
|
return pc;
|
|
};
|
|
var applyPatch = function(realtime, isFromMe, patch) {
|
|
Common.assert(patch);
|
|
var newAuthDoc;
|
|
if (isFromMe) {
|
|
Common.assert(patch.parentHash === realtime.uncommitted.parentHash);
|
|
realtime.uncommitted = Patch.merge(inversePatch(patch), realtime.uncommitted);
|
|
} else {
|
|
realtime.uncommitted = Patch.transform(realtime.uncommitted, patch, realtime.authDoc, realtime.config.patchTransformer);
|
|
if (realtime.config.validateContent) {
|
|
newAuthDoc = Patch.apply(patch, realtime.authDoc);
|
|
var userDoc = Patch.apply(realtime.uncommitted, newAuthDoc);
|
|
if (!realtime.config.validateContent(userDoc)) {
|
|
warn(realtime, "Transformed patch is not valid");
|
|
realtime.uncommitted = Patch.create(Sha.hex_sha256(realtime.authDoc));
|
|
}
|
|
}
|
|
}
|
|
Common.assert(realtime.uncommitted.parentHash === inversePatch(patch).parentHash);
|
|
realtime.authDoc = newAuthDoc || Patch.apply(patch, realtime.authDoc);
|
|
if (Common.PARANOIA) {
|
|
Common.assert(realtime.uncommitted.parentHash === inversePatch(patch).parentHash);
|
|
Common.assert(Sha.hex_sha256(realtime.authDoc) === realtime.uncommitted.parentHash);
|
|
realtime.userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
|
|
}
|
|
};
|
|
var revertPatch = function(realtime, isFromMe, patch) {
|
|
applyPatch(realtime, isFromMe, inversePatch(patch));
|
|
};
|
|
var getBestChild = function(realtime, msg) {
|
|
var best = msg;
|
|
(realtime.messagesByParent[msg.hashOf] || []).forEach((function(child) {
|
|
Common.assert(child.lastMsgHash === msg.hashOf);
|
|
child = getBestChild(realtime, child);
|
|
if (parentCount(realtime, child) > parentCount(realtime, best)) {
|
|
best = child;
|
|
}
|
|
}));
|
|
return best;
|
|
};
|
|
var pushUIPatch = function(realtime, patch) {
|
|
if (!patch.operations.length) {
|
|
return;
|
|
}
|
|
realtime.patchHandlers.forEach((function(handler) {
|
|
handler(patch);
|
|
}));
|
|
realtime.changeHandlers.forEach((function(handler) {
|
|
patch.operations.forEach((function(op) {
|
|
handler(op.offset, op.toRemove, op.toInsert);
|
|
}));
|
|
}));
|
|
};
|
|
var validContent = function(realtime, contentGetter) {
|
|
try {
|
|
return realtime.config.validateContent(contentGetter());
|
|
} catch (e) {
|
|
warn(realtime, "Error in content validator [" + e.stack + "]");
|
|
}
|
|
return false;
|
|
};
|
|
var forEachParent = function(realtime, patch, callback) {
|
|
for (var m = getParent(realtime, patch); m; m = getParent(realtime, m)) {
|
|
if (callback(m) === false) {
|
|
return;
|
|
}
|
|
}
|
|
};
|
|
var mkInverse = function(patch, content) {
|
|
if (patch.mut.inverseOf) {
|
|
return;
|
|
}
|
|
var inverse = patch.mut.inverseOf = Patch.invert(patch, content);
|
|
inverse.mut.inverseOf = patch;
|
|
};
|
|
var handleMessage = function(realtime, msgStr, isFromMe) {
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
var msg = Message.fromString(msgStr);
|
|
debug(realtime, JSON.stringify([ msg.hashOf, msg.content.operations ]));
|
|
if (realtime.messages[msg.hashOf]) {
|
|
if (realtime.setContentPatch && realtime.setContentPatch.hashOf === msg.hashOf) {
|
|
realtime.setContentPatch = null;
|
|
} else {
|
|
if (msg.content.isCheckpoint) {
|
|
debug(realtime, "[" + (isFromMe ? "our" : "their") + "] Checkpoint [" + msg.hashOf + "] is already known");
|
|
return true;
|
|
}
|
|
debug(realtime, "Patch [" + msg.hashOf + "] is already known");
|
|
}
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
return;
|
|
}
|
|
if (msg.content.isCheckpoint && !validContent(realtime, (function() {
|
|
return msg.content.operations[0].toInsert;
|
|
}))) {
|
|
debug(realtime, "Checkpoint [" + msg.hashOf + "] failed content validation");
|
|
return;
|
|
}
|
|
storeMessage(realtime, msg);
|
|
if (!isAncestorOf(realtime, realtime.rootMessage, msg)) {
|
|
if (msg.content.isCheckpoint && realtime.best.mut.isInitialMessage) {
|
|
debug(realtime, "applying checkpoint [" + msg.hashOf + "]");
|
|
var userDoc = Patch.apply(realtime.uncommitted, realtime.authDoc);
|
|
Common.assert(!Common.PARANOIA || realtime.userInterfaceContent === userDoc);
|
|
var fixUserDocPatch = Patch.invert(realtime.uncommitted, realtime.authDoc);
|
|
Patch.addOperation(fixUserDocPatch, Operation.create(0, realtime.authDoc.length, msg.content.operations[0].toInsert));
|
|
fixUserDocPatch = Patch.simplify(fixUserDocPatch, userDoc, realtime.config.operationSimplify);
|
|
msg.mut.parentCount = 0;
|
|
realtime.rootMessage = realtime.best = msg;
|
|
realtime.authDoc = msg.content.operations[0].toInsert;
|
|
realtime.uncommitted = Patch.create(Sha.hex_sha256(realtime.authDoc));
|
|
pushUIPatch(realtime, fixUserDocPatch);
|
|
if (Common.PARANOIA) {
|
|
realtime.userInterfaceContent = realtime.authDoc;
|
|
}
|
|
return true;
|
|
} else {
|
|
debug(realtime, "Patch [" + msg.hashOf + "] not connected to root (parent: [" + msg.lastMsgHash + "])");
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
msg = getBestChild(realtime, msg);
|
|
msg.mut.isFromMe = isFromMe;
|
|
var patch = msg.content;
|
|
var toRevert = [];
|
|
var commonAncestor = realtime.best;
|
|
if (!isAncestorOf(realtime, realtime.best, msg)) {
|
|
var pcBest = parentCount(realtime, realtime.best);
|
|
var pcMsg = parentCount(realtime, msg);
|
|
if (pcBest < pcMsg || pcBest === pcMsg && Common.strcmp(realtime.best.hashOf, msg.hashOf) > 0) {
|
|
while (commonAncestor && !isAncestorOf(realtime, commonAncestor, msg)) {
|
|
toRevert.push(commonAncestor);
|
|
commonAncestor = getParent(realtime, commonAncestor);
|
|
}
|
|
Common.assert(commonAncestor);
|
|
debug(realtime, "Patch [" + msg.hashOf + "] better than best chain, switching");
|
|
} else {
|
|
debug(realtime, "Patch [" + msg.hashOf + "] chain is [" + pcMsg + "] best chain is [" + pcBest + "]");
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
var toApply = [];
|
|
var current = msg;
|
|
do {
|
|
toApply.unshift(current);
|
|
current = getParent(realtime, current);
|
|
Common.assert(current);
|
|
} while (current !== commonAncestor);
|
|
var authDocAtTimeOfPatch = realtime.authDoc;
|
|
toRevert.forEach((function(tr) {
|
|
authDocAtTimeOfPatch = Patch.apply(inversePatch(tr.content), authDocAtTimeOfPatch);
|
|
}));
|
|
toApply.forEach((function(ta, i) {
|
|
if (i === toApply.length - 1) {
|
|
return;
|
|
}
|
|
mkInverse(ta.content, authDocAtTimeOfPatch);
|
|
authDocAtTimeOfPatch = Patch.apply(ta.content, authDocAtTimeOfPatch);
|
|
}));
|
|
var headAtTimeOfPatch = realtime.best;
|
|
if (toApply.length > 1) {
|
|
headAtTimeOfPatch = toApply[toApply.length - 2];
|
|
Common.assert(headAtTimeOfPatch);
|
|
} else if (toRevert.length) {
|
|
headAtTimeOfPatch = getParent(realtime, toRevert[toRevert.length - 1]);
|
|
Common.assert(headAtTimeOfPatch);
|
|
}
|
|
Common.assert(inversePatch(headAtTimeOfPatch.content).parentHash);
|
|
Common.assert(!Common.PARANOIA || inversePatch(headAtTimeOfPatch.content).parentHash === Sha.hex_sha256(authDocAtTimeOfPatch));
|
|
if (inversePatch(headAtTimeOfPatch.content).parentHash !== patch.parentHash) {
|
|
debug(realtime, "patch [" + msg.hashOf + "] parentHash is not valid");
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
if (Common.TESTING) {
|
|
throw new Error;
|
|
}
|
|
forgetMessage(realtime, msg);
|
|
return;
|
|
}
|
|
if (patch.isCheckpoint && realtime.config.noPrune) ; else if (patch.isCheckpoint) {
|
|
var checkpointP;
|
|
forEachParent(realtime, msg, (function(m) {
|
|
if (m.content.isCheckpoint) {
|
|
if (checkpointP) {
|
|
checkpointP = m;
|
|
return false;
|
|
}
|
|
checkpointP = m;
|
|
}
|
|
}));
|
|
if (checkpointP && checkpointP !== realtime.rootMessage) {
|
|
var point = parentCount(realtime, checkpointP);
|
|
if (realtime.config.strictCheckpointValidation && point % realtime.config.checkpointInterval !== 0) {
|
|
debug(realtime, "checkpoint [" + msg.hashOf + "] at invalid point [" + point + "]");
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
if (Common.TESTING) {
|
|
throw new Error;
|
|
}
|
|
forgetMessage(realtime, msg);
|
|
return;
|
|
}
|
|
debug(realtime, "checkpoint [" + msg.hashOf + "]");
|
|
forEachParent(realtime, checkpointP, (function(m) {
|
|
debug(realtime, "pruning [" + m.hashOf + "]");
|
|
forgetMessage(realtime, m);
|
|
}));
|
|
realtime.rootMessage = checkpointP;
|
|
}
|
|
} else {
|
|
var simplePatch = Patch.simplify(patch, authDocAtTimeOfPatch, realtime.config.operationSimplify);
|
|
if (!Patch.equals(simplePatch, patch)) {
|
|
debug(realtime, "patch [" + msg.hashOf + "] can be simplified");
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
if (Common.TESTING) {
|
|
throw new Error;
|
|
}
|
|
forgetMessage(realtime, msg);
|
|
return;
|
|
}
|
|
if (!validContent(realtime, (function() {
|
|
return Patch.apply(patch, authDocAtTimeOfPatch);
|
|
}))) {
|
|
debug(realtime, "Patch [" + msg.hashOf + "] failed content validation");
|
|
return;
|
|
}
|
|
}
|
|
mkInverse(patch, authDocAtTimeOfPatch);
|
|
realtime.uncommitted = Patch.simplify(realtime.uncommitted, realtime.authDoc, realtime.config.operationSimplify);
|
|
var oldUserInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
|
|
if (Common.PARANOIA) {
|
|
Common.assert(oldUserInterfaceContent === realtime.userInterfaceContent);
|
|
}
|
|
var uncommittedPatch = Patch.invert(realtime.uncommitted, realtime.authDoc);
|
|
toRevert.forEach((function(tr) {
|
|
debug(realtime, "reverting [" + tr.hashOf + "]");
|
|
if (tr.mut.isFromMe) {
|
|
debug(realtime, "reverting patch 'from me' [" + JSON.stringify(tr.content.operations) + "]");
|
|
}
|
|
uncommittedPatch = Patch.merge(uncommittedPatch, inversePatch(tr.content));
|
|
revertPatch(realtime, tr.mut.isFromMe, tr.content);
|
|
}));
|
|
toApply.forEach((function(ta) {
|
|
debug(realtime, "applying [" + ta.hashOf + "]");
|
|
uncommittedPatch = Patch.merge(uncommittedPatch, ta.content);
|
|
applyPatch(realtime, ta.mut.isFromMe, ta.content);
|
|
}));
|
|
uncommittedPatch = Patch.merge(uncommittedPatch, realtime.uncommitted);
|
|
uncommittedPatch = Patch.simplify(uncommittedPatch, oldUserInterfaceContent, realtime.config.operationSimplify);
|
|
realtime.best = msg;
|
|
if (Common.PARANOIA) {
|
|
var newUserInterfaceContent = Patch.apply(uncommittedPatch, oldUserInterfaceContent);
|
|
Common.assert(realtime.userInterfaceContent.length === uncommittedDocLength(realtime));
|
|
Common.assert(newUserInterfaceContent === realtime.userInterfaceContent);
|
|
}
|
|
pushUIPatch(realtime, uncommittedPatch);
|
|
if (!realtime.uncommitted.operations.length) {
|
|
settle(realtime);
|
|
}
|
|
if (Common.PARANOIA) {
|
|
check(realtime);
|
|
}
|
|
return true;
|
|
};
|
|
var getDepthOfState = function(content, minDepth, realtime) {
|
|
Common.assert(typeof content === "string");
|
|
minDepth = minDepth || 0;
|
|
if (minDepth === 0 && realtime.authDoc === content) {
|
|
return 0;
|
|
}
|
|
var hash = Sha.hex_sha256(content);
|
|
var patchMsg = realtime.best;
|
|
var depth = 0;
|
|
do {
|
|
if (depth < minDepth) ; else {
|
|
if (patchMsg.content.parentHash === hash) {
|
|
return depth + 1;
|
|
}
|
|
}
|
|
depth++;
|
|
} while (patchMsg = getParent(realtime, patchMsg));
|
|
return -1;
|
|
};
|
|
var getContentAtState = function(realtime, msg) {
|
|
var patches = [ msg ];
|
|
while (patches[0] !== realtime.rootMessage) {
|
|
var parent = getParent(realtime, patches[0]);
|
|
if (!parent) {
|
|
return {
|
|
error: "not connected to root",
|
|
doc: undefined
|
|
};
|
|
}
|
|
patches.unshift(parent);
|
|
}
|
|
var doc = "";
|
|
if (realtime.rootMessage.content.operations.length) {
|
|
Common.assert(realtime.rootMessage.content.operations.length === 1);
|
|
doc = realtime.rootMessage.content.operations[0].toInsert;
|
|
}
|
|
for (var i = 1; i < patches.length; i++) {
|
|
doc = Patch.apply(patches[i].content, doc);
|
|
}
|
|
return {
|
|
error: undefined,
|
|
doc
|
|
};
|
|
};
|
|
var wrapMessage = function(realtime, msg) {
|
|
return Object.freeze({
|
|
type: "Block",
|
|
hashOf: msg.hashOf,
|
|
lastMsgHash: msg.lastMsgHash,
|
|
isCheckpoint: !!msg.content.isCheckpoint,
|
|
isFromMe: msg.mut && msg.mut.isFromMe,
|
|
author: msg.mut && msg.mut.author,
|
|
serverHash: msg.mut && msg.mut.serverHash,
|
|
time: msg.mut && msg.mut.time,
|
|
getParent: function() {
|
|
var parentMsg = getParent(realtime, msg);
|
|
if (parentMsg) {
|
|
return wrapMessage(realtime, parentMsg);
|
|
}
|
|
},
|
|
getContent: function() {
|
|
return getContentAtState(realtime, msg);
|
|
},
|
|
getPatch: function() {
|
|
return Patch.clone(msg.content);
|
|
},
|
|
getInversePatch: function() {
|
|
return Patch.clone(inversePatch(msg.content));
|
|
},
|
|
equals: function(block, msgOpt) {
|
|
if (msgOpt) {
|
|
return msg === msgOpt;
|
|
}
|
|
if (!block || typeof block !== "object" || block.type !== "Block") {
|
|
return false;
|
|
}
|
|
return block.equals(block, msg);
|
|
}
|
|
});
|
|
};
|
|
var mkConfig = function(config) {
|
|
config = config || {};
|
|
if (config.transformFunction) {
|
|
throw new Error("chainpad config transformFunction is nolonger used");
|
|
}
|
|
return Object.freeze({
|
|
initialState: config.initialState || "",
|
|
checkpointInterval: config.checkpointInterval || DEFAULT_CHECKPOINT_INTERVAL,
|
|
avgSyncMilliseconds: config.avgSyncMilliseconds || DEFAULT_AVERAGE_SYNC_MILLISECONDS,
|
|
strictCheckpointValidation: config.strictCheckpointValidation || DEFAULT_STRICT_CHECKPOINT_VALIDATION,
|
|
operationSimplify: config.operationSimplify || Operation.simplify,
|
|
logLevel: typeof config.logLevel === "number" ? config.logLevel : 2,
|
|
noPrune: config.noPrune,
|
|
patchTransformer: config.patchTransformer || TextTransformer,
|
|
userName: config.userName || "anonymous",
|
|
validateContent: config.validateContent || function(x) {
|
|
return true;
|
|
},
|
|
diffFunction: config.diffFunction || function(strA, strB) {
|
|
return Diff.diff(strA, strB);
|
|
}
|
|
});
|
|
};
|
|
module.exports.create = function(conf) {
|
|
var realtime = create(mkConfig(conf));
|
|
var out = {
|
|
onPatch: function(handler) {
|
|
Common.assert(typeof handler === "function");
|
|
realtime.patchHandlers.push(handler);
|
|
},
|
|
patch: function(patch, x, y) {
|
|
if (typeof patch === "number") {
|
|
if (typeof x !== "number" || typeof y !== "string") {
|
|
throw new Error;
|
|
}
|
|
out.change(patch, x, y);
|
|
return;
|
|
}
|
|
doPatch(realtime, patch);
|
|
},
|
|
onChange: function(handler) {
|
|
Common.assert(typeof handler === "function");
|
|
realtime.changeHandlers.push(handler);
|
|
},
|
|
change: function(offset, count, chars) {
|
|
if (count === 0 && chars === "") {
|
|
return;
|
|
}
|
|
doOperation(realtime, Operation.create(offset, count, chars));
|
|
},
|
|
contentUpdate: function(newContent) {
|
|
var ops = realtime.config.diffFunction(realtime.authDoc, newContent);
|
|
var uncommitted = Patch.create(realtime.uncommitted.parentHash);
|
|
Array.prototype.push.apply(uncommitted.operations, ops);
|
|
realtime.uncommitted = uncommitted;
|
|
},
|
|
onMessage: function(handler) {
|
|
Common.assert(typeof handler === "function");
|
|
realtime.messageHandlers.push(handler);
|
|
},
|
|
message: function(message) {
|
|
handleMessage(realtime, message, false);
|
|
},
|
|
start: function() {
|
|
realtime.aborted = false;
|
|
if (realtime.syncSchedule) {
|
|
unschedule(realtime, realtime.syncSchedule);
|
|
}
|
|
realtime.pending = null;
|
|
realtime.syncSchedule = schedule(realtime, (function() {
|
|
sync(realtime);
|
|
}));
|
|
},
|
|
abort: function() {
|
|
realtime.aborted = true;
|
|
realtime.schedules.forEach((function(s) {
|
|
clearTimeout(s);
|
|
}));
|
|
},
|
|
sync: function() {
|
|
sync(realtime);
|
|
},
|
|
getAuthDoc: function() {
|
|
return realtime.authDoc;
|
|
},
|
|
getUserDoc: function() {
|
|
return Patch.apply(realtime.uncommitted, realtime.authDoc);
|
|
},
|
|
getDepthOfState: function(content, minDepth) {
|
|
return getDepthOfState(content, minDepth, realtime);
|
|
},
|
|
onSettle: function(handler) {
|
|
Common.assert(typeof handler === "function");
|
|
realtime.onSettle.push(handler);
|
|
},
|
|
getAuthBlock: function() {
|
|
return wrapMessage(realtime, realtime.best);
|
|
},
|
|
getBlockForHash: function(hash) {
|
|
Common.assert(typeof hash === "string");
|
|
var msg = realtime.messages[hash];
|
|
if (msg) {
|
|
return wrapMessage(realtime, msg);
|
|
}
|
|
},
|
|
getLag: function() {
|
|
var isPending = !!realtime.pending;
|
|
var lag = realtime.lag;
|
|
if (realtime.pending) {
|
|
lag = +new Date - realtime.timeOfLastSuccess;
|
|
}
|
|
return {
|
|
pending: isPending,
|
|
lag,
|
|
active: !realtime.aborted && realtime.syncSchedule !== -2
|
|
};
|
|
},
|
|
_: undefined
|
|
};
|
|
out._ = realtime;
|
|
return Object.freeze(out);
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"Operation.js": function(module, exports, require) {
|
|
var Common = require("./Common");
|
|
var Operation = module.exports;
|
|
var check = Operation.check = function(op, docLength_opt) {
|
|
Common.assert(op.type === "Operation");
|
|
if (!Common.isUint(op.offset)) {
|
|
throw new Error;
|
|
}
|
|
if (!Common.isUint(op.toRemove)) {
|
|
throw new Error;
|
|
}
|
|
if (typeof op.toInsert !== "string") {
|
|
throw new Error;
|
|
}
|
|
if (op.toRemove < 1 && op.toInsert.length < 1) {
|
|
throw new Error;
|
|
}
|
|
Common.assert(typeof docLength_opt !== "number" || op.offset + op.toRemove <= docLength_opt);
|
|
return op;
|
|
};
|
|
var create = Operation.create = function(offset, toRemove, toInsert) {
|
|
var out = {
|
|
type: "Operation",
|
|
offset: offset || 0,
|
|
toRemove: toRemove || 0,
|
|
toInsert: toInsert || ""
|
|
};
|
|
if (Common.PARANOIA) {
|
|
check(out);
|
|
}
|
|
return Object.freeze(out);
|
|
};
|
|
Operation.toObj = function(op) {
|
|
if (Common.PARANOIA) {
|
|
check(op);
|
|
}
|
|
return [ op.offset, op.toRemove, op.toInsert ];
|
|
};
|
|
Operation.fromObj = function(obj) {
|
|
Common.assert(Array.isArray(obj) && obj.length === 3);
|
|
return create(obj[0], obj[1], obj[2]);
|
|
};
|
|
var apply = Operation.apply = function(op, doc) {
|
|
if (Common.PARANOIA) {
|
|
Common.assert(typeof doc === "string");
|
|
check(op, doc.length);
|
|
}
|
|
return doc.substring(0, op.offset) + op.toInsert + doc.substring(op.offset + op.toRemove);
|
|
};
|
|
Operation.applyMulti = function(ops, doc) {
|
|
for (var i = ops.length - 1; i >= 0; i--) {
|
|
doc = apply(ops[i], doc);
|
|
}
|
|
return doc;
|
|
};
|
|
var invert = Operation.invert = function(op, doc) {
|
|
if (Common.PARANOIA) {
|
|
check(op);
|
|
Common.assert(typeof doc === "string");
|
|
Common.assert(op.offset + op.toRemove <= doc.length);
|
|
}
|
|
return create(op.offset, op.toInsert.length, (" " + doc.substring(op.offset, op.offset + op.toRemove)).slice(1));
|
|
};
|
|
var surrogatePattern = /[\uD800-\uDBFF]|[\uDC00-\uDFFF]/;
|
|
var hasSurrogate = Operation.hasSurrogate = function(str) {
|
|
return surrogatePattern.test(str);
|
|
};
|
|
Operation.simplify = function(op, doc) {
|
|
if (Common.PARANOIA) {
|
|
check(op);
|
|
Common.assert(typeof doc === "string");
|
|
Common.assert(op.offset + op.toRemove <= doc.length);
|
|
}
|
|
var rop = invert(op, doc);
|
|
var minLen = Math.min(op.toInsert.length, rop.toInsert.length);
|
|
var i = 0;
|
|
while (i < minLen && rop.toInsert[i] === op.toInsert[i]) {
|
|
if (hasSurrogate(rop.toInsert[i]) || hasSurrogate(op.toInsert[i])) {
|
|
if (op.toInsert[i + 1] === rop.toInsert[i + 1]) {
|
|
i++;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
i++;
|
|
}
|
|
var opOffset = op.offset + i;
|
|
var opToRemove = op.toRemove - i;
|
|
var opToInsert = op.toInsert.substring(i);
|
|
var ropToInsert = rop.toInsert.substring(i);
|
|
if (ropToInsert.length === opToInsert.length) {
|
|
for (i = ropToInsert.length - 1; i >= 0 && ropToInsert[i] === opToInsert[i]; i--) ;
|
|
opToInsert = opToInsert.substring(0, i + 1);
|
|
opToRemove = i + 1;
|
|
}
|
|
if (opToRemove === 0 && opToInsert.length === 0) {
|
|
return null;
|
|
}
|
|
return create(opOffset, opToRemove, opToInsert);
|
|
};
|
|
Operation.equals = function(opA, opB) {
|
|
return opA.toRemove === opB.toRemove && opA.toInsert === opB.toInsert && opA.offset === opB.offset;
|
|
};
|
|
Operation.lengthChange = function(op) {
|
|
if (Common.PARANOIA) {
|
|
check(op);
|
|
}
|
|
return op.toInsert.length - op.toRemove;
|
|
};
|
|
Operation.merge = function(oldOpOrig, newOpOrig) {
|
|
if (Common.PARANOIA) {
|
|
check(newOpOrig);
|
|
check(oldOpOrig);
|
|
}
|
|
var oldOp_offset = oldOpOrig.offset;
|
|
var oldOp_toRemove = oldOpOrig.toRemove;
|
|
var oldOp_toInsert = oldOpOrig.toInsert;
|
|
var newOp_offset = newOpOrig.offset;
|
|
var newOp_toRemove = newOpOrig.toRemove;
|
|
var newOp_toInsert = newOpOrig.toInsert;
|
|
var offsetDiff = newOp_offset - oldOp_offset;
|
|
if (newOp_toRemove > 0) {
|
|
var origOldInsert = oldOp_toInsert;
|
|
oldOp_toInsert = oldOp_toInsert.substring(0, offsetDiff) + oldOp_toInsert.substring(offsetDiff + newOp_toRemove);
|
|
newOp_toRemove -= origOldInsert.length - oldOp_toInsert.length;
|
|
if (newOp_toRemove < 0) {
|
|
newOp_toRemove = 0;
|
|
}
|
|
oldOp_toRemove += newOp_toRemove;
|
|
newOp_toRemove = 0;
|
|
}
|
|
if (offsetDiff < 0) {
|
|
oldOp_offset += offsetDiff;
|
|
oldOp_toInsert = newOp_toInsert + oldOp_toInsert;
|
|
} else if (oldOp_toInsert.length === offsetDiff) {
|
|
oldOp_toInsert = oldOp_toInsert + newOp_toInsert;
|
|
} else if (oldOp_toInsert.length > offsetDiff) {
|
|
oldOp_toInsert = oldOp_toInsert.substring(0, offsetDiff) + newOp_toInsert + oldOp_toInsert.substring(offsetDiff);
|
|
} else {
|
|
throw new Error("should never happen\n" + JSON.stringify([ oldOpOrig, newOpOrig ], null, " "));
|
|
}
|
|
if (oldOp_toInsert === "" && oldOp_toRemove === 0) {
|
|
return null;
|
|
}
|
|
return create(oldOp_offset, oldOp_toRemove, oldOp_toInsert);
|
|
};
|
|
Operation.shouldMerge = function(oldOp, newOp) {
|
|
if (Common.PARANOIA) {
|
|
check(oldOp);
|
|
check(newOp);
|
|
}
|
|
if (newOp.offset < oldOp.offset) {
|
|
return oldOp.offset <= newOp.offset + newOp.toRemove;
|
|
} else {
|
|
return newOp.offset <= oldOp.offset + oldOp.toInsert.length;
|
|
}
|
|
};
|
|
Operation.rebase = function(oldOp, newOp) {
|
|
if (Common.PARANOIA) {
|
|
check(oldOp);
|
|
check(newOp);
|
|
}
|
|
if (newOp.offset < oldOp.offset) {
|
|
return newOp;
|
|
}
|
|
return create(newOp.offset + oldOp.toRemove - oldOp.toInsert.length, newOp.toRemove, newOp.toInsert);
|
|
};
|
|
Operation.random = function(docLength) {
|
|
Common.assert(Common.isUint(docLength));
|
|
var offset = Math.floor(Math.random() * 1e8 % docLength) || 0;
|
|
var toRemove = Math.floor(Math.random() * 1e8 % (docLength - offset)) || 0;
|
|
var toInsert = "";
|
|
do {
|
|
toInsert = Common.randomASCII(Math.floor(Math.random() * 20));
|
|
} while (toRemove === 0 && toInsert === "");
|
|
return create(offset, toRemove, toInsert);
|
|
};
|
|
Object.freeze(module.exports);
|
|
},
|
|
"sha256/hash.js": function(module, exports, require) {
|
|
var Utils = require("./utils.js");
|
|
function hash_reset() {
|
|
this.result = null;
|
|
this.pos = 0;
|
|
this.len = 0;
|
|
this.asm.reset();
|
|
return this;
|
|
}
|
|
function hash_process(data) {
|
|
if (this.result !== null) throw new IllegalStateError("state must be reset before processing new data");
|
|
if (Utils.is_string(data)) data = Utils.string_to_bytes(data);
|
|
if (Utils.is_buffer(data)) data = new Uint8Array(data);
|
|
if (!Utils.is_bytes(data)) throw new TypeError("data isn't of expected type");
|
|
var asm = this.asm, heap = this.heap, hpos = this.pos, hlen = this.len, dpos = 0, dlen = data.length, wlen = 0;
|
|
while (dlen > 0) {
|
|
wlen = Utils._heap_write(heap, hpos + hlen, data, dpos, dlen);
|
|
hlen += wlen;
|
|
dpos += wlen;
|
|
dlen -= wlen;
|
|
wlen = asm.process(hpos, hlen);
|
|
hpos += wlen;
|
|
hlen -= wlen;
|
|
if (!hlen) hpos = 0;
|
|
}
|
|
this.pos = hpos;
|
|
this.len = hlen;
|
|
return this;
|
|
}
|
|
function hash_finish() {
|
|
if (this.result !== null) throw new IllegalStateError("state must be reset before processing new data");
|
|
this.asm.finish(this.pos, this.len, 0);
|
|
this.result = new Uint8Array(this.HASH_SIZE);
|
|
this.result.set(this.heap.subarray(0, this.HASH_SIZE));
|
|
this.pos = 0;
|
|
this.len = 0;
|
|
return this;
|
|
}
|
|
module.exports.hash_reset = hash_reset;
|
|
module.exports.hash_process = hash_process;
|
|
module.exports.hash_finish = hash_finish;
|
|
},
|
|
"sha256/utils.js": function(module, exports, require) {
|
|
var string_to_bytes = module.exports.string_to_bytes = function(str, utf8) {
|
|
utf8 = !!utf8;
|
|
var len = str.length, bytes = new Uint8Array(utf8 ? 4 * len : len);
|
|
for (var i = 0, j = 0; i < len; i++) {
|
|
var c = str.charCodeAt(i);
|
|
if (utf8 && 55296 <= c && c <= 56319) {
|
|
if (++i >= len) throw new Error("Malformed string, low surrogate expected at position " + i);
|
|
c = (c ^ 55296) << 10 | 65536 | str.charCodeAt(i) ^ 56320;
|
|
} else if (!utf8 && c >>> 8) {
|
|
throw new Error("Wide characters are not allowed.");
|
|
}
|
|
if (!utf8 || c <= 127) {
|
|
bytes[j++] = c;
|
|
} else if (c <= 2047) {
|
|
bytes[j++] = 192 | c >> 6;
|
|
bytes[j++] = 128 | c & 63;
|
|
} else if (c <= 65535) {
|
|
bytes[j++] = 224 | c >> 12;
|
|
bytes[j++] = 128 | c >> 6 & 63;
|
|
bytes[j++] = 128 | c & 63;
|
|
} else {
|
|
bytes[j++] = 240 | c >> 18;
|
|
bytes[j++] = 128 | c >> 12 & 63;
|
|
bytes[j++] = 128 | c >> 6 & 63;
|
|
bytes[j++] = 128 | c & 63;
|
|
}
|
|
}
|
|
return bytes.subarray(0, j);
|
|
};
|
|
module.exports.hex_to_bytes = function(str) {
|
|
var len = str.length;
|
|
if (len & 1) {
|
|
str = "0" + str;
|
|
len++;
|
|
}
|
|
var bytes = new Uint8Array(len >> 1);
|
|
for (var i = 0; i < len; i += 2) {
|
|
bytes[i >> 1] = parseInt(str.substr(i, 2), 16);
|
|
}
|
|
return bytes;
|
|
};
|
|
module.exports.base64_to_bytes = function(str) {
|
|
return string_to_bytes(atob(str));
|
|
};
|
|
var bytes_to_string = module.exports.bytes_to_string = function(bytes, utf8) {
|
|
utf8 = !!utf8;
|
|
var len = bytes.length, chars = new Array(len);
|
|
for (var i = 0, j = 0; i < len; i++) {
|
|
var b = bytes[i];
|
|
if (!utf8 || b < 128) {
|
|
chars[j++] = b;
|
|
} else if (b >= 192 && b < 224 && i + 1 < len) {
|
|
chars[j++] = (b & 31) << 6 | bytes[++i] & 63;
|
|
} else if (b >= 224 && b < 240 && i + 2 < len) {
|
|
chars[j++] = (b & 15) << 12 | (bytes[++i] & 63) << 6 | bytes[++i] & 63;
|
|
} else if (b >= 240 && b < 248 && i + 3 < len) {
|
|
var c = (b & 7) << 18 | (bytes[++i] & 63) << 12 | (bytes[++i] & 63) << 6 | bytes[++i] & 63;
|
|
if (c <= 65535) {
|
|
chars[j++] = c;
|
|
} else {
|
|
c ^= 65536;
|
|
chars[j++] = 55296 | c >> 10;
|
|
chars[j++] = 56320 | c & 1023;
|
|
}
|
|
} else {
|
|
throw new Error("Malformed UTF8 character at byte offset " + i);
|
|
}
|
|
}
|
|
var str = "", bs = 16384;
|
|
for (var _i = 0; _i < j; _i += bs) {
|
|
str += String.fromCharCode.apply(String, chars.slice(_i, _i + bs <= j ? _i + bs : j));
|
|
}
|
|
return str;
|
|
};
|
|
module.exports.bytes_to_hex = function(arr) {
|
|
var str = "";
|
|
for (var i = 0; i < arr.length; i++) {
|
|
var h = (arr[i] & 255).toString(16);
|
|
if (h.length < 2) str += "0";
|
|
str += h;
|
|
}
|
|
return str;
|
|
};
|
|
module.exports.bytes_to_base64 = function(arr) {
|
|
return btoa(bytes_to_string(arr));
|
|
};
|
|
module.exports.pow2_ceil = function(a) {
|
|
a -= 1;
|
|
a |= a >>> 1;
|
|
a |= a >>> 2;
|
|
a |= a >>> 4;
|
|
a |= a >>> 8;
|
|
a |= a >>> 16;
|
|
a += 1;
|
|
return a;
|
|
};
|
|
module.exports.is_number = function(a) {
|
|
return typeof a === "number";
|
|
};
|
|
module.exports.is_string = function(a) {
|
|
return typeof a === "string";
|
|
};
|
|
module.exports.is_buffer = function(a) {
|
|
return a instanceof ArrayBuffer;
|
|
};
|
|
module.exports.is_bytes = function(a) {
|
|
return a instanceof Uint8Array;
|
|
};
|
|
module.exports.is_typed_array = function(a) {
|
|
return a instanceof Int8Array || a instanceof Uint8Array || a instanceof Int16Array || a instanceof Uint16Array || a instanceof Int32Array || a instanceof Uint32Array || a instanceof Float32Array || a instanceof Float64Array;
|
|
};
|
|
module.exports._heap_init = function(constructor, options) {
|
|
var heap = options.heap, size = heap ? heap.byteLength : options.heapSize || 65536;
|
|
if (size & 4095 || size <= 0) throw new Error("heap size must be a positive integer and a multiple of 4096");
|
|
heap = heap || new constructor(new ArrayBuffer(size));
|
|
return heap;
|
|
};
|
|
module.exports._heap_write = function(heap, hpos, data, dpos, dlen) {
|
|
var hlen = heap.length - hpos, wlen = hlen < dlen ? hlen : dlen;
|
|
heap.set(data.subarray(dpos, dpos + wlen), hpos);
|
|
return wlen;
|
|
};
|
|
},
|
|
"sha256/sha256.js": function(module, exports, require) {
|
|
var Utils = require("./utils.js");
|
|
var Hash = require("./hash.js");
|
|
var Asm = require("./sha256.asm.js");
|
|
var _sha256_block_size = 64, _sha256_hash_size = 32;
|
|
function sha256_constructor(options) {
|
|
options = options || {};
|
|
this.heap = Utils._heap_init(Uint8Array, options);
|
|
this.asm = options.asm || Asm.sha256_asm({
|
|
Uint8Array
|
|
}, null, this.heap.buffer);
|
|
this.BLOCK_SIZE = _sha256_block_size;
|
|
this.HASH_SIZE = _sha256_hash_size;
|
|
this.reset();
|
|
}
|
|
sha256_constructor.BLOCK_SIZE = _sha256_block_size;
|
|
sha256_constructor.HASH_SIZE = _sha256_hash_size;
|
|
var sha256_prototype = sha256_constructor.prototype;
|
|
sha256_prototype.reset = Hash.hash_reset;
|
|
sha256_prototype.process = Hash.hash_process;
|
|
sha256_prototype.finish = Hash.hash_finish;
|
|
var sha256_instance = null;
|
|
function get_sha256_instance() {
|
|
if (sha256_instance === null) sha256_instance = new sha256_constructor({
|
|
heapSize: 1048576
|
|
});
|
|
return sha256_instance;
|
|
}
|
|
module.exports.get_sha256_instance = get_sha256_instance;
|
|
module.exports.sha256_constructor = sha256_constructor;
|
|
},
|
|
"sha256/exports.js": function(module, exports, require) {
|
|
var Sha256 = require("./sha256.js");
|
|
var Utils = require("./utils.js");
|
|
function sha256_bytes(data) {
|
|
if (data === undefined) throw new SyntaxError("data required");
|
|
return Sha256.get_sha256_instance().reset().process(data).finish().result;
|
|
}
|
|
function sha256_hex(data) {
|
|
var result = sha256_bytes(data);
|
|
return Utils.bytes_to_hex(result);
|
|
}
|
|
function sha256_base64(data) {
|
|
var result = sha256_bytes(data);
|
|
return Utils.bytes_to_base64(result);
|
|
}
|
|
Sha256.sha256_constructor.bytes = sha256_bytes;
|
|
Sha256.sha256_constructor.hex = sha256_hex;
|
|
Sha256.sha256_constructor.base64 = sha256_base64;
|
|
module.exports = Sha256.sha256_constructor;
|
|
},
|
|
"sha256/sha256.asm.js": function(module, exports, require) {
|
|
module.exports.sha256_asm = function sha256_asm(stdlib, foreign, buffer) {
|
|
"use asm";
|
|
var H0 = 0, H1 = 0, H2 = 0, H3 = 0, H4 = 0, H5 = 0, H6 = 0, H7 = 0, TOTAL0 = 0, TOTAL1 = 0;
|
|
var I0 = 0, I1 = 0, I2 = 0, I3 = 0, I4 = 0, I5 = 0, I6 = 0, I7 = 0, O0 = 0, O1 = 0, O2 = 0, O3 = 0, O4 = 0, O5 = 0, O6 = 0, O7 = 0;
|
|
var HEAP = new stdlib.Uint8Array(buffer);
|
|
function _core(w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15) {
|
|
w0 = w0 | 0;
|
|
w1 = w1 | 0;
|
|
w2 = w2 | 0;
|
|
w3 = w3 | 0;
|
|
w4 = w4 | 0;
|
|
w5 = w5 | 0;
|
|
w6 = w6 | 0;
|
|
w7 = w7 | 0;
|
|
w8 = w8 | 0;
|
|
w9 = w9 | 0;
|
|
w10 = w10 | 0;
|
|
w11 = w11 | 0;
|
|
w12 = w12 | 0;
|
|
w13 = w13 | 0;
|
|
w14 = w14 | 0;
|
|
w15 = w15 | 0;
|
|
var a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0, t = 0;
|
|
a = H0;
|
|
b = H1;
|
|
c = H2;
|
|
d = H3;
|
|
e = H4;
|
|
f = H5;
|
|
g = H6;
|
|
h = H7;
|
|
t = w0 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x428a2f98 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w1 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x71374491 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w2 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xb5c0fbcf | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w3 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xe9b5dba5 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w4 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x3956c25b | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w5 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x59f111f1 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w6 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x923f82a4 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w7 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xab1c5ed5 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w8 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xd807aa98 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w9 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x12835b01 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w10 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x243185be | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w11 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x550c7dc3 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w12 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x72be5d74 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w13 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x80deb1fe | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w14 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x9bdc06a7 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
t = w15 + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xc19bf174 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w0 = t = (w1 >>> 7 ^ w1 >>> 18 ^ w1 >>> 3 ^ w1 << 25 ^ w1 << 14) + (w14 >>> 17 ^ w14 >>> 19 ^ w14 >>> 10 ^ w14 << 15 ^ w14 << 13) + w0 + w9 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xe49b69c1 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w1 = t = (w2 >>> 7 ^ w2 >>> 18 ^ w2 >>> 3 ^ w2 << 25 ^ w2 << 14) + (w15 >>> 17 ^ w15 >>> 19 ^ w15 >>> 10 ^ w15 << 15 ^ w15 << 13) + w1 + w10 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xefbe4786 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w2 = t = (w3 >>> 7 ^ w3 >>> 18 ^ w3 >>> 3 ^ w3 << 25 ^ w3 << 14) + (w0 >>> 17 ^ w0 >>> 19 ^ w0 >>> 10 ^ w0 << 15 ^ w0 << 13) + w2 + w11 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x0fc19dc6 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w3 = t = (w4 >>> 7 ^ w4 >>> 18 ^ w4 >>> 3 ^ w4 << 25 ^ w4 << 14) + (w1 >>> 17 ^ w1 >>> 19 ^ w1 >>> 10 ^ w1 << 15 ^ w1 << 13) + w3 + w12 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x240ca1cc | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w4 = t = (w5 >>> 7 ^ w5 >>> 18 ^ w5 >>> 3 ^ w5 << 25 ^ w5 << 14) + (w2 >>> 17 ^ w2 >>> 19 ^ w2 >>> 10 ^ w2 << 15 ^ w2 << 13) + w4 + w13 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x2de92c6f | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w5 = t = (w6 >>> 7 ^ w6 >>> 18 ^ w6 >>> 3 ^ w6 << 25 ^ w6 << 14) + (w3 >>> 17 ^ w3 >>> 19 ^ w3 >>> 10 ^ w3 << 15 ^ w3 << 13) + w5 + w14 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x4a7484aa | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w6 = t = (w7 >>> 7 ^ w7 >>> 18 ^ w7 >>> 3 ^ w7 << 25 ^ w7 << 14) + (w4 >>> 17 ^ w4 >>> 19 ^ w4 >>> 10 ^ w4 << 15 ^ w4 << 13) + w6 + w15 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x5cb0a9dc | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w7 = t = (w8 >>> 7 ^ w8 >>> 18 ^ w8 >>> 3 ^ w8 << 25 ^ w8 << 14) + (w5 >>> 17 ^ w5 >>> 19 ^ w5 >>> 10 ^ w5 << 15 ^ w5 << 13) + w7 + w0 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x76f988da | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w8 = t = (w9 >>> 7 ^ w9 >>> 18 ^ w9 >>> 3 ^ w9 << 25 ^ w9 << 14) + (w6 >>> 17 ^ w6 >>> 19 ^ w6 >>> 10 ^ w6 << 15 ^ w6 << 13) + w8 + w1 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x983e5152 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w9 = t = (w10 >>> 7 ^ w10 >>> 18 ^ w10 >>> 3 ^ w10 << 25 ^ w10 << 14) + (w7 >>> 17 ^ w7 >>> 19 ^ w7 >>> 10 ^ w7 << 15 ^ w7 << 13) + w9 + w2 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xa831c66d | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w10 = t = (w11 >>> 7 ^ w11 >>> 18 ^ w11 >>> 3 ^ w11 << 25 ^ w11 << 14) + (w8 >>> 17 ^ w8 >>> 19 ^ w8 >>> 10 ^ w8 << 15 ^ w8 << 13) + w10 + w3 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xb00327c8 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w11 = t = (w12 >>> 7 ^ w12 >>> 18 ^ w12 >>> 3 ^ w12 << 25 ^ w12 << 14) + (w9 >>> 17 ^ w9 >>> 19 ^ w9 >>> 10 ^ w9 << 15 ^ w9 << 13) + w11 + w4 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xbf597fc7 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w12 = t = (w13 >>> 7 ^ w13 >>> 18 ^ w13 >>> 3 ^ w13 << 25 ^ w13 << 14) + (w10 >>> 17 ^ w10 >>> 19 ^ w10 >>> 10 ^ w10 << 15 ^ w10 << 13) + w12 + w5 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xc6e00bf3 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w13 = t = (w14 >>> 7 ^ w14 >>> 18 ^ w14 >>> 3 ^ w14 << 25 ^ w14 << 14) + (w11 >>> 17 ^ w11 >>> 19 ^ w11 >>> 10 ^ w11 << 15 ^ w11 << 13) + w13 + w6 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xd5a79147 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w14 = t = (w15 >>> 7 ^ w15 >>> 18 ^ w15 >>> 3 ^ w15 << 25 ^ w15 << 14) + (w12 >>> 17 ^ w12 >>> 19 ^ w12 >>> 10 ^ w12 << 15 ^ w12 << 13) + w14 + w7 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x06ca6351 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w15 = t = (w0 >>> 7 ^ w0 >>> 18 ^ w0 >>> 3 ^ w0 << 25 ^ w0 << 14) + (w13 >>> 17 ^ w13 >>> 19 ^ w13 >>> 10 ^ w13 << 15 ^ w13 << 13) + w15 + w8 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x14292967 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w0 = t = (w1 >>> 7 ^ w1 >>> 18 ^ w1 >>> 3 ^ w1 << 25 ^ w1 << 14) + (w14 >>> 17 ^ w14 >>> 19 ^ w14 >>> 10 ^ w14 << 15 ^ w14 << 13) + w0 + w9 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x27b70a85 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w1 = t = (w2 >>> 7 ^ w2 >>> 18 ^ w2 >>> 3 ^ w2 << 25 ^ w2 << 14) + (w15 >>> 17 ^ w15 >>> 19 ^ w15 >>> 10 ^ w15 << 15 ^ w15 << 13) + w1 + w10 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x2e1b2138 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w2 = t = (w3 >>> 7 ^ w3 >>> 18 ^ w3 >>> 3 ^ w3 << 25 ^ w3 << 14) + (w0 >>> 17 ^ w0 >>> 19 ^ w0 >>> 10 ^ w0 << 15 ^ w0 << 13) + w2 + w11 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x4d2c6dfc | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w3 = t = (w4 >>> 7 ^ w4 >>> 18 ^ w4 >>> 3 ^ w4 << 25 ^ w4 << 14) + (w1 >>> 17 ^ w1 >>> 19 ^ w1 >>> 10 ^ w1 << 15 ^ w1 << 13) + w3 + w12 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x53380d13 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w4 = t = (w5 >>> 7 ^ w5 >>> 18 ^ w5 >>> 3 ^ w5 << 25 ^ w5 << 14) + (w2 >>> 17 ^ w2 >>> 19 ^ w2 >>> 10 ^ w2 << 15 ^ w2 << 13) + w4 + w13 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x650a7354 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w5 = t = (w6 >>> 7 ^ w6 >>> 18 ^ w6 >>> 3 ^ w6 << 25 ^ w6 << 14) + (w3 >>> 17 ^ w3 >>> 19 ^ w3 >>> 10 ^ w3 << 15 ^ w3 << 13) + w5 + w14 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x766a0abb | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w6 = t = (w7 >>> 7 ^ w7 >>> 18 ^ w7 >>> 3 ^ w7 << 25 ^ w7 << 14) + (w4 >>> 17 ^ w4 >>> 19 ^ w4 >>> 10 ^ w4 << 15 ^ w4 << 13) + w6 + w15 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x81c2c92e | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w7 = t = (w8 >>> 7 ^ w8 >>> 18 ^ w8 >>> 3 ^ w8 << 25 ^ w8 << 14) + (w5 >>> 17 ^ w5 >>> 19 ^ w5 >>> 10 ^ w5 << 15 ^ w5 << 13) + w7 + w0 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x92722c85 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w8 = t = (w9 >>> 7 ^ w9 >>> 18 ^ w9 >>> 3 ^ w9 << 25 ^ w9 << 14) + (w6 >>> 17 ^ w6 >>> 19 ^ w6 >>> 10 ^ w6 << 15 ^ w6 << 13) + w8 + w1 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xa2bfe8a1 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w9 = t = (w10 >>> 7 ^ w10 >>> 18 ^ w10 >>> 3 ^ w10 << 25 ^ w10 << 14) + (w7 >>> 17 ^ w7 >>> 19 ^ w7 >>> 10 ^ w7 << 15 ^ w7 << 13) + w9 + w2 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xa81a664b | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w10 = t = (w11 >>> 7 ^ w11 >>> 18 ^ w11 >>> 3 ^ w11 << 25 ^ w11 << 14) + (w8 >>> 17 ^ w8 >>> 19 ^ w8 >>> 10 ^ w8 << 15 ^ w8 << 13) + w10 + w3 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xc24b8b70 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w11 = t = (w12 >>> 7 ^ w12 >>> 18 ^ w12 >>> 3 ^ w12 << 25 ^ w12 << 14) + (w9 >>> 17 ^ w9 >>> 19 ^ w9 >>> 10 ^ w9 << 15 ^ w9 << 13) + w11 + w4 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xc76c51a3 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w12 = t = (w13 >>> 7 ^ w13 >>> 18 ^ w13 >>> 3 ^ w13 << 25 ^ w13 << 14) + (w10 >>> 17 ^ w10 >>> 19 ^ w10 >>> 10 ^ w10 << 15 ^ w10 << 13) + w12 + w5 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xd192e819 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w13 = t = (w14 >>> 7 ^ w14 >>> 18 ^ w14 >>> 3 ^ w14 << 25 ^ w14 << 14) + (w11 >>> 17 ^ w11 >>> 19 ^ w11 >>> 10 ^ w11 << 15 ^ w11 << 13) + w13 + w6 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xd6990624 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w14 = t = (w15 >>> 7 ^ w15 >>> 18 ^ w15 >>> 3 ^ w15 << 25 ^ w15 << 14) + (w12 >>> 17 ^ w12 >>> 19 ^ w12 >>> 10 ^ w12 << 15 ^ w12 << 13) + w14 + w7 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xf40e3585 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w15 = t = (w0 >>> 7 ^ w0 >>> 18 ^ w0 >>> 3 ^ w0 << 25 ^ w0 << 14) + (w13 >>> 17 ^ w13 >>> 19 ^ w13 >>> 10 ^ w13 << 15 ^ w13 << 13) + w15 + w8 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x106aa070 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w0 = t = (w1 >>> 7 ^ w1 >>> 18 ^ w1 >>> 3 ^ w1 << 25 ^ w1 << 14) + (w14 >>> 17 ^ w14 >>> 19 ^ w14 >>> 10 ^ w14 << 15 ^ w14 << 13) + w0 + w9 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x19a4c116 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w1 = t = (w2 >>> 7 ^ w2 >>> 18 ^ w2 >>> 3 ^ w2 << 25 ^ w2 << 14) + (w15 >>> 17 ^ w15 >>> 19 ^ w15 >>> 10 ^ w15 << 15 ^ w15 << 13) + w1 + w10 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x1e376c08 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w2 = t = (w3 >>> 7 ^ w3 >>> 18 ^ w3 >>> 3 ^ w3 << 25 ^ w3 << 14) + (w0 >>> 17 ^ w0 >>> 19 ^ w0 >>> 10 ^ w0 << 15 ^ w0 << 13) + w2 + w11 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x2748774c | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w3 = t = (w4 >>> 7 ^ w4 >>> 18 ^ w4 >>> 3 ^ w4 << 25 ^ w4 << 14) + (w1 >>> 17 ^ w1 >>> 19 ^ w1 >>> 10 ^ w1 << 15 ^ w1 << 13) + w3 + w12 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x34b0bcb5 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w4 = t = (w5 >>> 7 ^ w5 >>> 18 ^ w5 >>> 3 ^ w5 << 25 ^ w5 << 14) + (w2 >>> 17 ^ w2 >>> 19 ^ w2 >>> 10 ^ w2 << 15 ^ w2 << 13) + w4 + w13 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x391c0cb3 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w5 = t = (w6 >>> 7 ^ w6 >>> 18 ^ w6 >>> 3 ^ w6 << 25 ^ w6 << 14) + (w3 >>> 17 ^ w3 >>> 19 ^ w3 >>> 10 ^ w3 << 15 ^ w3 << 13) + w5 + w14 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x4ed8aa4a | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w6 = t = (w7 >>> 7 ^ w7 >>> 18 ^ w7 >>> 3 ^ w7 << 25 ^ w7 << 14) + (w4 >>> 17 ^ w4 >>> 19 ^ w4 >>> 10 ^ w4 << 15 ^ w4 << 13) + w6 + w15 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x5b9cca4f | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w7 = t = (w8 >>> 7 ^ w8 >>> 18 ^ w8 >>> 3 ^ w8 << 25 ^ w8 << 14) + (w5 >>> 17 ^ w5 >>> 19 ^ w5 >>> 10 ^ w5 << 15 ^ w5 << 13) + w7 + w0 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x682e6ff3 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w8 = t = (w9 >>> 7 ^ w9 >>> 18 ^ w9 >>> 3 ^ w9 << 25 ^ w9 << 14) + (w6 >>> 17 ^ w6 >>> 19 ^ w6 >>> 10 ^ w6 << 15 ^ w6 << 13) + w8 + w1 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x748f82ee | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w9 = t = (w10 >>> 7 ^ w10 >>> 18 ^ w10 >>> 3 ^ w10 << 25 ^ w10 << 14) + (w7 >>> 17 ^ w7 >>> 19 ^ w7 >>> 10 ^ w7 << 15 ^ w7 << 13) + w9 + w2 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x78a5636f | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w10 = t = (w11 >>> 7 ^ w11 >>> 18 ^ w11 >>> 3 ^ w11 << 25 ^ w11 << 14) + (w8 >>> 17 ^ w8 >>> 19 ^ w8 >>> 10 ^ w8 << 15 ^ w8 << 13) + w10 + w3 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x84c87814 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w11 = t = (w12 >>> 7 ^ w12 >>> 18 ^ w12 >>> 3 ^ w12 << 25 ^ w12 << 14) + (w9 >>> 17 ^ w9 >>> 19 ^ w9 >>> 10 ^ w9 << 15 ^ w9 << 13) + w11 + w4 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x8cc70208 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w12 = t = (w13 >>> 7 ^ w13 >>> 18 ^ w13 >>> 3 ^ w13 << 25 ^ w13 << 14) + (w10 >>> 17 ^ w10 >>> 19 ^ w10 >>> 10 ^ w10 << 15 ^ w10 << 13) + w12 + w5 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0x90befffa | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w13 = t = (w14 >>> 7 ^ w14 >>> 18 ^ w14 >>> 3 ^ w14 << 25 ^ w14 << 14) + (w11 >>> 17 ^ w11 >>> 19 ^ w11 >>> 10 ^ w11 << 15 ^ w11 << 13) + w13 + w6 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xa4506ceb | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w14 = t = (w15 >>> 7 ^ w15 >>> 18 ^ w15 >>> 3 ^ w15 << 25 ^ w15 << 14) + (w12 >>> 17 ^ w12 >>> 19 ^ w12 >>> 10 ^ w12 << 15 ^ w12 << 13) + w14 + w7 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xbef9a3f7 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
w15 = t = (w0 >>> 7 ^ w0 >>> 18 ^ w0 >>> 3 ^ w0 << 25 ^ w0 << 14) + (w13 >>> 17 ^ w13 >>> 19 ^ w13 >>> 10 ^ w13 << 15 ^ w13 << 13) + w15 + w8 | 0;
|
|
t = t + h + (e >>> 6 ^ e >>> 11 ^ e >>> 25 ^ e << 26 ^ e << 21 ^ e << 7) + (g ^ e & (f ^ g)) + 0xc67178f2 | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t + (b & c ^ d & (b ^ c)) + (b >>> 2 ^ b >>> 13 ^ b >>> 22 ^ b << 30 ^ b << 19 ^ b << 10) | 0;
|
|
H0 = H0 + a | 0;
|
|
H1 = H1 + b | 0;
|
|
H2 = H2 + c | 0;
|
|
H3 = H3 + d | 0;
|
|
H4 = H4 + e | 0;
|
|
H5 = H5 + f | 0;
|
|
H6 = H6 + g | 0;
|
|
H7 = H7 + h | 0;
|
|
}
|
|
function _core_heap(offset) {
|
|
offset = offset | 0;
|
|
_core(HEAP[offset | 0] << 24 | HEAP[offset | 1] << 16 | HEAP[offset | 2] << 8 | HEAP[offset | 3], HEAP[offset | 4] << 24 | HEAP[offset | 5] << 16 | HEAP[offset | 6] << 8 | HEAP[offset | 7], HEAP[offset | 8] << 24 | HEAP[offset | 9] << 16 | HEAP[offset | 10] << 8 | HEAP[offset | 11], HEAP[offset | 12] << 24 | HEAP[offset | 13] << 16 | HEAP[offset | 14] << 8 | HEAP[offset | 15], HEAP[offset | 16] << 24 | HEAP[offset | 17] << 16 | HEAP[offset | 18] << 8 | HEAP[offset | 19], HEAP[offset | 20] << 24 | HEAP[offset | 21] << 16 | HEAP[offset | 22] << 8 | HEAP[offset | 23], HEAP[offset | 24] << 24 | HEAP[offset | 25] << 16 | HEAP[offset | 26] << 8 | HEAP[offset | 27], HEAP[offset | 28] << 24 | HEAP[offset | 29] << 16 | HEAP[offset | 30] << 8 | HEAP[offset | 31], HEAP[offset | 32] << 24 | HEAP[offset | 33] << 16 | HEAP[offset | 34] << 8 | HEAP[offset | 35], HEAP[offset | 36] << 24 | HEAP[offset | 37] << 16 | HEAP[offset | 38] << 8 | HEAP[offset | 39], HEAP[offset | 40] << 24 | HEAP[offset | 41] << 16 | HEAP[offset | 42] << 8 | HEAP[offset | 43], HEAP[offset | 44] << 24 | HEAP[offset | 45] << 16 | HEAP[offset | 46] << 8 | HEAP[offset | 47], HEAP[offset | 48] << 24 | HEAP[offset | 49] << 16 | HEAP[offset | 50] << 8 | HEAP[offset | 51], HEAP[offset | 52] << 24 | HEAP[offset | 53] << 16 | HEAP[offset | 54] << 8 | HEAP[offset | 55], HEAP[offset | 56] << 24 | HEAP[offset | 57] << 16 | HEAP[offset | 58] << 8 | HEAP[offset | 59], HEAP[offset | 60] << 24 | HEAP[offset | 61] << 16 | HEAP[offset | 62] << 8 | HEAP[offset | 63]);
|
|
}
|
|
function _state_to_heap(output) {
|
|
output = output | 0;
|
|
HEAP[output | 0] = H0 >>> 24;
|
|
HEAP[output | 1] = H0 >>> 16 & 255;
|
|
HEAP[output | 2] = H0 >>> 8 & 255;
|
|
HEAP[output | 3] = H0 & 255;
|
|
HEAP[output | 4] = H1 >>> 24;
|
|
HEAP[output | 5] = H1 >>> 16 & 255;
|
|
HEAP[output | 6] = H1 >>> 8 & 255;
|
|
HEAP[output | 7] = H1 & 255;
|
|
HEAP[output | 8] = H2 >>> 24;
|
|
HEAP[output | 9] = H2 >>> 16 & 255;
|
|
HEAP[output | 10] = H2 >>> 8 & 255;
|
|
HEAP[output | 11] = H2 & 255;
|
|
HEAP[output | 12] = H3 >>> 24;
|
|
HEAP[output | 13] = H3 >>> 16 & 255;
|
|
HEAP[output | 14] = H3 >>> 8 & 255;
|
|
HEAP[output | 15] = H3 & 255;
|
|
HEAP[output | 16] = H4 >>> 24;
|
|
HEAP[output | 17] = H4 >>> 16 & 255;
|
|
HEAP[output | 18] = H4 >>> 8 & 255;
|
|
HEAP[output | 19] = H4 & 255;
|
|
HEAP[output | 20] = H5 >>> 24;
|
|
HEAP[output | 21] = H5 >>> 16 & 255;
|
|
HEAP[output | 22] = H5 >>> 8 & 255;
|
|
HEAP[output | 23] = H5 & 255;
|
|
HEAP[output | 24] = H6 >>> 24;
|
|
HEAP[output | 25] = H6 >>> 16 & 255;
|
|
HEAP[output | 26] = H6 >>> 8 & 255;
|
|
HEAP[output | 27] = H6 & 255;
|
|
HEAP[output | 28] = H7 >>> 24;
|
|
HEAP[output | 29] = H7 >>> 16 & 255;
|
|
HEAP[output | 30] = H7 >>> 8 & 255;
|
|
HEAP[output | 31] = H7 & 255;
|
|
}
|
|
function reset() {
|
|
H0 = 0x6a09e667;
|
|
H1 = 0xbb67ae85;
|
|
H2 = 0x3c6ef372;
|
|
H3 = 0xa54ff53a;
|
|
H4 = 0x510e527f;
|
|
H5 = 0x9b05688c;
|
|
H6 = 0x1f83d9ab;
|
|
H7 = 0x5be0cd19;
|
|
TOTAL0 = TOTAL1 = 0;
|
|
}
|
|
function init(h0, h1, h2, h3, h4, h5, h6, h7, total0, total1) {
|
|
h0 = h0 | 0;
|
|
h1 = h1 | 0;
|
|
h2 = h2 | 0;
|
|
h3 = h3 | 0;
|
|
h4 = h4 | 0;
|
|
h5 = h5 | 0;
|
|
h6 = h6 | 0;
|
|
h7 = h7 | 0;
|
|
total0 = total0 | 0;
|
|
total1 = total1 | 0;
|
|
H0 = h0;
|
|
H1 = h1;
|
|
H2 = h2;
|
|
H3 = h3;
|
|
H4 = h4;
|
|
H5 = h5;
|
|
H6 = h6;
|
|
H7 = h7;
|
|
TOTAL0 = total0;
|
|
TOTAL1 = total1;
|
|
}
|
|
function process(offset, length) {
|
|
offset = offset | 0;
|
|
length = length | 0;
|
|
var hashed = 0;
|
|
if (offset & 63) return -1;
|
|
while ((length | 0) >= 64) {
|
|
_core_heap(offset);
|
|
offset = offset + 64 | 0;
|
|
length = length - 64 | 0;
|
|
hashed = hashed + 64 | 0;
|
|
}
|
|
TOTAL0 = TOTAL0 + hashed | 0;
|
|
if (TOTAL0 >>> 0 < hashed >>> 0) TOTAL1 = TOTAL1 + 1 | 0;
|
|
return hashed | 0;
|
|
}
|
|
function finish(offset, length, output) {
|
|
offset = offset | 0;
|
|
length = length | 0;
|
|
output = output | 0;
|
|
var hashed = 0, i = 0;
|
|
if (offset & 63) return -1;
|
|
if (~output) if (output & 31) return -1;
|
|
if ((length | 0) >= 64) {
|
|
hashed = process(offset, length) | 0;
|
|
if ((hashed | 0) == -1) return -1;
|
|
offset = offset + hashed | 0;
|
|
length = length - hashed | 0;
|
|
}
|
|
hashed = hashed + length | 0;
|
|
TOTAL0 = TOTAL0 + length | 0;
|
|
if (TOTAL0 >>> 0 < length >>> 0) TOTAL1 = TOTAL1 + 1 | 0;
|
|
HEAP[offset | length] = 0x80;
|
|
if ((length | 0) >= 56) {
|
|
for (i = length + 1 | 0; (i | 0) < 64; i = i + 1 | 0) HEAP[offset | i] = 0x00;
|
|
_core_heap(offset);
|
|
length = 0;
|
|
HEAP[offset | 0] = 0;
|
|
}
|
|
for (i = length + 1 | 0; (i | 0) < 59; i = i + 1 | 0) HEAP[offset | i] = 0;
|
|
HEAP[offset | 56] = TOTAL1 >>> 21 & 255;
|
|
HEAP[offset | 57] = TOTAL1 >>> 13 & 255;
|
|
HEAP[offset | 58] = TOTAL1 >>> 5 & 255;
|
|
HEAP[offset | 59] = TOTAL1 << 3 & 255 | TOTAL0 >>> 29;
|
|
HEAP[offset | 60] = TOTAL0 >>> 21 & 255;
|
|
HEAP[offset | 61] = TOTAL0 >>> 13 & 255;
|
|
HEAP[offset | 62] = TOTAL0 >>> 5 & 255;
|
|
HEAP[offset | 63] = TOTAL0 << 3 & 255;
|
|
_core_heap(offset);
|
|
if (~output) _state_to_heap(output);
|
|
return hashed | 0;
|
|
}
|
|
function hmac_reset() {
|
|
H0 = I0;
|
|
H1 = I1;
|
|
H2 = I2;
|
|
H3 = I3;
|
|
H4 = I4;
|
|
H5 = I5;
|
|
H6 = I6;
|
|
H7 = I7;
|
|
TOTAL0 = 64;
|
|
TOTAL1 = 0;
|
|
}
|
|
function _hmac_opad() {
|
|
H0 = O0;
|
|
H1 = O1;
|
|
H2 = O2;
|
|
H3 = O3;
|
|
H4 = O4;
|
|
H5 = O5;
|
|
H6 = O6;
|
|
H7 = O7;
|
|
TOTAL0 = 64;
|
|
TOTAL1 = 0;
|
|
}
|
|
function hmac_init(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15) {
|
|
p0 = p0 | 0;
|
|
p1 = p1 | 0;
|
|
p2 = p2 | 0;
|
|
p3 = p3 | 0;
|
|
p4 = p4 | 0;
|
|
p5 = p5 | 0;
|
|
p6 = p6 | 0;
|
|
p7 = p7 | 0;
|
|
p8 = p8 | 0;
|
|
p9 = p9 | 0;
|
|
p10 = p10 | 0;
|
|
p11 = p11 | 0;
|
|
p12 = p12 | 0;
|
|
p13 = p13 | 0;
|
|
p14 = p14 | 0;
|
|
p15 = p15 | 0;
|
|
reset();
|
|
_core(p0 ^ 0x5c5c5c5c, p1 ^ 0x5c5c5c5c, p2 ^ 0x5c5c5c5c, p3 ^ 0x5c5c5c5c, p4 ^ 0x5c5c5c5c, p5 ^ 0x5c5c5c5c, p6 ^ 0x5c5c5c5c, p7 ^ 0x5c5c5c5c, p8 ^ 0x5c5c5c5c, p9 ^ 0x5c5c5c5c, p10 ^ 0x5c5c5c5c, p11 ^ 0x5c5c5c5c, p12 ^ 0x5c5c5c5c, p13 ^ 0x5c5c5c5c, p14 ^ 0x5c5c5c5c, p15 ^ 0x5c5c5c5c);
|
|
O0 = H0;
|
|
O1 = H1;
|
|
O2 = H2;
|
|
O3 = H3;
|
|
O4 = H4;
|
|
O5 = H5;
|
|
O6 = H6;
|
|
O7 = H7;
|
|
reset();
|
|
_core(p0 ^ 0x36363636, p1 ^ 0x36363636, p2 ^ 0x36363636, p3 ^ 0x36363636, p4 ^ 0x36363636, p5 ^ 0x36363636, p6 ^ 0x36363636, p7 ^ 0x36363636, p8 ^ 0x36363636, p9 ^ 0x36363636, p10 ^ 0x36363636, p11 ^ 0x36363636, p12 ^ 0x36363636, p13 ^ 0x36363636, p14 ^ 0x36363636, p15 ^ 0x36363636);
|
|
I0 = H0;
|
|
I1 = H1;
|
|
I2 = H2;
|
|
I3 = H3;
|
|
I4 = H4;
|
|
I5 = H5;
|
|
I6 = H6;
|
|
I7 = H7;
|
|
TOTAL0 = 64;
|
|
TOTAL1 = 0;
|
|
}
|
|
function hmac_finish(offset, length, output) {
|
|
offset = offset | 0;
|
|
length = length | 0;
|
|
output = output | 0;
|
|
var t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, hashed = 0;
|
|
if (offset & 63) return -1;
|
|
if (~output) if (output & 31) return -1;
|
|
hashed = finish(offset, length, -1) | 0;
|
|
t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
|
|
_hmac_opad();
|
|
_core(t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768);
|
|
if (~output) _state_to_heap(output);
|
|
return hashed | 0;
|
|
}
|
|
function pbkdf2_generate_block(offset, length, block, count, output) {
|
|
offset = offset | 0;
|
|
length = length | 0;
|
|
block = block | 0;
|
|
count = count | 0;
|
|
output = output | 0;
|
|
var h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0, h7 = 0, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0;
|
|
if (offset & 63) return -1;
|
|
if (~output) if (output & 31) return -1;
|
|
HEAP[offset + length | 0] = block >>> 24;
|
|
HEAP[offset + length + 1 | 0] = block >>> 16 & 255;
|
|
HEAP[offset + length + 2 | 0] = block >>> 8 & 255;
|
|
HEAP[offset + length + 3 | 0] = block & 255;
|
|
hmac_finish(offset, length + 4 | 0, -1) | 0;
|
|
h0 = t0 = H0, h1 = t1 = H1, h2 = t2 = H2, h3 = t3 = H3, h4 = t4 = H4, h5 = t5 = H5,
|
|
h6 = t6 = H6, h7 = t7 = H7;
|
|
count = count - 1 | 0;
|
|
while ((count | 0) > 0) {
|
|
hmac_reset();
|
|
_core(t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768);
|
|
t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
|
|
_hmac_opad();
|
|
_core(t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768);
|
|
t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
|
|
h0 = h0 ^ H0;
|
|
h1 = h1 ^ H1;
|
|
h2 = h2 ^ H2;
|
|
h3 = h3 ^ H3;
|
|
h4 = h4 ^ H4;
|
|
h5 = h5 ^ H5;
|
|
h6 = h6 ^ H6;
|
|
h7 = h7 ^ H7;
|
|
count = count - 1 | 0;
|
|
}
|
|
H0 = h0;
|
|
H1 = h1;
|
|
H2 = h2;
|
|
H3 = h3;
|
|
H4 = h4;
|
|
H5 = h5;
|
|
H6 = h6;
|
|
H7 = h7;
|
|
if (~output) _state_to_heap(output);
|
|
return 0;
|
|
}
|
|
return {
|
|
reset,
|
|
init,
|
|
process,
|
|
finish,
|
|
hmac_reset,
|
|
hmac_init,
|
|
hmac_finish,
|
|
pbkdf2_generate_block
|
|
};
|
|
};
|
|
},
|
|
"transform/TextTransformer.js": function(module, exports, require) {
|
|
var Operation = require("../Operation");
|
|
var Common = require("../Common");
|
|
var transformOp0 = function(toTransform, transformBy) {
|
|
if (toTransform.offset > transformBy.offset) {
|
|
if (toTransform.offset > transformBy.offset + transformBy.toRemove) {
|
|
return Operation.create(toTransform.offset - transformBy.toRemove + transformBy.toInsert.length, toTransform.toRemove, toTransform.toInsert);
|
|
}
|
|
var newToRemove = toTransform.toRemove - (transformBy.offset + transformBy.toRemove - toTransform.offset);
|
|
if (newToRemove < 0) {
|
|
newToRemove = 0;
|
|
}
|
|
if (newToRemove === 0 && toTransform.toInsert.length === 0) {
|
|
return null;
|
|
}
|
|
return Operation.create(transformBy.offset + transformBy.toInsert.length, newToRemove, toTransform.toInsert);
|
|
}
|
|
if (toTransform.offset + toTransform.toRemove < transformBy.offset) {
|
|
return toTransform;
|
|
}
|
|
var _newToRemove = transformBy.offset - toTransform.offset;
|
|
if (_newToRemove === 0 && toTransform.toInsert.length === 0) {
|
|
return null;
|
|
}
|
|
return Operation.create(toTransform.offset, _newToRemove, toTransform.toInsert);
|
|
};
|
|
var transformOp = function(toTransform, transformBy) {
|
|
if (Common.PARANOIA) {
|
|
Operation.check(toTransform);
|
|
Operation.check(transformBy);
|
|
}
|
|
var result = transformOp0(toTransform, transformBy);
|
|
if (Common.PARANOIA && result) {
|
|
Operation.check(result);
|
|
}
|
|
return result;
|
|
};
|
|
module.exports = function(opsToTransform, opsTransformBy, doc) {
|
|
var resultOfTransformBy = doc;
|
|
var i;
|
|
for (i = opsTransformBy.length - 1; i >= 0; i--) {
|
|
resultOfTransformBy = Operation.apply(opsTransformBy[i], resultOfTransformBy);
|
|
}
|
|
var out = [];
|
|
for (i = opsToTransform.length - 1; i >= 0; i--) {
|
|
var tti = opsToTransform[i];
|
|
for (var j = opsTransformBy.length - 1; j >= 0; j--) {
|
|
try {
|
|
tti = transformOp(tti, opsTransformBy[j]);
|
|
} catch (e) {
|
|
console.error("The pluggable transform function threw an error, " + "failing operational transformation");
|
|
console.error(e.stack);
|
|
return [];
|
|
}
|
|
if (!tti) {
|
|
break;
|
|
}
|
|
}
|
|
if (tti) {
|
|
if (Common.PARANOIA) {
|
|
Operation.check(tti, resultOfTransformBy.length);
|
|
}
|
|
out.unshift(tti);
|
|
}
|
|
}
|
|
return out;
|
|
};
|
|
},
|
|
"transform/NaiveJSONTransformer.js": function(module, exports, require) {
|
|
var TextTransformer = require("./TextTransformer");
|
|
var Operation = require("../Operation");
|
|
var Common = require("../Common");
|
|
module.exports = function(opsToTransform, opsTransformBy, text) {
|
|
var DEBUG = Common.global.REALTIME_DEBUG = Common.global.REALTIME_DEBUG || {};
|
|
var resultOps, text2, text3;
|
|
try {
|
|
resultOps = TextTransformer(opsToTransform, opsTransformBy, text);
|
|
text2 = Operation.applyMulti(opsTransformBy, text);
|
|
text3 = Operation.applyMulti(resultOps, text2);
|
|
try {
|
|
JSON.parse(text3);
|
|
return resultOps;
|
|
} catch (e) {
|
|
console.error(e);
|
|
DEBUG.ot_parseError = {
|
|
type: "resultParseError",
|
|
resultOps,
|
|
toTransform: opsToTransform,
|
|
transformBy: opsTransformBy,
|
|
text1: text,
|
|
text2,
|
|
text3,
|
|
error: e
|
|
};
|
|
console.log("Debugging info available at `window.REALTIME_DEBUG.ot_parseError`");
|
|
}
|
|
} catch (x) {
|
|
console.error(x);
|
|
DEBUG.ot_applyError = {
|
|
type: "resultParseError",
|
|
resultOps,
|
|
toTransform: opsToTransform,
|
|
transformBy: opsTransformBy,
|
|
text1: text,
|
|
text2,
|
|
text3,
|
|
error: x
|
|
};
|
|
console.log("Debugging info available at `window.REALTIME_DEBUG.ot_applyError`");
|
|
}
|
|
return [];
|
|
};
|
|
},
|
|
"transform/SmartJSONTransformer.js": function(module, exports, require) {
|
|
var Sortify = require("json.sortify");
|
|
var Diff = require("../Diff");
|
|
var Operation = require("../Operation");
|
|
var TextTransformer = require("./TextTransformer");
|
|
var isArray = function(obj) {
|
|
return Object.prototype.toString.call(obj) === "[object Array]";
|
|
};
|
|
var type = function(dat) {
|
|
return dat === null ? "null" : isArray(dat) ? "array" : typeof dat;
|
|
};
|
|
var find = function(map, path) {
|
|
var l = path.length;
|
|
for (var i = 0; i < l; i++) {
|
|
if (typeof map[path[i]] === "undefined") {
|
|
return;
|
|
}
|
|
map = map[path[i]];
|
|
}
|
|
return map;
|
|
};
|
|
var clone = function(val) {
|
|
return JSON.parse(JSON.stringify(val));
|
|
};
|
|
var deepEqual = function(A, B) {
|
|
var t_A = type(A);
|
|
var t_B = type(B);
|
|
if (t_A !== t_B) {
|
|
return false;
|
|
}
|
|
if (t_A === "object") {
|
|
var k_A = Object.keys(A);
|
|
var k_B = Object.keys(B);
|
|
return k_A.length === k_B.length && !k_A.some((function(a) {
|
|
return !deepEqual(A[a], B[a]);
|
|
})) && !k_B.some((function(b) {
|
|
return !(b in A);
|
|
}));
|
|
} else if (t_A === "array") {
|
|
return A.length === B.length && !A.some((function(a, i) {
|
|
return !deepEqual(a, B[i]);
|
|
}));
|
|
} else {
|
|
return A === B;
|
|
}
|
|
};
|
|
var operation = function(type, path, value, prev, other) {
|
|
if (type === "replace") {
|
|
return {
|
|
type: "replace",
|
|
path,
|
|
value,
|
|
prev
|
|
};
|
|
} else if (type === "splice") {
|
|
if (typeof prev !== "number") {
|
|
throw new Error;
|
|
}
|
|
if (typeof other !== "number") {
|
|
throw new Error;
|
|
}
|
|
return {
|
|
type: "splice",
|
|
path,
|
|
value,
|
|
offset: prev,
|
|
removals: other
|
|
};
|
|
} else if (type !== "remove") {
|
|
throw new Error("expected a removal");
|
|
}
|
|
return {
|
|
type: "remove",
|
|
path,
|
|
value
|
|
};
|
|
};
|
|
var replace = function(ops, path, to, from) {
|
|
ops.push(operation("replace", path, to, from));
|
|
};
|
|
var remove = function(ops, path, val) {
|
|
ops.push(operation("remove", path, val));
|
|
};
|
|
var splice = function(ops, path, value, offset, removals) {
|
|
ops.push(operation("splice", path, value, offset, removals));
|
|
};
|
|
var pathOverlaps = function(A, B) {
|
|
return !A.some((function(a, i) {
|
|
return a !== B[i];
|
|
}));
|
|
};
|
|
var CASES = function() {
|
|
var types = [ "replace", "remove", "splice" ];
|
|
var matrix = {};
|
|
var i = 1;
|
|
types.forEach((function(a) {
|
|
matrix[a] = {};
|
|
return types.forEach((function(b) {
|
|
matrix[a][b] = i++;
|
|
}));
|
|
}));
|
|
return matrix;
|
|
}();
|
|
var resolve = function(A, B, arbiter) {
|
|
if (!(type(A) === "array" && type(B) === "array")) {
|
|
throw new Error("[resolve] expected two arrays");
|
|
}
|
|
B = B.filter((function(b) {
|
|
if (A.some((function(a) {
|
|
if (a.type === "remove") {
|
|
if (pathOverlaps(a.path, b.path)) {
|
|
if (b.path.length - a.path.length > 1) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}))) {
|
|
return false;
|
|
}
|
|
if (b.type === "splice" && A.some((function(a) {
|
|
if (a.type === "splice" && pathOverlaps(a.path, b.path)) {
|
|
if (a.path.length - b.path.length < 0) {
|
|
if (!a.removals) {
|
|
return;
|
|
}
|
|
var start = a.offset;
|
|
var end = a.offset + a.removals;
|
|
for (;start < end; start++) {
|
|
if (start === b.path[a.path.length]) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}))) {
|
|
return false;
|
|
}
|
|
if (!A.some((function(a) {
|
|
return b.type === "remove" && deepEqual(a.path, b.path);
|
|
}))) {
|
|
return true;
|
|
}
|
|
})).filter((function(b) {
|
|
return !A.some((function(a) {
|
|
if (b.type === "replace" && a.type === "replace") {
|
|
if (deepEqual(a.path, b.path)) {
|
|
if (typeof a.value === "string" && typeof b.value === "string") {
|
|
if (arbiter && a.prev === b.prev && a.value !== b.value) {
|
|
return arbiter(a, b, CASES.replace.replace);
|
|
}
|
|
return true;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}));
|
|
})).map((function(b) {
|
|
A.forEach((function(a) {
|
|
if (a.type === "splice") {
|
|
if (deepEqual(a.path, b.path)) {
|
|
if (b.type === "splice") {
|
|
if (b.offset > a.offset + b.removals) {
|
|
b.offset += a.value.length - a.removals;
|
|
return;
|
|
}
|
|
if (b.offset < a.offset) {
|
|
b.removals = Math.max(a.offset - b.offset, 0);
|
|
return;
|
|
}
|
|
b.removals = Math.max(b.removals - (b.offset + a.removals - b.offset), 0);
|
|
b.offset += a.value.length - a.removals;
|
|
}
|
|
return;
|
|
}
|
|
if (pathOverlaps(a.path, b.path)) {
|
|
var pos = a.path.length;
|
|
if (typeof b.path[pos] === "number" && a.offset <= b.path[pos]) {
|
|
b.path[pos] += a.value.length - a.removals;
|
|
}
|
|
}
|
|
}
|
|
}));
|
|
return b;
|
|
}));
|
|
return B;
|
|
};
|
|
var objects = function(A, B, path, ops) {
|
|
var Akeys = Object.keys(A);
|
|
var Bkeys = Object.keys(B);
|
|
Bkeys.forEach((function(b) {
|
|
var t_b = type(B[b]);
|
|
var old = A[b];
|
|
var nextPath = path.concat(b);
|
|
if (Akeys.indexOf(b) === -1) {
|
|
if (t_b === "undefined") {
|
|
throw new Error("undefined type has key. this shouldn't happen?");
|
|
}
|
|
if (old) {
|
|
throw new Error("no such key existed in b, so 'old' should be falsey");
|
|
}
|
|
replace(ops, nextPath, B[b], old);
|
|
return;
|
|
}
|
|
var t_a = type(old);
|
|
if (t_a !== t_b) {
|
|
console.log("type changed from [%s] to [%s]", t_a, t_b);
|
|
if (t_b === "undefined") {
|
|
throw new Error("first pass should never reveal undefined keys");
|
|
}
|
|
replace(ops, nextPath, B[b], old);
|
|
return;
|
|
}
|
|
if (t_a === "object") {
|
|
objects(A[b], B[b], nextPath, ops);
|
|
} else if (t_a === "array") {
|
|
arrays(A[b], B[b], nextPath, ops);
|
|
} else if (A[b] !== B[b]) {
|
|
replace(ops, nextPath, B[b], old);
|
|
}
|
|
}));
|
|
Akeys.forEach((function(a) {
|
|
if (Bkeys.indexOf(a) === -1 || type(B[a]) === "undefined") {
|
|
remove(ops, path.concat(a), A[a]);
|
|
}
|
|
}));
|
|
};
|
|
var arrayShallowEquality = function(A, B) {
|
|
if (A.length !== B.length) {
|
|
return false;
|
|
}
|
|
for (var i = 0; i < A.length; i++) {
|
|
if (type(A[i]) !== type(B[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var arrays = function(A_orig, B, path, ops) {
|
|
var A = A_orig.slice(0);
|
|
if (A.length === 0) {
|
|
splice(ops, path, B, 0, 0);
|
|
} else if (arrayShallowEquality(A, B)) {
|
|
A.forEach((function(a, i) {
|
|
var b = B[i];
|
|
if (b === a) {
|
|
return;
|
|
}
|
|
var old = a;
|
|
var nextPath = path.concat(i);
|
|
var t_a = type(a);
|
|
switch (t_a) {
|
|
case "undefined":
|
|
throw new Error("existing key had type `undefined`. this should never happen");
|
|
|
|
case "object":
|
|
objects(a, b, nextPath, ops);
|
|
break;
|
|
|
|
case "array":
|
|
arrays(a, b, nextPath, ops);
|
|
break;
|
|
|
|
default:
|
|
replace(ops, nextPath, b, old);
|
|
}
|
|
}));
|
|
} else {
|
|
var commonStart = 0;
|
|
var commonEnd = 0;
|
|
while (commonStart < A.length && deepEqual(A[commonStart], B[commonStart])) {
|
|
commonStart++;
|
|
}
|
|
while (deepEqual(A[A.length - 1 - commonEnd], B[B.length - 1 - commonEnd]) && commonEnd + commonStart < A.length && commonEnd + commonStart < B.length) {
|
|
commonEnd++;
|
|
}
|
|
var toRemove = A.length - commonStart - commonEnd;
|
|
var toInsert = [];
|
|
if (B.length !== commonStart + commonEnd) {
|
|
toInsert = B.slice(commonStart, B.length - commonEnd);
|
|
}
|
|
splice(ops, path, toInsert, commonStart, toRemove);
|
|
}
|
|
};
|
|
var diff = function(A, B) {
|
|
var ops = [];
|
|
var t_A = type(A);
|
|
var t_B = type(B);
|
|
if (t_A !== t_B) {
|
|
throw new Error("Can't merge two objects of differing types");
|
|
}
|
|
if (t_B === "array") {
|
|
arrays(A, B, [], ops);
|
|
} else if (t_B === "object") {
|
|
objects(A, B, [], ops);
|
|
} else {
|
|
throw new Error("unsupported datatype" + t_B);
|
|
}
|
|
return ops;
|
|
};
|
|
var applyOp = function(O, op) {
|
|
var path;
|
|
var key;
|
|
var result;
|
|
switch (op.type) {
|
|
case "replace":
|
|
key = op.path[op.path.length - 1];
|
|
path = op.path.slice(0, op.path.length - 1);
|
|
var parent = find(O, path);
|
|
if (!parent) {
|
|
throw new Error("cannot apply change to non-existent element");
|
|
}
|
|
parent[key] = op.value;
|
|
break;
|
|
|
|
case "splice":
|
|
var found = find(O, op.path);
|
|
if (!found) {
|
|
console.error("[applyOp] expected path [%s] to exist in object", op.path.join(","));
|
|
throw new Error("Path did not exist");
|
|
}
|
|
if (type(found) !== "array") {
|
|
throw new Error("Can't splice non-array");
|
|
}
|
|
Array.prototype.splice.apply(found, [ op.offset, op.removals ].concat(op.value));
|
|
break;
|
|
|
|
case "remove":
|
|
key = op.path[op.path.length - 1];
|
|
path = op.path.slice(0, op.path.length - 1);
|
|
result = find(O, path);
|
|
if (typeof result !== "undefined") {
|
|
delete result[key];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
throw new Error("unsupported operation type");
|
|
}
|
|
};
|
|
var patch = function(O, ops) {
|
|
ops.forEach((function(op) {
|
|
applyOp(O, op);
|
|
}));
|
|
return O;
|
|
};
|
|
var arbiter = function(p_a, p_b, c) {
|
|
if (p_a.prev !== p_b.prev) {
|
|
throw new Error("Parent values don't match!");
|
|
}
|
|
if (c === CASES.splice.splice) {
|
|
console.log(p_a);
|
|
console.log(p_b);
|
|
console.log("\n\n\n\n\n\n\n\n\n");
|
|
return true;
|
|
}
|
|
var o = p_a.prev;
|
|
var ops_a = Diff.diff(o, p_a.value);
|
|
var ops_b = Diff.diff(o, p_b.value);
|
|
var ops_x = TextTransformer(ops_b, ops_a, o);
|
|
var x2 = Operation.applyMulti(ops_a, o);
|
|
var x3 = Operation.applyMulti(ops_x, x2);
|
|
p_b.value = x3;
|
|
};
|
|
module.exports = function(opsToTransform, opsTransformBy, s_orig) {
|
|
var o_orig = JSON.parse(s_orig);
|
|
var s_transformBy = Operation.applyMulti(opsTransformBy, s_orig);
|
|
var o_transformBy = JSON.parse(s_transformBy);
|
|
var s_toTransform = Operation.applyMulti(opsToTransform, s_orig);
|
|
var o_toTransform = JSON.parse(s_toTransform);
|
|
try {
|
|
var diffTTF = diff(o_orig, o_toTransform);
|
|
var diffTFB = diff(o_orig, o_transformBy);
|
|
var newDiffTTF = resolve(diffTFB, diffTTF, arbiter);
|
|
patch(o_orig, diffTFB);
|
|
patch(o_orig, newDiffTTF);
|
|
var result = Sortify(o_orig);
|
|
var ret = Diff.diff(s_transformBy, result);
|
|
return ret;
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
return [];
|
|
};
|
|
module.exports._ = {
|
|
clone,
|
|
pathOverlaps,
|
|
deepEqual,
|
|
diff,
|
|
resolve,
|
|
patch,
|
|
arbiter
|
|
};
|
|
}
|
|
};
|
|
r.m[1] = {
|
|
"dist/JSON.sortify.js": function(module, exports, require) {
|
|
(function(factory) {
|
|
if (typeof module !== "undefined" && module.exports) module.exports = factory(); else JSON.sortify = factory();
|
|
})((function() {
|
|
var sortKeys = function sortKeys(o) {
|
|
if (Array.isArray(o)) {
|
|
return o.map(sortKeys);
|
|
} else if (o instanceof Object) {
|
|
var _ret = function() {
|
|
var numeric = [];
|
|
var nonNumeric = [];
|
|
Object.keys(o).forEach((function(key) {
|
|
if (/^(0|[1-9][0-9]*)$/.test(key)) {
|
|
numeric.push(+key);
|
|
} else {
|
|
nonNumeric.push(key);
|
|
}
|
|
}));
|
|
return {
|
|
v: numeric.sort((function(a, b) {
|
|
return a - b;
|
|
})).concat(nonNumeric.sort()).reduce((function(result, key) {
|
|
result[key] = sortKeys(o[key]);
|
|
return result;
|
|
}), {})
|
|
};
|
|
}();
|
|
if (typeof _ret === "object") return _ret.v;
|
|
}
|
|
return o;
|
|
};
|
|
var jsonStringify = JSON.stringify.bind(JSON);
|
|
var sortify = function sortify(value, replacer, space) {
|
|
var nativeJson = jsonStringify(value, replacer, 0);
|
|
if (!nativeJson || nativeJson[0] !== "{" && nativeJson[0] !== "[") {
|
|
return nativeJson;
|
|
}
|
|
var cleanObj = JSON.parse(nativeJson);
|
|
return jsonStringify(sortKeys(cleanObj), null, space);
|
|
};
|
|
return sortify;
|
|
}));
|
|
}
|
|
};
|
|
r.m[2] = {
|
|
"diff.js": function(module, exports, require) {
|
|
var DIFF_DELETE = -1;
|
|
var DIFF_INSERT = 1;
|
|
var DIFF_EQUAL = 0;
|
|
function diff_main(text1, text2, cursor_pos, _fix_unicode) {
|
|
if (text1 === text2) {
|
|
if (text1) {
|
|
return [ [ DIFF_EQUAL, text1 ] ];
|
|
}
|
|
return [];
|
|
}
|
|
if (cursor_pos != null) {
|
|
var editdiff = find_cursor_edit_diff(text1, text2, cursor_pos);
|
|
if (editdiff) {
|
|
return editdiff;
|
|
}
|
|
}
|
|
var commonlength = diff_commonPrefix(text1, text2);
|
|
var commonprefix = text1.substring(0, commonlength);
|
|
text1 = text1.substring(commonlength);
|
|
text2 = text2.substring(commonlength);
|
|
commonlength = diff_commonSuffix(text1, text2);
|
|
var commonsuffix = text1.substring(text1.length - commonlength);
|
|
text1 = text1.substring(0, text1.length - commonlength);
|
|
text2 = text2.substring(0, text2.length - commonlength);
|
|
var diffs = diff_compute_(text1, text2);
|
|
if (commonprefix) {
|
|
diffs.unshift([ DIFF_EQUAL, commonprefix ]);
|
|
}
|
|
if (commonsuffix) {
|
|
diffs.push([ DIFF_EQUAL, commonsuffix ]);
|
|
}
|
|
diff_cleanupMerge(diffs, _fix_unicode);
|
|
return diffs;
|
|
}
|
|
function diff_compute_(text1, text2) {
|
|
var diffs;
|
|
if (!text1) {
|
|
return [ [ DIFF_INSERT, text2 ] ];
|
|
}
|
|
if (!text2) {
|
|
return [ [ DIFF_DELETE, text1 ] ];
|
|
}
|
|
var longtext = text1.length > text2.length ? text1 : text2;
|
|
var shorttext = text1.length > text2.length ? text2 : text1;
|
|
var i = longtext.indexOf(shorttext);
|
|
if (i !== -1) {
|
|
diffs = [ [ DIFF_INSERT, longtext.substring(0, i) ], [ DIFF_EQUAL, shorttext ], [ DIFF_INSERT, longtext.substring(i + shorttext.length) ] ];
|
|
if (text1.length > text2.length) {
|
|
diffs[0][0] = diffs[2][0] = DIFF_DELETE;
|
|
}
|
|
return diffs;
|
|
}
|
|
if (shorttext.length === 1) {
|
|
return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ];
|
|
}
|
|
var hm = diff_halfMatch_(text1, text2);
|
|
if (hm) {
|
|
var text1_a = hm[0];
|
|
var text1_b = hm[1];
|
|
var text2_a = hm[2];
|
|
var text2_b = hm[3];
|
|
var mid_common = hm[4];
|
|
var diffs_a = diff_main(text1_a, text2_a);
|
|
var diffs_b = diff_main(text1_b, text2_b);
|
|
return diffs_a.concat([ [ DIFF_EQUAL, mid_common ] ], diffs_b);
|
|
}
|
|
return diff_bisect_(text1, text2);
|
|
}
|
|
function diff_bisect_(text1, text2) {
|
|
var text1_length = text1.length;
|
|
var text2_length = text2.length;
|
|
var max_d = Math.ceil((text1_length + text2_length) / 2);
|
|
var v_offset = max_d;
|
|
var v_length = 2 * max_d;
|
|
var v1 = new Array(v_length);
|
|
var v2 = new Array(v_length);
|
|
for (var x = 0; x < v_length; x++) {
|
|
v1[x] = -1;
|
|
v2[x] = -1;
|
|
}
|
|
v1[v_offset + 1] = 0;
|
|
v2[v_offset + 1] = 0;
|
|
var delta = text1_length - text2_length;
|
|
var front = delta % 2 !== 0;
|
|
var k1start = 0;
|
|
var k1end = 0;
|
|
var k2start = 0;
|
|
var k2end = 0;
|
|
for (var d = 0; d < max_d; d++) {
|
|
for (var k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
|
|
var k1_offset = v_offset + k1;
|
|
var x1;
|
|
if (k1 === -d || k1 !== d && v1[k1_offset - 1] < v1[k1_offset + 1]) {
|
|
x1 = v1[k1_offset + 1];
|
|
} else {
|
|
x1 = v1[k1_offset - 1] + 1;
|
|
}
|
|
var y1 = x1 - k1;
|
|
while (x1 < text1_length && y1 < text2_length && text1.charAt(x1) === text2.charAt(y1)) {
|
|
x1++;
|
|
y1++;
|
|
}
|
|
v1[k1_offset] = x1;
|
|
if (x1 > text1_length) {
|
|
k1end += 2;
|
|
} else if (y1 > text2_length) {
|
|
k1start += 2;
|
|
} else if (front) {
|
|
var k2_offset = v_offset + delta - k1;
|
|
if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] !== -1) {
|
|
var x2 = text1_length - v2[k2_offset];
|
|
if (x1 >= x2) {
|
|
return diff_bisectSplit_(text1, text2, x1, y1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
for (var k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
|
|
var k2_offset = v_offset + k2;
|
|
var x2;
|
|
if (k2 === -d || k2 !== d && v2[k2_offset - 1] < v2[k2_offset + 1]) {
|
|
x2 = v2[k2_offset + 1];
|
|
} else {
|
|
x2 = v2[k2_offset - 1] + 1;
|
|
}
|
|
var y2 = x2 - k2;
|
|
while (x2 < text1_length && y2 < text2_length && text1.charAt(text1_length - x2 - 1) === text2.charAt(text2_length - y2 - 1)) {
|
|
x2++;
|
|
y2++;
|
|
}
|
|
v2[k2_offset] = x2;
|
|
if (x2 > text1_length) {
|
|
k2end += 2;
|
|
} else if (y2 > text2_length) {
|
|
k2start += 2;
|
|
} else if (!front) {
|
|
var k1_offset = v_offset + delta - k2;
|
|
if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] !== -1) {
|
|
var x1 = v1[k1_offset];
|
|
var y1 = v_offset + x1 - k1_offset;
|
|
x2 = text1_length - x2;
|
|
if (x1 >= x2) {
|
|
return diff_bisectSplit_(text1, text2, x1, y1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return [ [ DIFF_DELETE, text1 ], [ DIFF_INSERT, text2 ] ];
|
|
}
|
|
function diff_bisectSplit_(text1, text2, x, y) {
|
|
var text1a = text1.substring(0, x);
|
|
var text2a = text2.substring(0, y);
|
|
var text1b = text1.substring(x);
|
|
var text2b = text2.substring(y);
|
|
var diffs = diff_main(text1a, text2a);
|
|
var diffsb = diff_main(text1b, text2b);
|
|
return diffs.concat(diffsb);
|
|
}
|
|
function diff_commonPrefix(text1, text2) {
|
|
if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
|
|
return 0;
|
|
}
|
|
var pointermin = 0;
|
|
var pointermax = Math.min(text1.length, text2.length);
|
|
var pointermid = pointermax;
|
|
var pointerstart = 0;
|
|
while (pointermin < pointermid) {
|
|
if (text1.substring(pointerstart, pointermid) == text2.substring(pointerstart, pointermid)) {
|
|
pointermin = pointermid;
|
|
pointerstart = pointermin;
|
|
} else {
|
|
pointermax = pointermid;
|
|
}
|
|
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
|
|
}
|
|
if (is_surrogate_pair_start(text1.charCodeAt(pointermid - 1))) {
|
|
pointermid--;
|
|
}
|
|
return pointermid;
|
|
}
|
|
function diff_commonSuffix(text1, text2) {
|
|
if (!text1 || !text2 || text1.slice(-1) !== text2.slice(-1)) {
|
|
return 0;
|
|
}
|
|
var pointermin = 0;
|
|
var pointermax = Math.min(text1.length, text2.length);
|
|
var pointermid = pointermax;
|
|
var pointerend = 0;
|
|
while (pointermin < pointermid) {
|
|
if (text1.substring(text1.length - pointermid, text1.length - pointerend) == text2.substring(text2.length - pointermid, text2.length - pointerend)) {
|
|
pointermin = pointermid;
|
|
pointerend = pointermin;
|
|
} else {
|
|
pointermax = pointermid;
|
|
}
|
|
pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
|
|
}
|
|
if (is_surrogate_pair_end(text1.charCodeAt(text1.length - pointermid))) {
|
|
pointermid--;
|
|
}
|
|
return pointermid;
|
|
}
|
|
function diff_halfMatch_(text1, text2) {
|
|
var longtext = text1.length > text2.length ? text1 : text2;
|
|
var shorttext = text1.length > text2.length ? text2 : text1;
|
|
if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
|
|
return null;
|
|
}
|
|
function diff_halfMatchI_(longtext, shorttext, i) {
|
|
var seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
|
|
var j = -1;
|
|
var best_common = "";
|
|
var best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b;
|
|
while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
|
|
var prefixLength = diff_commonPrefix(longtext.substring(i), shorttext.substring(j));
|
|
var suffixLength = diff_commonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
|
|
if (best_common.length < suffixLength + prefixLength) {
|
|
best_common = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
|
|
best_longtext_a = longtext.substring(0, i - suffixLength);
|
|
best_longtext_b = longtext.substring(i + prefixLength);
|
|
best_shorttext_a = shorttext.substring(0, j - suffixLength);
|
|
best_shorttext_b = shorttext.substring(j + prefixLength);
|
|
}
|
|
}
|
|
if (best_common.length * 2 >= longtext.length) {
|
|
return [ best_longtext_a, best_longtext_b, best_shorttext_a, best_shorttext_b, best_common ];
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
var hm1 = diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 4));
|
|
var hm2 = diff_halfMatchI_(longtext, shorttext, Math.ceil(longtext.length / 2));
|
|
var hm;
|
|
if (!hm1 && !hm2) {
|
|
return null;
|
|
} else if (!hm2) {
|
|
hm = hm1;
|
|
} else if (!hm1) {
|
|
hm = hm2;
|
|
} else {
|
|
hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
|
|
}
|
|
var text1_a, text1_b, text2_a, text2_b;
|
|
if (text1.length > text2.length) {
|
|
text1_a = hm[0];
|
|
text1_b = hm[1];
|
|
text2_a = hm[2];
|
|
text2_b = hm[3];
|
|
} else {
|
|
text2_a = hm[0];
|
|
text2_b = hm[1];
|
|
text1_a = hm[2];
|
|
text1_b = hm[3];
|
|
}
|
|
var mid_common = hm[4];
|
|
return [ text1_a, text1_b, text2_a, text2_b, mid_common ];
|
|
}
|
|
function diff_cleanupMerge(diffs, fix_unicode) {
|
|
diffs.push([ DIFF_EQUAL, "" ]);
|
|
var pointer = 0;
|
|
var count_delete = 0;
|
|
var count_insert = 0;
|
|
var text_delete = "";
|
|
var text_insert = "";
|
|
var commonlength;
|
|
while (pointer < diffs.length) {
|
|
if (pointer < diffs.length - 1 && !diffs[pointer][1]) {
|
|
diffs.splice(pointer, 1);
|
|
continue;
|
|
}
|
|
switch (diffs[pointer][0]) {
|
|
case DIFF_INSERT:
|
|
count_insert++;
|
|
text_insert += diffs[pointer][1];
|
|
pointer++;
|
|
break;
|
|
|
|
case DIFF_DELETE:
|
|
count_delete++;
|
|
text_delete += diffs[pointer][1];
|
|
pointer++;
|
|
break;
|
|
|
|
case DIFF_EQUAL:
|
|
var previous_equality = pointer - count_insert - count_delete - 1;
|
|
if (fix_unicode) {
|
|
if (previous_equality >= 0 && ends_with_pair_start(diffs[previous_equality][1])) {
|
|
var stray = diffs[previous_equality][1].slice(-1);
|
|
diffs[previous_equality][1] = diffs[previous_equality][1].slice(0, -1);
|
|
text_delete = stray + text_delete;
|
|
text_insert = stray + text_insert;
|
|
if (!diffs[previous_equality][1]) {
|
|
diffs.splice(previous_equality, 1);
|
|
pointer--;
|
|
var k = previous_equality - 1;
|
|
if (diffs[k] && diffs[k][0] === DIFF_INSERT) {
|
|
count_insert++;
|
|
text_insert = diffs[k][1] + text_insert;
|
|
k--;
|
|
}
|
|
if (diffs[k] && diffs[k][0] === DIFF_DELETE) {
|
|
count_delete++;
|
|
text_delete = diffs[k][1] + text_delete;
|
|
k--;
|
|
}
|
|
previous_equality = k;
|
|
}
|
|
}
|
|
if (starts_with_pair_end(diffs[pointer][1])) {
|
|
var stray = diffs[pointer][1].charAt(0);
|
|
diffs[pointer][1] = diffs[pointer][1].slice(1);
|
|
text_delete += stray;
|
|
text_insert += stray;
|
|
}
|
|
}
|
|
if (pointer < diffs.length - 1 && !diffs[pointer][1]) {
|
|
diffs.splice(pointer, 1);
|
|
break;
|
|
}
|
|
if (text_delete.length > 0 || text_insert.length > 0) {
|
|
if (text_delete.length > 0 && text_insert.length > 0) {
|
|
commonlength = diff_commonPrefix(text_insert, text_delete);
|
|
if (commonlength !== 0) {
|
|
if (previous_equality >= 0) {
|
|
diffs[previous_equality][1] += text_insert.substring(0, commonlength);
|
|
} else {
|
|
diffs.splice(0, 0, [ DIFF_EQUAL, text_insert.substring(0, commonlength) ]);
|
|
pointer++;
|
|
}
|
|
text_insert = text_insert.substring(commonlength);
|
|
text_delete = text_delete.substring(commonlength);
|
|
}
|
|
commonlength = diff_commonSuffix(text_insert, text_delete);
|
|
if (commonlength !== 0) {
|
|
diffs[pointer][1] = text_insert.substring(text_insert.length - commonlength) + diffs[pointer][1];
|
|
text_insert = text_insert.substring(0, text_insert.length - commonlength);
|
|
text_delete = text_delete.substring(0, text_delete.length - commonlength);
|
|
}
|
|
}
|
|
var n = count_insert + count_delete;
|
|
if (text_delete.length === 0 && text_insert.length === 0) {
|
|
diffs.splice(pointer - n, n);
|
|
pointer = pointer - n;
|
|
} else if (text_delete.length === 0) {
|
|
diffs.splice(pointer - n, n, [ DIFF_INSERT, text_insert ]);
|
|
pointer = pointer - n + 1;
|
|
} else if (text_insert.length === 0) {
|
|
diffs.splice(pointer - n, n, [ DIFF_DELETE, text_delete ]);
|
|
pointer = pointer - n + 1;
|
|
} else {
|
|
diffs.splice(pointer - n, n, [ DIFF_DELETE, text_delete ], [ DIFF_INSERT, text_insert ]);
|
|
pointer = pointer - n + 2;
|
|
}
|
|
}
|
|
if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
|
|
diffs[pointer - 1][1] += diffs[pointer][1];
|
|
diffs.splice(pointer, 1);
|
|
} else {
|
|
pointer++;
|
|
}
|
|
count_insert = 0;
|
|
count_delete = 0;
|
|
text_delete = "";
|
|
text_insert = "";
|
|
break;
|
|
}
|
|
}
|
|
if (diffs[diffs.length - 1][1] === "") {
|
|
diffs.pop();
|
|
}
|
|
var changes = false;
|
|
pointer = 1;
|
|
while (pointer < diffs.length - 1) {
|
|
if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
|
|
if (diffs[pointer][1].substring(diffs[pointer][1].length - diffs[pointer - 1][1].length) === diffs[pointer - 1][1]) {
|
|
diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
|
|
diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
|
|
diffs.splice(pointer - 1, 1);
|
|
changes = true;
|
|
} else if (diffs[pointer][1].substring(0, diffs[pointer + 1][1].length) == diffs[pointer + 1][1]) {
|
|
diffs[pointer - 1][1] += diffs[pointer + 1][1];
|
|
diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
|
|
diffs.splice(pointer + 1, 1);
|
|
changes = true;
|
|
}
|
|
}
|
|
pointer++;
|
|
}
|
|
if (changes) {
|
|
diff_cleanupMerge(diffs, fix_unicode);
|
|
}
|
|
}
|
|
function is_surrogate_pair_start(charCode) {
|
|
return charCode >= 55296 && charCode <= 56319;
|
|
}
|
|
function is_surrogate_pair_end(charCode) {
|
|
return charCode >= 56320 && charCode <= 57343;
|
|
}
|
|
function starts_with_pair_end(str) {
|
|
return is_surrogate_pair_end(str.charCodeAt(0));
|
|
}
|
|
function ends_with_pair_start(str) {
|
|
return is_surrogate_pair_start(str.charCodeAt(str.length - 1));
|
|
}
|
|
function remove_empty_tuples(tuples) {
|
|
var ret = [];
|
|
for (var i = 0; i < tuples.length; i++) {
|
|
if (tuples[i][1].length > 0) {
|
|
ret.push(tuples[i]);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
function make_edit_splice(before, oldMiddle, newMiddle, after) {
|
|
if (ends_with_pair_start(before) || starts_with_pair_end(after)) {
|
|
return null;
|
|
}
|
|
return remove_empty_tuples([ [ DIFF_EQUAL, before ], [ DIFF_DELETE, oldMiddle ], [ DIFF_INSERT, newMiddle ], [ DIFF_EQUAL, after ] ]);
|
|
}
|
|
function find_cursor_edit_diff(oldText, newText, cursor_pos) {
|
|
var oldRange = typeof cursor_pos === "number" ? {
|
|
index: cursor_pos,
|
|
length: 0
|
|
} : cursor_pos.oldRange;
|
|
var newRange = typeof cursor_pos === "number" ? null : cursor_pos.newRange;
|
|
var oldLength = oldText.length;
|
|
var newLength = newText.length;
|
|
if (oldRange.length === 0 && (newRange === null || newRange.length === 0)) {
|
|
var oldCursor = oldRange.index;
|
|
var oldBefore = oldText.slice(0, oldCursor);
|
|
var oldAfter = oldText.slice(oldCursor);
|
|
var maybeNewCursor = newRange ? newRange.index : null;
|
|
editBefore: {
|
|
var newCursor = oldCursor + newLength - oldLength;
|
|
if (maybeNewCursor !== null && maybeNewCursor !== newCursor) {
|
|
break editBefore;
|
|
}
|
|
if (newCursor < 0 || newCursor > newLength) {
|
|
break editBefore;
|
|
}
|
|
var newBefore = newText.slice(0, newCursor);
|
|
var newAfter = newText.slice(newCursor);
|
|
if (newAfter !== oldAfter) {
|
|
break editBefore;
|
|
}
|
|
var prefixLength = Math.min(oldCursor, newCursor);
|
|
var oldPrefix = oldBefore.slice(0, prefixLength);
|
|
var newPrefix = newBefore.slice(0, prefixLength);
|
|
if (oldPrefix !== newPrefix) {
|
|
break editBefore;
|
|
}
|
|
var oldMiddle = oldBefore.slice(prefixLength);
|
|
var newMiddle = newBefore.slice(prefixLength);
|
|
return make_edit_splice(oldPrefix, oldMiddle, newMiddle, oldAfter);
|
|
}
|
|
editAfter: {
|
|
if (maybeNewCursor !== null && maybeNewCursor !== oldCursor) {
|
|
break editAfter;
|
|
}
|
|
var cursor = oldCursor;
|
|
var newBefore = newText.slice(0, cursor);
|
|
var newAfter = newText.slice(cursor);
|
|
if (newBefore !== oldBefore) {
|
|
break editAfter;
|
|
}
|
|
var suffixLength = Math.min(oldLength - cursor, newLength - cursor);
|
|
var oldSuffix = oldAfter.slice(oldAfter.length - suffixLength);
|
|
var newSuffix = newAfter.slice(newAfter.length - suffixLength);
|
|
if (oldSuffix !== newSuffix) {
|
|
break editAfter;
|
|
}
|
|
var oldMiddle = oldAfter.slice(0, oldAfter.length - suffixLength);
|
|
var newMiddle = newAfter.slice(0, newAfter.length - suffixLength);
|
|
return make_edit_splice(oldBefore, oldMiddle, newMiddle, oldSuffix);
|
|
}
|
|
}
|
|
if (oldRange.length > 0 && newRange && newRange.length === 0) {
|
|
replaceRange: {
|
|
var oldPrefix = oldText.slice(0, oldRange.index);
|
|
var oldSuffix = oldText.slice(oldRange.index + oldRange.length);
|
|
var prefixLength = oldPrefix.length;
|
|
var suffixLength = oldSuffix.length;
|
|
if (newLength < prefixLength + suffixLength) {
|
|
break replaceRange;
|
|
}
|
|
var newPrefix = newText.slice(0, prefixLength);
|
|
var newSuffix = newText.slice(newLength - suffixLength);
|
|
if (oldPrefix !== newPrefix || oldSuffix !== newSuffix) {
|
|
break replaceRange;
|
|
}
|
|
var oldMiddle = oldText.slice(prefixLength, oldLength - suffixLength);
|
|
var newMiddle = newText.slice(prefixLength, newLength - suffixLength);
|
|
return make_edit_splice(oldPrefix, oldMiddle, newMiddle, oldSuffix);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function diff(text1, text2, cursor_pos) {
|
|
return diff_main(text1, text2, cursor_pos, true);
|
|
}
|
|
diff.INSERT = DIFF_INSERT;
|
|
diff.DELETE = DIFF_DELETE;
|
|
diff.EQUAL = DIFF_EQUAL;
|
|
module.exports = diff;
|
|
}
|
|
};
|
|
function umd(n, f) {
|
|
module.exports = n;
|
|
var e;
|
|
"undefined" != typeof window ? e = window : "undefined" != typeof commonjsGlobal ? e = commonjsGlobal : "undefined" != typeof self && (e = self),
|
|
e[f] = n;
|
|
}
|
|
umd(r("ChainPad.js"), "ChainPad");
|
|
})();
|
|
})(chainpad_dist$1);
|
|
return chainpad_dist$1.exports;
|
|
}
|
|
var chainpad_distExports = requireChainpad_dist();
|
|
var chainpad_dist = getDefaultExportFromCjs(chainpad_distExports);
|
|
var ChainPad = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: chainpad_dist
|
|
}, [ chainpad_distExports ]);
|
|
var crypto = {
|
|
exports: {}
|
|
};
|
|
var naclFast = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredNaclFast;
|
|
function requireNaclFast() {
|
|
if (hasRequiredNaclFast) return naclFast.exports;
|
|
hasRequiredNaclFast = 1;
|
|
(function(module) {
|
|
(function(nacl) {
|
|
var gf = function(init) {
|
|
var i, r = new Float64Array(16);
|
|
if (init) for (i = 0; i < init.length; i++) r[i] = init[i];
|
|
return r;
|
|
};
|
|
var randombytes = function() {
|
|
throw new Error("no PRNG");
|
|
};
|
|
var _0 = new Uint8Array(16);
|
|
var _9 = new Uint8Array(32);
|
|
_9[0] = 9;
|
|
var gf0 = gf(), gf1 = gf([ 1 ]), _121665 = gf([ 56129, 1 ]), D = gf([ 30883, 4953, 19914, 30187, 55467, 16705, 2637, 112, 59544, 30585, 16505, 36039, 65139, 11119, 27886, 20995 ]), D2 = gf([ 61785, 9906, 39828, 60374, 45398, 33411, 5274, 224, 53552, 61171, 33010, 6542, 64743, 22239, 55772, 9222 ]), X = gf([ 54554, 36645, 11616, 51542, 42930, 38181, 51040, 26924, 56412, 64982, 57905, 49316, 21502, 52590, 14035, 8553 ]), Y = gf([ 26200, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214, 26214 ]), I = gf([ 41136, 18958, 6951, 50414, 58488, 44335, 6150, 12099, 55207, 15867, 153, 11085, 57099, 20417, 9344, 11139 ]);
|
|
function ts64(x, i, h, l) {
|
|
x[i] = h >> 24 & 255;
|
|
x[i + 1] = h >> 16 & 255;
|
|
x[i + 2] = h >> 8 & 255;
|
|
x[i + 3] = h & 255;
|
|
x[i + 4] = l >> 24 & 255;
|
|
x[i + 5] = l >> 16 & 255;
|
|
x[i + 6] = l >> 8 & 255;
|
|
x[i + 7] = l & 255;
|
|
}
|
|
function vn(x, xi, y, yi, n) {
|
|
var i, d = 0;
|
|
for (i = 0; i < n; i++) d |= x[xi + i] ^ y[yi + i];
|
|
return (1 & d - 1 >>> 8) - 1;
|
|
}
|
|
function crypto_verify_16(x, xi, y, yi) {
|
|
return vn(x, xi, y, yi, 16);
|
|
}
|
|
function crypto_verify_32(x, xi, y, yi) {
|
|
return vn(x, xi, y, yi, 32);
|
|
}
|
|
function core_salsa20(o, p, k, c) {
|
|
var j0 = c[0] & 255 | (c[1] & 255) << 8 | (c[2] & 255) << 16 | (c[3] & 255) << 24, j1 = k[0] & 255 | (k[1] & 255) << 8 | (k[2] & 255) << 16 | (k[3] & 255) << 24, j2 = k[4] & 255 | (k[5] & 255) << 8 | (k[6] & 255) << 16 | (k[7] & 255) << 24, j3 = k[8] & 255 | (k[9] & 255) << 8 | (k[10] & 255) << 16 | (k[11] & 255) << 24, j4 = k[12] & 255 | (k[13] & 255) << 8 | (k[14] & 255) << 16 | (k[15] & 255) << 24, j5 = c[4] & 255 | (c[5] & 255) << 8 | (c[6] & 255) << 16 | (c[7] & 255) << 24, j6 = p[0] & 255 | (p[1] & 255) << 8 | (p[2] & 255) << 16 | (p[3] & 255) << 24, j7 = p[4] & 255 | (p[5] & 255) << 8 | (p[6] & 255) << 16 | (p[7] & 255) << 24, j8 = p[8] & 255 | (p[9] & 255) << 8 | (p[10] & 255) << 16 | (p[11] & 255) << 24, j9 = p[12] & 255 | (p[13] & 255) << 8 | (p[14] & 255) << 16 | (p[15] & 255) << 24, j10 = c[8] & 255 | (c[9] & 255) << 8 | (c[10] & 255) << 16 | (c[11] & 255) << 24, j11 = k[16] & 255 | (k[17] & 255) << 8 | (k[18] & 255) << 16 | (k[19] & 255) << 24, j12 = k[20] & 255 | (k[21] & 255) << 8 | (k[22] & 255) << 16 | (k[23] & 255) << 24, j13 = k[24] & 255 | (k[25] & 255) << 8 | (k[26] & 255) << 16 | (k[27] & 255) << 24, j14 = k[28] & 255 | (k[29] & 255) << 8 | (k[30] & 255) << 16 | (k[31] & 255) << 24, j15 = c[12] & 255 | (c[13] & 255) << 8 | (c[14] & 255) << 16 | (c[15] & 255) << 24;
|
|
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15, u;
|
|
for (var i = 0; i < 20; i += 2) {
|
|
u = x0 + x12 | 0;
|
|
x4 ^= u << 7 | u >>> 32 - 7;
|
|
u = x4 + x0 | 0;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x4 | 0;
|
|
x12 ^= u << 13 | u >>> 32 - 13;
|
|
u = x12 + x8 | 0;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x1 | 0;
|
|
x9 ^= u << 7 | u >>> 32 - 7;
|
|
u = x9 + x5 | 0;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x9 | 0;
|
|
x1 ^= u << 13 | u >>> 32 - 13;
|
|
u = x1 + x13 | 0;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x6 | 0;
|
|
x14 ^= u << 7 | u >>> 32 - 7;
|
|
u = x14 + x10 | 0;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x14 | 0;
|
|
x6 ^= u << 13 | u >>> 32 - 13;
|
|
u = x6 + x2 | 0;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x11 | 0;
|
|
x3 ^= u << 7 | u >>> 32 - 7;
|
|
u = x3 + x15 | 0;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x3 | 0;
|
|
x11 ^= u << 13 | u >>> 32 - 13;
|
|
u = x11 + x7 | 0;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
u = x0 + x3 | 0;
|
|
x1 ^= u << 7 | u >>> 32 - 7;
|
|
u = x1 + x0 | 0;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x1 | 0;
|
|
x3 ^= u << 13 | u >>> 32 - 13;
|
|
u = x3 + x2 | 0;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x4 | 0;
|
|
x6 ^= u << 7 | u >>> 32 - 7;
|
|
u = x6 + x5 | 0;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x6 | 0;
|
|
x4 ^= u << 13 | u >>> 32 - 13;
|
|
u = x4 + x7 | 0;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x9 | 0;
|
|
x11 ^= u << 7 | u >>> 32 - 7;
|
|
u = x11 + x10 | 0;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x11 | 0;
|
|
x9 ^= u << 13 | u >>> 32 - 13;
|
|
u = x9 + x8 | 0;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x14 | 0;
|
|
x12 ^= u << 7 | u >>> 32 - 7;
|
|
u = x12 + x15 | 0;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x12 | 0;
|
|
x14 ^= u << 13 | u >>> 32 - 13;
|
|
u = x14 + x13 | 0;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
}
|
|
x0 = x0 + j0 | 0;
|
|
x1 = x1 + j1 | 0;
|
|
x2 = x2 + j2 | 0;
|
|
x3 = x3 + j3 | 0;
|
|
x4 = x4 + j4 | 0;
|
|
x5 = x5 + j5 | 0;
|
|
x6 = x6 + j6 | 0;
|
|
x7 = x7 + j7 | 0;
|
|
x8 = x8 + j8 | 0;
|
|
x9 = x9 + j9 | 0;
|
|
x10 = x10 + j10 | 0;
|
|
x11 = x11 + j11 | 0;
|
|
x12 = x12 + j12 | 0;
|
|
x13 = x13 + j13 | 0;
|
|
x14 = x14 + j14 | 0;
|
|
x15 = x15 + j15 | 0;
|
|
o[0] = x0 >>> 0 & 255;
|
|
o[1] = x0 >>> 8 & 255;
|
|
o[2] = x0 >>> 16 & 255;
|
|
o[3] = x0 >>> 24 & 255;
|
|
o[4] = x1 >>> 0 & 255;
|
|
o[5] = x1 >>> 8 & 255;
|
|
o[6] = x1 >>> 16 & 255;
|
|
o[7] = x1 >>> 24 & 255;
|
|
o[8] = x2 >>> 0 & 255;
|
|
o[9] = x2 >>> 8 & 255;
|
|
o[10] = x2 >>> 16 & 255;
|
|
o[11] = x2 >>> 24 & 255;
|
|
o[12] = x3 >>> 0 & 255;
|
|
o[13] = x3 >>> 8 & 255;
|
|
o[14] = x3 >>> 16 & 255;
|
|
o[15] = x3 >>> 24 & 255;
|
|
o[16] = x4 >>> 0 & 255;
|
|
o[17] = x4 >>> 8 & 255;
|
|
o[18] = x4 >>> 16 & 255;
|
|
o[19] = x4 >>> 24 & 255;
|
|
o[20] = x5 >>> 0 & 255;
|
|
o[21] = x5 >>> 8 & 255;
|
|
o[22] = x5 >>> 16 & 255;
|
|
o[23] = x5 >>> 24 & 255;
|
|
o[24] = x6 >>> 0 & 255;
|
|
o[25] = x6 >>> 8 & 255;
|
|
o[26] = x6 >>> 16 & 255;
|
|
o[27] = x6 >>> 24 & 255;
|
|
o[28] = x7 >>> 0 & 255;
|
|
o[29] = x7 >>> 8 & 255;
|
|
o[30] = x7 >>> 16 & 255;
|
|
o[31] = x7 >>> 24 & 255;
|
|
o[32] = x8 >>> 0 & 255;
|
|
o[33] = x8 >>> 8 & 255;
|
|
o[34] = x8 >>> 16 & 255;
|
|
o[35] = x8 >>> 24 & 255;
|
|
o[36] = x9 >>> 0 & 255;
|
|
o[37] = x9 >>> 8 & 255;
|
|
o[38] = x9 >>> 16 & 255;
|
|
o[39] = x9 >>> 24 & 255;
|
|
o[40] = x10 >>> 0 & 255;
|
|
o[41] = x10 >>> 8 & 255;
|
|
o[42] = x10 >>> 16 & 255;
|
|
o[43] = x10 >>> 24 & 255;
|
|
o[44] = x11 >>> 0 & 255;
|
|
o[45] = x11 >>> 8 & 255;
|
|
o[46] = x11 >>> 16 & 255;
|
|
o[47] = x11 >>> 24 & 255;
|
|
o[48] = x12 >>> 0 & 255;
|
|
o[49] = x12 >>> 8 & 255;
|
|
o[50] = x12 >>> 16 & 255;
|
|
o[51] = x12 >>> 24 & 255;
|
|
o[52] = x13 >>> 0 & 255;
|
|
o[53] = x13 >>> 8 & 255;
|
|
o[54] = x13 >>> 16 & 255;
|
|
o[55] = x13 >>> 24 & 255;
|
|
o[56] = x14 >>> 0 & 255;
|
|
o[57] = x14 >>> 8 & 255;
|
|
o[58] = x14 >>> 16 & 255;
|
|
o[59] = x14 >>> 24 & 255;
|
|
o[60] = x15 >>> 0 & 255;
|
|
o[61] = x15 >>> 8 & 255;
|
|
o[62] = x15 >>> 16 & 255;
|
|
o[63] = x15 >>> 24 & 255;
|
|
}
|
|
function core_hsalsa20(o, p, k, c) {
|
|
var j0 = c[0] & 255 | (c[1] & 255) << 8 | (c[2] & 255) << 16 | (c[3] & 255) << 24, j1 = k[0] & 255 | (k[1] & 255) << 8 | (k[2] & 255) << 16 | (k[3] & 255) << 24, j2 = k[4] & 255 | (k[5] & 255) << 8 | (k[6] & 255) << 16 | (k[7] & 255) << 24, j3 = k[8] & 255 | (k[9] & 255) << 8 | (k[10] & 255) << 16 | (k[11] & 255) << 24, j4 = k[12] & 255 | (k[13] & 255) << 8 | (k[14] & 255) << 16 | (k[15] & 255) << 24, j5 = c[4] & 255 | (c[5] & 255) << 8 | (c[6] & 255) << 16 | (c[7] & 255) << 24, j6 = p[0] & 255 | (p[1] & 255) << 8 | (p[2] & 255) << 16 | (p[3] & 255) << 24, j7 = p[4] & 255 | (p[5] & 255) << 8 | (p[6] & 255) << 16 | (p[7] & 255) << 24, j8 = p[8] & 255 | (p[9] & 255) << 8 | (p[10] & 255) << 16 | (p[11] & 255) << 24, j9 = p[12] & 255 | (p[13] & 255) << 8 | (p[14] & 255) << 16 | (p[15] & 255) << 24, j10 = c[8] & 255 | (c[9] & 255) << 8 | (c[10] & 255) << 16 | (c[11] & 255) << 24, j11 = k[16] & 255 | (k[17] & 255) << 8 | (k[18] & 255) << 16 | (k[19] & 255) << 24, j12 = k[20] & 255 | (k[21] & 255) << 8 | (k[22] & 255) << 16 | (k[23] & 255) << 24, j13 = k[24] & 255 | (k[25] & 255) << 8 | (k[26] & 255) << 16 | (k[27] & 255) << 24, j14 = k[28] & 255 | (k[29] & 255) << 8 | (k[30] & 255) << 16 | (k[31] & 255) << 24, j15 = c[12] & 255 | (c[13] & 255) << 8 | (c[14] & 255) << 16 | (c[15] & 255) << 24;
|
|
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15, u;
|
|
for (var i = 0; i < 20; i += 2) {
|
|
u = x0 + x12 | 0;
|
|
x4 ^= u << 7 | u >>> 32 - 7;
|
|
u = x4 + x0 | 0;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x4 | 0;
|
|
x12 ^= u << 13 | u >>> 32 - 13;
|
|
u = x12 + x8 | 0;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x1 | 0;
|
|
x9 ^= u << 7 | u >>> 32 - 7;
|
|
u = x9 + x5 | 0;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x9 | 0;
|
|
x1 ^= u << 13 | u >>> 32 - 13;
|
|
u = x1 + x13 | 0;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x6 | 0;
|
|
x14 ^= u << 7 | u >>> 32 - 7;
|
|
u = x14 + x10 | 0;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x14 | 0;
|
|
x6 ^= u << 13 | u >>> 32 - 13;
|
|
u = x6 + x2 | 0;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x11 | 0;
|
|
x3 ^= u << 7 | u >>> 32 - 7;
|
|
u = x3 + x15 | 0;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x3 | 0;
|
|
x11 ^= u << 13 | u >>> 32 - 13;
|
|
u = x11 + x7 | 0;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
u = x0 + x3 | 0;
|
|
x1 ^= u << 7 | u >>> 32 - 7;
|
|
u = x1 + x0 | 0;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x1 | 0;
|
|
x3 ^= u << 13 | u >>> 32 - 13;
|
|
u = x3 + x2 | 0;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x4 | 0;
|
|
x6 ^= u << 7 | u >>> 32 - 7;
|
|
u = x6 + x5 | 0;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x6 | 0;
|
|
x4 ^= u << 13 | u >>> 32 - 13;
|
|
u = x4 + x7 | 0;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x9 | 0;
|
|
x11 ^= u << 7 | u >>> 32 - 7;
|
|
u = x11 + x10 | 0;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x11 | 0;
|
|
x9 ^= u << 13 | u >>> 32 - 13;
|
|
u = x9 + x8 | 0;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x14 | 0;
|
|
x12 ^= u << 7 | u >>> 32 - 7;
|
|
u = x12 + x15 | 0;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x12 | 0;
|
|
x14 ^= u << 13 | u >>> 32 - 13;
|
|
u = x14 + x13 | 0;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
}
|
|
o[0] = x0 >>> 0 & 255;
|
|
o[1] = x0 >>> 8 & 255;
|
|
o[2] = x0 >>> 16 & 255;
|
|
o[3] = x0 >>> 24 & 255;
|
|
o[4] = x5 >>> 0 & 255;
|
|
o[5] = x5 >>> 8 & 255;
|
|
o[6] = x5 >>> 16 & 255;
|
|
o[7] = x5 >>> 24 & 255;
|
|
o[8] = x10 >>> 0 & 255;
|
|
o[9] = x10 >>> 8 & 255;
|
|
o[10] = x10 >>> 16 & 255;
|
|
o[11] = x10 >>> 24 & 255;
|
|
o[12] = x15 >>> 0 & 255;
|
|
o[13] = x15 >>> 8 & 255;
|
|
o[14] = x15 >>> 16 & 255;
|
|
o[15] = x15 >>> 24 & 255;
|
|
o[16] = x6 >>> 0 & 255;
|
|
o[17] = x6 >>> 8 & 255;
|
|
o[18] = x6 >>> 16 & 255;
|
|
o[19] = x6 >>> 24 & 255;
|
|
o[20] = x7 >>> 0 & 255;
|
|
o[21] = x7 >>> 8 & 255;
|
|
o[22] = x7 >>> 16 & 255;
|
|
o[23] = x7 >>> 24 & 255;
|
|
o[24] = x8 >>> 0 & 255;
|
|
o[25] = x8 >>> 8 & 255;
|
|
o[26] = x8 >>> 16 & 255;
|
|
o[27] = x8 >>> 24 & 255;
|
|
o[28] = x9 >>> 0 & 255;
|
|
o[29] = x9 >>> 8 & 255;
|
|
o[30] = x9 >>> 16 & 255;
|
|
o[31] = x9 >>> 24 & 255;
|
|
}
|
|
function crypto_core_salsa20(out, inp, k, c) {
|
|
core_salsa20(out, inp, k, c);
|
|
}
|
|
function crypto_core_hsalsa20(out, inp, k, c) {
|
|
core_hsalsa20(out, inp, k, c);
|
|
}
|
|
var sigma = new Uint8Array([ 101, 120, 112, 97, 110, 100, 32, 51, 50, 45, 98, 121, 116, 101, 32, 107 ]);
|
|
function crypto_stream_salsa20_xor(c, cpos, m, mpos, b, n, k) {
|
|
var z = new Uint8Array(16), x = new Uint8Array(64);
|
|
var u, i;
|
|
for (i = 0; i < 16; i++) z[i] = 0;
|
|
for (i = 0; i < 8; i++) z[i] = n[i];
|
|
while (b >= 64) {
|
|
crypto_core_salsa20(x, z, k, sigma);
|
|
for (i = 0; i < 64; i++) c[cpos + i] = m[mpos + i] ^ x[i];
|
|
u = 1;
|
|
for (i = 8; i < 16; i++) {
|
|
u = u + (z[i] & 255) | 0;
|
|
z[i] = u & 255;
|
|
u >>>= 8;
|
|
}
|
|
b -= 64;
|
|
cpos += 64;
|
|
mpos += 64;
|
|
}
|
|
if (b > 0) {
|
|
crypto_core_salsa20(x, z, k, sigma);
|
|
for (i = 0; i < b; i++) c[cpos + i] = m[mpos + i] ^ x[i];
|
|
}
|
|
return 0;
|
|
}
|
|
function crypto_stream_salsa20(c, cpos, b, n, k) {
|
|
var z = new Uint8Array(16), x = new Uint8Array(64);
|
|
var u, i;
|
|
for (i = 0; i < 16; i++) z[i] = 0;
|
|
for (i = 0; i < 8; i++) z[i] = n[i];
|
|
while (b >= 64) {
|
|
crypto_core_salsa20(x, z, k, sigma);
|
|
for (i = 0; i < 64; i++) c[cpos + i] = x[i];
|
|
u = 1;
|
|
for (i = 8; i < 16; i++) {
|
|
u = u + (z[i] & 255) | 0;
|
|
z[i] = u & 255;
|
|
u >>>= 8;
|
|
}
|
|
b -= 64;
|
|
cpos += 64;
|
|
}
|
|
if (b > 0) {
|
|
crypto_core_salsa20(x, z, k, sigma);
|
|
for (i = 0; i < b; i++) c[cpos + i] = x[i];
|
|
}
|
|
return 0;
|
|
}
|
|
function crypto_stream(c, cpos, d, n, k) {
|
|
var s = new Uint8Array(32);
|
|
crypto_core_hsalsa20(s, n, k, sigma);
|
|
var sn = new Uint8Array(8);
|
|
for (var i = 0; i < 8; i++) sn[i] = n[i + 16];
|
|
return crypto_stream_salsa20(c, cpos, d, sn, s);
|
|
}
|
|
function crypto_stream_xor(c, cpos, m, mpos, d, n, k) {
|
|
var s = new Uint8Array(32);
|
|
crypto_core_hsalsa20(s, n, k, sigma);
|
|
var sn = new Uint8Array(8);
|
|
for (var i = 0; i < 8; i++) sn[i] = n[i + 16];
|
|
return crypto_stream_salsa20_xor(c, cpos, m, mpos, d, sn, s);
|
|
}
|
|
var poly1305 = function(key) {
|
|
this.buffer = new Uint8Array(16);
|
|
this.r = new Uint16Array(10);
|
|
this.h = new Uint16Array(10);
|
|
this.pad = new Uint16Array(8);
|
|
this.leftover = 0;
|
|
this.fin = 0;
|
|
var t0, t1, t2, t3, t4, t5, t6, t7;
|
|
t0 = key[0] & 255 | (key[1] & 255) << 8;
|
|
this.r[0] = t0 & 8191;
|
|
t1 = key[2] & 255 | (key[3] & 255) << 8;
|
|
this.r[1] = (t0 >>> 13 | t1 << 3) & 8191;
|
|
t2 = key[4] & 255 | (key[5] & 255) << 8;
|
|
this.r[2] = (t1 >>> 10 | t2 << 6) & 7939;
|
|
t3 = key[6] & 255 | (key[7] & 255) << 8;
|
|
this.r[3] = (t2 >>> 7 | t3 << 9) & 8191;
|
|
t4 = key[8] & 255 | (key[9] & 255) << 8;
|
|
this.r[4] = (t3 >>> 4 | t4 << 12) & 255;
|
|
this.r[5] = t4 >>> 1 & 8190;
|
|
t5 = key[10] & 255 | (key[11] & 255) << 8;
|
|
this.r[6] = (t4 >>> 14 | t5 << 2) & 8191;
|
|
t6 = key[12] & 255 | (key[13] & 255) << 8;
|
|
this.r[7] = (t5 >>> 11 | t6 << 5) & 8065;
|
|
t7 = key[14] & 255 | (key[15] & 255) << 8;
|
|
this.r[8] = (t6 >>> 8 | t7 << 8) & 8191;
|
|
this.r[9] = t7 >>> 5 & 127;
|
|
this.pad[0] = key[16] & 255 | (key[17] & 255) << 8;
|
|
this.pad[1] = key[18] & 255 | (key[19] & 255) << 8;
|
|
this.pad[2] = key[20] & 255 | (key[21] & 255) << 8;
|
|
this.pad[3] = key[22] & 255 | (key[23] & 255) << 8;
|
|
this.pad[4] = key[24] & 255 | (key[25] & 255) << 8;
|
|
this.pad[5] = key[26] & 255 | (key[27] & 255) << 8;
|
|
this.pad[6] = key[28] & 255 | (key[29] & 255) << 8;
|
|
this.pad[7] = key[30] & 255 | (key[31] & 255) << 8;
|
|
};
|
|
poly1305.prototype.blocks = function(m, mpos, bytes) {
|
|
var hibit = this.fin ? 0 : 1 << 11;
|
|
var t0, t1, t2, t3, t4, t5, t6, t7, c;
|
|
var d0, d1, d2, d3, d4, d5, d6, d7, d8, d9;
|
|
var h0 = this.h[0], h1 = this.h[1], h2 = this.h[2], h3 = this.h[3], h4 = this.h[4], h5 = this.h[5], h6 = this.h[6], h7 = this.h[7], h8 = this.h[8], h9 = this.h[9];
|
|
var r0 = this.r[0], r1 = this.r[1], r2 = this.r[2], r3 = this.r[3], r4 = this.r[4], r5 = this.r[5], r6 = this.r[6], r7 = this.r[7], r8 = this.r[8], r9 = this.r[9];
|
|
while (bytes >= 16) {
|
|
t0 = m[mpos + 0] & 255 | (m[mpos + 1] & 255) << 8;
|
|
h0 += t0 & 8191;
|
|
t1 = m[mpos + 2] & 255 | (m[mpos + 3] & 255) << 8;
|
|
h1 += (t0 >>> 13 | t1 << 3) & 8191;
|
|
t2 = m[mpos + 4] & 255 | (m[mpos + 5] & 255) << 8;
|
|
h2 += (t1 >>> 10 | t2 << 6) & 8191;
|
|
t3 = m[mpos + 6] & 255 | (m[mpos + 7] & 255) << 8;
|
|
h3 += (t2 >>> 7 | t3 << 9) & 8191;
|
|
t4 = m[mpos + 8] & 255 | (m[mpos + 9] & 255) << 8;
|
|
h4 += (t3 >>> 4 | t4 << 12) & 8191;
|
|
h5 += t4 >>> 1 & 8191;
|
|
t5 = m[mpos + 10] & 255 | (m[mpos + 11] & 255) << 8;
|
|
h6 += (t4 >>> 14 | t5 << 2) & 8191;
|
|
t6 = m[mpos + 12] & 255 | (m[mpos + 13] & 255) << 8;
|
|
h7 += (t5 >>> 11 | t6 << 5) & 8191;
|
|
t7 = m[mpos + 14] & 255 | (m[mpos + 15] & 255) << 8;
|
|
h8 += (t6 >>> 8 | t7 << 8) & 8191;
|
|
h9 += t7 >>> 5 | hibit;
|
|
c = 0;
|
|
d0 = c;
|
|
d0 += h0 * r0;
|
|
d0 += h1 * (5 * r9);
|
|
d0 += h2 * (5 * r8);
|
|
d0 += h3 * (5 * r7);
|
|
d0 += h4 * (5 * r6);
|
|
c = d0 >>> 13;
|
|
d0 &= 8191;
|
|
d0 += h5 * (5 * r5);
|
|
d0 += h6 * (5 * r4);
|
|
d0 += h7 * (5 * r3);
|
|
d0 += h8 * (5 * r2);
|
|
d0 += h9 * (5 * r1);
|
|
c += d0 >>> 13;
|
|
d0 &= 8191;
|
|
d1 = c;
|
|
d1 += h0 * r1;
|
|
d1 += h1 * r0;
|
|
d1 += h2 * (5 * r9);
|
|
d1 += h3 * (5 * r8);
|
|
d1 += h4 * (5 * r7);
|
|
c = d1 >>> 13;
|
|
d1 &= 8191;
|
|
d1 += h5 * (5 * r6);
|
|
d1 += h6 * (5 * r5);
|
|
d1 += h7 * (5 * r4);
|
|
d1 += h8 * (5 * r3);
|
|
d1 += h9 * (5 * r2);
|
|
c += d1 >>> 13;
|
|
d1 &= 8191;
|
|
d2 = c;
|
|
d2 += h0 * r2;
|
|
d2 += h1 * r1;
|
|
d2 += h2 * r0;
|
|
d2 += h3 * (5 * r9);
|
|
d2 += h4 * (5 * r8);
|
|
c = d2 >>> 13;
|
|
d2 &= 8191;
|
|
d2 += h5 * (5 * r7);
|
|
d2 += h6 * (5 * r6);
|
|
d2 += h7 * (5 * r5);
|
|
d2 += h8 * (5 * r4);
|
|
d2 += h9 * (5 * r3);
|
|
c += d2 >>> 13;
|
|
d2 &= 8191;
|
|
d3 = c;
|
|
d3 += h0 * r3;
|
|
d3 += h1 * r2;
|
|
d3 += h2 * r1;
|
|
d3 += h3 * r0;
|
|
d3 += h4 * (5 * r9);
|
|
c = d3 >>> 13;
|
|
d3 &= 8191;
|
|
d3 += h5 * (5 * r8);
|
|
d3 += h6 * (5 * r7);
|
|
d3 += h7 * (5 * r6);
|
|
d3 += h8 * (5 * r5);
|
|
d3 += h9 * (5 * r4);
|
|
c += d3 >>> 13;
|
|
d3 &= 8191;
|
|
d4 = c;
|
|
d4 += h0 * r4;
|
|
d4 += h1 * r3;
|
|
d4 += h2 * r2;
|
|
d4 += h3 * r1;
|
|
d4 += h4 * r0;
|
|
c = d4 >>> 13;
|
|
d4 &= 8191;
|
|
d4 += h5 * (5 * r9);
|
|
d4 += h6 * (5 * r8);
|
|
d4 += h7 * (5 * r7);
|
|
d4 += h8 * (5 * r6);
|
|
d4 += h9 * (5 * r5);
|
|
c += d4 >>> 13;
|
|
d4 &= 8191;
|
|
d5 = c;
|
|
d5 += h0 * r5;
|
|
d5 += h1 * r4;
|
|
d5 += h2 * r3;
|
|
d5 += h3 * r2;
|
|
d5 += h4 * r1;
|
|
c = d5 >>> 13;
|
|
d5 &= 8191;
|
|
d5 += h5 * r0;
|
|
d5 += h6 * (5 * r9);
|
|
d5 += h7 * (5 * r8);
|
|
d5 += h8 * (5 * r7);
|
|
d5 += h9 * (5 * r6);
|
|
c += d5 >>> 13;
|
|
d5 &= 8191;
|
|
d6 = c;
|
|
d6 += h0 * r6;
|
|
d6 += h1 * r5;
|
|
d6 += h2 * r4;
|
|
d6 += h3 * r3;
|
|
d6 += h4 * r2;
|
|
c = d6 >>> 13;
|
|
d6 &= 8191;
|
|
d6 += h5 * r1;
|
|
d6 += h6 * r0;
|
|
d6 += h7 * (5 * r9);
|
|
d6 += h8 * (5 * r8);
|
|
d6 += h9 * (5 * r7);
|
|
c += d6 >>> 13;
|
|
d6 &= 8191;
|
|
d7 = c;
|
|
d7 += h0 * r7;
|
|
d7 += h1 * r6;
|
|
d7 += h2 * r5;
|
|
d7 += h3 * r4;
|
|
d7 += h4 * r3;
|
|
c = d7 >>> 13;
|
|
d7 &= 8191;
|
|
d7 += h5 * r2;
|
|
d7 += h6 * r1;
|
|
d7 += h7 * r0;
|
|
d7 += h8 * (5 * r9);
|
|
d7 += h9 * (5 * r8);
|
|
c += d7 >>> 13;
|
|
d7 &= 8191;
|
|
d8 = c;
|
|
d8 += h0 * r8;
|
|
d8 += h1 * r7;
|
|
d8 += h2 * r6;
|
|
d8 += h3 * r5;
|
|
d8 += h4 * r4;
|
|
c = d8 >>> 13;
|
|
d8 &= 8191;
|
|
d8 += h5 * r3;
|
|
d8 += h6 * r2;
|
|
d8 += h7 * r1;
|
|
d8 += h8 * r0;
|
|
d8 += h9 * (5 * r9);
|
|
c += d8 >>> 13;
|
|
d8 &= 8191;
|
|
d9 = c;
|
|
d9 += h0 * r9;
|
|
d9 += h1 * r8;
|
|
d9 += h2 * r7;
|
|
d9 += h3 * r6;
|
|
d9 += h4 * r5;
|
|
c = d9 >>> 13;
|
|
d9 &= 8191;
|
|
d9 += h5 * r4;
|
|
d9 += h6 * r3;
|
|
d9 += h7 * r2;
|
|
d9 += h8 * r1;
|
|
d9 += h9 * r0;
|
|
c += d9 >>> 13;
|
|
d9 &= 8191;
|
|
c = (c << 2) + c | 0;
|
|
c = c + d0 | 0;
|
|
d0 = c & 8191;
|
|
c = c >>> 13;
|
|
d1 += c;
|
|
h0 = d0;
|
|
h1 = d1;
|
|
h2 = d2;
|
|
h3 = d3;
|
|
h4 = d4;
|
|
h5 = d5;
|
|
h6 = d6;
|
|
h7 = d7;
|
|
h8 = d8;
|
|
h9 = d9;
|
|
mpos += 16;
|
|
bytes -= 16;
|
|
}
|
|
this.h[0] = h0;
|
|
this.h[1] = h1;
|
|
this.h[2] = h2;
|
|
this.h[3] = h3;
|
|
this.h[4] = h4;
|
|
this.h[5] = h5;
|
|
this.h[6] = h6;
|
|
this.h[7] = h7;
|
|
this.h[8] = h8;
|
|
this.h[9] = h9;
|
|
};
|
|
poly1305.prototype.finish = function(mac, macpos) {
|
|
var g = new Uint16Array(10);
|
|
var c, mask, f, i;
|
|
if (this.leftover) {
|
|
i = this.leftover;
|
|
this.buffer[i++] = 1;
|
|
for (;i < 16; i++) this.buffer[i] = 0;
|
|
this.fin = 1;
|
|
this.blocks(this.buffer, 0, 16);
|
|
}
|
|
c = this.h[1] >>> 13;
|
|
this.h[1] &= 8191;
|
|
for (i = 2; i < 10; i++) {
|
|
this.h[i] += c;
|
|
c = this.h[i] >>> 13;
|
|
this.h[i] &= 8191;
|
|
}
|
|
this.h[0] += c * 5;
|
|
c = this.h[0] >>> 13;
|
|
this.h[0] &= 8191;
|
|
this.h[1] += c;
|
|
c = this.h[1] >>> 13;
|
|
this.h[1] &= 8191;
|
|
this.h[2] += c;
|
|
g[0] = this.h[0] + 5;
|
|
c = g[0] >>> 13;
|
|
g[0] &= 8191;
|
|
for (i = 1; i < 10; i++) {
|
|
g[i] = this.h[i] + c;
|
|
c = g[i] >>> 13;
|
|
g[i] &= 8191;
|
|
}
|
|
g[9] -= 1 << 13;
|
|
mask = (g[9] >>> 2 * 8 - 1) - 1;
|
|
for (i = 0; i < 10; i++) g[i] &= mask;
|
|
mask = ~mask;
|
|
for (i = 0; i < 10; i++) this.h[i] = this.h[i] & mask | g[i];
|
|
this.h[0] = (this.h[0] | this.h[1] << 13) & 65535;
|
|
this.h[1] = (this.h[1] >>> 3 | this.h[2] << 10) & 65535;
|
|
this.h[2] = (this.h[2] >>> 6 | this.h[3] << 7) & 65535;
|
|
this.h[3] = (this.h[3] >>> 9 | this.h[4] << 4) & 65535;
|
|
this.h[4] = (this.h[4] >>> 12 | this.h[5] << 1 | this.h[6] << 14) & 65535;
|
|
this.h[5] = (this.h[6] >>> 2 | this.h[7] << 11) & 65535;
|
|
this.h[6] = (this.h[7] >>> 5 | this.h[8] << 8) & 65535;
|
|
this.h[7] = (this.h[8] >>> 8 | this.h[9] << 5) & 65535;
|
|
f = this.h[0] + this.pad[0];
|
|
this.h[0] = f & 65535;
|
|
for (i = 1; i < 8; i++) {
|
|
f = (this.h[i] + this.pad[i] | 0) + (f >>> 16) | 0;
|
|
this.h[i] = f & 65535;
|
|
}
|
|
mac[macpos + 0] = this.h[0] >>> 0 & 255;
|
|
mac[macpos + 1] = this.h[0] >>> 8 & 255;
|
|
mac[macpos + 2] = this.h[1] >>> 0 & 255;
|
|
mac[macpos + 3] = this.h[1] >>> 8 & 255;
|
|
mac[macpos + 4] = this.h[2] >>> 0 & 255;
|
|
mac[macpos + 5] = this.h[2] >>> 8 & 255;
|
|
mac[macpos + 6] = this.h[3] >>> 0 & 255;
|
|
mac[macpos + 7] = this.h[3] >>> 8 & 255;
|
|
mac[macpos + 8] = this.h[4] >>> 0 & 255;
|
|
mac[macpos + 9] = this.h[4] >>> 8 & 255;
|
|
mac[macpos + 10] = this.h[5] >>> 0 & 255;
|
|
mac[macpos + 11] = this.h[5] >>> 8 & 255;
|
|
mac[macpos + 12] = this.h[6] >>> 0 & 255;
|
|
mac[macpos + 13] = this.h[6] >>> 8 & 255;
|
|
mac[macpos + 14] = this.h[7] >>> 0 & 255;
|
|
mac[macpos + 15] = this.h[7] >>> 8 & 255;
|
|
};
|
|
poly1305.prototype.update = function(m, mpos, bytes) {
|
|
var i, want;
|
|
if (this.leftover) {
|
|
want = 16 - this.leftover;
|
|
if (want > bytes) want = bytes;
|
|
for (i = 0; i < want; i++) this.buffer[this.leftover + i] = m[mpos + i];
|
|
bytes -= want;
|
|
mpos += want;
|
|
this.leftover += want;
|
|
if (this.leftover < 16) return;
|
|
this.blocks(buffer, 0, 16);
|
|
this.leftover = 0;
|
|
}
|
|
if (bytes >= 16) {
|
|
want = bytes - bytes % 16;
|
|
this.blocks(m, mpos, want);
|
|
mpos += want;
|
|
bytes -= want;
|
|
}
|
|
if (bytes) {
|
|
for (i = 0; i < bytes; i++) this.buffer[this.leftover + i] = m[mpos + i];
|
|
this.leftover += bytes;
|
|
}
|
|
};
|
|
function crypto_onetimeauth(out, outpos, m, mpos, n, k) {
|
|
var s = new poly1305(k);
|
|
s.update(m, mpos, n);
|
|
s.finish(out, outpos);
|
|
return 0;
|
|
}
|
|
function crypto_onetimeauth_verify(h, hpos, m, mpos, n, k) {
|
|
var x = new Uint8Array(16);
|
|
crypto_onetimeauth(x, 0, m, mpos, n, k);
|
|
return crypto_verify_16(h, hpos, x, 0);
|
|
}
|
|
function crypto_secretbox(c, m, d, n, k) {
|
|
var i;
|
|
if (d < 32) return -1;
|
|
crypto_stream_xor(c, 0, m, 0, d, n, k);
|
|
crypto_onetimeauth(c, 16, c, 32, d - 32, c);
|
|
for (i = 0; i < 16; i++) c[i] = 0;
|
|
return 0;
|
|
}
|
|
function crypto_secretbox_open(m, c, d, n, k) {
|
|
var i;
|
|
var x = new Uint8Array(32);
|
|
if (d < 32) return -1;
|
|
crypto_stream(x, 0, 32, n, k);
|
|
if (crypto_onetimeauth_verify(c, 16, c, 32, d - 32, x) !== 0) return -1;
|
|
crypto_stream_xor(m, 0, c, 0, d, n, k);
|
|
for (i = 0; i < 32; i++) m[i] = 0;
|
|
return 0;
|
|
}
|
|
function set25519(r, a) {
|
|
var i;
|
|
for (i = 0; i < 16; i++) r[i] = a[i] | 0;
|
|
}
|
|
function car25519(o) {
|
|
var i, v, c = 1;
|
|
for (i = 0; i < 16; i++) {
|
|
v = o[i] + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
o[i] = v - c * 65536;
|
|
}
|
|
o[0] += c - 1 + 37 * (c - 1);
|
|
}
|
|
function sel25519(p, q, b) {
|
|
var t, c = ~(b - 1);
|
|
for (var i = 0; i < 16; i++) {
|
|
t = c & (p[i] ^ q[i]);
|
|
p[i] ^= t;
|
|
q[i] ^= t;
|
|
}
|
|
}
|
|
function pack25519(o, n) {
|
|
var i, j, b;
|
|
var m = gf(), t = gf();
|
|
for (i = 0; i < 16; i++) t[i] = n[i];
|
|
car25519(t);
|
|
car25519(t);
|
|
car25519(t);
|
|
for (j = 0; j < 2; j++) {
|
|
m[0] = t[0] - 65517;
|
|
for (i = 1; i < 15; i++) {
|
|
m[i] = t[i] - 65535 - (m[i - 1] >> 16 & 1);
|
|
m[i - 1] &= 65535;
|
|
}
|
|
m[15] = t[15] - 32767 - (m[14] >> 16 & 1);
|
|
b = m[15] >> 16 & 1;
|
|
m[14] &= 65535;
|
|
sel25519(t, m, 1 - b);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
o[2 * i] = t[i] & 255;
|
|
o[2 * i + 1] = t[i] >> 8;
|
|
}
|
|
}
|
|
function neq25519(a, b) {
|
|
var c = new Uint8Array(32), d = new Uint8Array(32);
|
|
pack25519(c, a);
|
|
pack25519(d, b);
|
|
return crypto_verify_32(c, 0, d, 0);
|
|
}
|
|
function par25519(a) {
|
|
var d = new Uint8Array(32);
|
|
pack25519(d, a);
|
|
return d[0] & 1;
|
|
}
|
|
function unpack25519(o, n) {
|
|
var i;
|
|
for (i = 0; i < 16; i++) o[i] = n[2 * i] + (n[2 * i + 1] << 8);
|
|
o[15] &= 32767;
|
|
}
|
|
function A(o, a, b) {
|
|
for (var i = 0; i < 16; i++) o[i] = a[i] + b[i];
|
|
}
|
|
function Z(o, a, b) {
|
|
for (var i = 0; i < 16; i++) o[i] = a[i] - b[i];
|
|
}
|
|
function M(o, a, b) {
|
|
var v, c, t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0, t8 = 0, t9 = 0, t10 = 0, t11 = 0, t12 = 0, t13 = 0, t14 = 0, t15 = 0, t16 = 0, t17 = 0, t18 = 0, t19 = 0, t20 = 0, t21 = 0, t22 = 0, t23 = 0, t24 = 0, t25 = 0, t26 = 0, t27 = 0, t28 = 0, t29 = 0, t30 = 0, b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3], b4 = b[4], b5 = b[5], b6 = b[6], b7 = b[7], b8 = b[8], b9 = b[9], b10 = b[10], b11 = b[11], b12 = b[12], b13 = b[13], b14 = b[14], b15 = b[15];
|
|
v = a[0];
|
|
t0 += v * b0;
|
|
t1 += v * b1;
|
|
t2 += v * b2;
|
|
t3 += v * b3;
|
|
t4 += v * b4;
|
|
t5 += v * b5;
|
|
t6 += v * b6;
|
|
t7 += v * b7;
|
|
t8 += v * b8;
|
|
t9 += v * b9;
|
|
t10 += v * b10;
|
|
t11 += v * b11;
|
|
t12 += v * b12;
|
|
t13 += v * b13;
|
|
t14 += v * b14;
|
|
t15 += v * b15;
|
|
v = a[1];
|
|
t1 += v * b0;
|
|
t2 += v * b1;
|
|
t3 += v * b2;
|
|
t4 += v * b3;
|
|
t5 += v * b4;
|
|
t6 += v * b5;
|
|
t7 += v * b6;
|
|
t8 += v * b7;
|
|
t9 += v * b8;
|
|
t10 += v * b9;
|
|
t11 += v * b10;
|
|
t12 += v * b11;
|
|
t13 += v * b12;
|
|
t14 += v * b13;
|
|
t15 += v * b14;
|
|
t16 += v * b15;
|
|
v = a[2];
|
|
t2 += v * b0;
|
|
t3 += v * b1;
|
|
t4 += v * b2;
|
|
t5 += v * b3;
|
|
t6 += v * b4;
|
|
t7 += v * b5;
|
|
t8 += v * b6;
|
|
t9 += v * b7;
|
|
t10 += v * b8;
|
|
t11 += v * b9;
|
|
t12 += v * b10;
|
|
t13 += v * b11;
|
|
t14 += v * b12;
|
|
t15 += v * b13;
|
|
t16 += v * b14;
|
|
t17 += v * b15;
|
|
v = a[3];
|
|
t3 += v * b0;
|
|
t4 += v * b1;
|
|
t5 += v * b2;
|
|
t6 += v * b3;
|
|
t7 += v * b4;
|
|
t8 += v * b5;
|
|
t9 += v * b6;
|
|
t10 += v * b7;
|
|
t11 += v * b8;
|
|
t12 += v * b9;
|
|
t13 += v * b10;
|
|
t14 += v * b11;
|
|
t15 += v * b12;
|
|
t16 += v * b13;
|
|
t17 += v * b14;
|
|
t18 += v * b15;
|
|
v = a[4];
|
|
t4 += v * b0;
|
|
t5 += v * b1;
|
|
t6 += v * b2;
|
|
t7 += v * b3;
|
|
t8 += v * b4;
|
|
t9 += v * b5;
|
|
t10 += v * b6;
|
|
t11 += v * b7;
|
|
t12 += v * b8;
|
|
t13 += v * b9;
|
|
t14 += v * b10;
|
|
t15 += v * b11;
|
|
t16 += v * b12;
|
|
t17 += v * b13;
|
|
t18 += v * b14;
|
|
t19 += v * b15;
|
|
v = a[5];
|
|
t5 += v * b0;
|
|
t6 += v * b1;
|
|
t7 += v * b2;
|
|
t8 += v * b3;
|
|
t9 += v * b4;
|
|
t10 += v * b5;
|
|
t11 += v * b6;
|
|
t12 += v * b7;
|
|
t13 += v * b8;
|
|
t14 += v * b9;
|
|
t15 += v * b10;
|
|
t16 += v * b11;
|
|
t17 += v * b12;
|
|
t18 += v * b13;
|
|
t19 += v * b14;
|
|
t20 += v * b15;
|
|
v = a[6];
|
|
t6 += v * b0;
|
|
t7 += v * b1;
|
|
t8 += v * b2;
|
|
t9 += v * b3;
|
|
t10 += v * b4;
|
|
t11 += v * b5;
|
|
t12 += v * b6;
|
|
t13 += v * b7;
|
|
t14 += v * b8;
|
|
t15 += v * b9;
|
|
t16 += v * b10;
|
|
t17 += v * b11;
|
|
t18 += v * b12;
|
|
t19 += v * b13;
|
|
t20 += v * b14;
|
|
t21 += v * b15;
|
|
v = a[7];
|
|
t7 += v * b0;
|
|
t8 += v * b1;
|
|
t9 += v * b2;
|
|
t10 += v * b3;
|
|
t11 += v * b4;
|
|
t12 += v * b5;
|
|
t13 += v * b6;
|
|
t14 += v * b7;
|
|
t15 += v * b8;
|
|
t16 += v * b9;
|
|
t17 += v * b10;
|
|
t18 += v * b11;
|
|
t19 += v * b12;
|
|
t20 += v * b13;
|
|
t21 += v * b14;
|
|
t22 += v * b15;
|
|
v = a[8];
|
|
t8 += v * b0;
|
|
t9 += v * b1;
|
|
t10 += v * b2;
|
|
t11 += v * b3;
|
|
t12 += v * b4;
|
|
t13 += v * b5;
|
|
t14 += v * b6;
|
|
t15 += v * b7;
|
|
t16 += v * b8;
|
|
t17 += v * b9;
|
|
t18 += v * b10;
|
|
t19 += v * b11;
|
|
t20 += v * b12;
|
|
t21 += v * b13;
|
|
t22 += v * b14;
|
|
t23 += v * b15;
|
|
v = a[9];
|
|
t9 += v * b0;
|
|
t10 += v * b1;
|
|
t11 += v * b2;
|
|
t12 += v * b3;
|
|
t13 += v * b4;
|
|
t14 += v * b5;
|
|
t15 += v * b6;
|
|
t16 += v * b7;
|
|
t17 += v * b8;
|
|
t18 += v * b9;
|
|
t19 += v * b10;
|
|
t20 += v * b11;
|
|
t21 += v * b12;
|
|
t22 += v * b13;
|
|
t23 += v * b14;
|
|
t24 += v * b15;
|
|
v = a[10];
|
|
t10 += v * b0;
|
|
t11 += v * b1;
|
|
t12 += v * b2;
|
|
t13 += v * b3;
|
|
t14 += v * b4;
|
|
t15 += v * b5;
|
|
t16 += v * b6;
|
|
t17 += v * b7;
|
|
t18 += v * b8;
|
|
t19 += v * b9;
|
|
t20 += v * b10;
|
|
t21 += v * b11;
|
|
t22 += v * b12;
|
|
t23 += v * b13;
|
|
t24 += v * b14;
|
|
t25 += v * b15;
|
|
v = a[11];
|
|
t11 += v * b0;
|
|
t12 += v * b1;
|
|
t13 += v * b2;
|
|
t14 += v * b3;
|
|
t15 += v * b4;
|
|
t16 += v * b5;
|
|
t17 += v * b6;
|
|
t18 += v * b7;
|
|
t19 += v * b8;
|
|
t20 += v * b9;
|
|
t21 += v * b10;
|
|
t22 += v * b11;
|
|
t23 += v * b12;
|
|
t24 += v * b13;
|
|
t25 += v * b14;
|
|
t26 += v * b15;
|
|
v = a[12];
|
|
t12 += v * b0;
|
|
t13 += v * b1;
|
|
t14 += v * b2;
|
|
t15 += v * b3;
|
|
t16 += v * b4;
|
|
t17 += v * b5;
|
|
t18 += v * b6;
|
|
t19 += v * b7;
|
|
t20 += v * b8;
|
|
t21 += v * b9;
|
|
t22 += v * b10;
|
|
t23 += v * b11;
|
|
t24 += v * b12;
|
|
t25 += v * b13;
|
|
t26 += v * b14;
|
|
t27 += v * b15;
|
|
v = a[13];
|
|
t13 += v * b0;
|
|
t14 += v * b1;
|
|
t15 += v * b2;
|
|
t16 += v * b3;
|
|
t17 += v * b4;
|
|
t18 += v * b5;
|
|
t19 += v * b6;
|
|
t20 += v * b7;
|
|
t21 += v * b8;
|
|
t22 += v * b9;
|
|
t23 += v * b10;
|
|
t24 += v * b11;
|
|
t25 += v * b12;
|
|
t26 += v * b13;
|
|
t27 += v * b14;
|
|
t28 += v * b15;
|
|
v = a[14];
|
|
t14 += v * b0;
|
|
t15 += v * b1;
|
|
t16 += v * b2;
|
|
t17 += v * b3;
|
|
t18 += v * b4;
|
|
t19 += v * b5;
|
|
t20 += v * b6;
|
|
t21 += v * b7;
|
|
t22 += v * b8;
|
|
t23 += v * b9;
|
|
t24 += v * b10;
|
|
t25 += v * b11;
|
|
t26 += v * b12;
|
|
t27 += v * b13;
|
|
t28 += v * b14;
|
|
t29 += v * b15;
|
|
v = a[15];
|
|
t15 += v * b0;
|
|
t16 += v * b1;
|
|
t17 += v * b2;
|
|
t18 += v * b3;
|
|
t19 += v * b4;
|
|
t20 += v * b5;
|
|
t21 += v * b6;
|
|
t22 += v * b7;
|
|
t23 += v * b8;
|
|
t24 += v * b9;
|
|
t25 += v * b10;
|
|
t26 += v * b11;
|
|
t27 += v * b12;
|
|
t28 += v * b13;
|
|
t29 += v * b14;
|
|
t30 += v * b15;
|
|
t0 += 38 * t16;
|
|
t1 += 38 * t17;
|
|
t2 += 38 * t18;
|
|
t3 += 38 * t19;
|
|
t4 += 38 * t20;
|
|
t5 += 38 * t21;
|
|
t6 += 38 * t22;
|
|
t7 += 38 * t23;
|
|
t8 += 38 * t24;
|
|
t9 += 38 * t25;
|
|
t10 += 38 * t26;
|
|
t11 += 38 * t27;
|
|
t12 += 38 * t28;
|
|
t13 += 38 * t29;
|
|
t14 += 38 * t30;
|
|
c = 1;
|
|
v = t0 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t0 = v - c * 65536;
|
|
v = t1 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t1 = v - c * 65536;
|
|
v = t2 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t2 = v - c * 65536;
|
|
v = t3 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t3 = v - c * 65536;
|
|
v = t4 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t4 = v - c * 65536;
|
|
v = t5 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t5 = v - c * 65536;
|
|
v = t6 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t6 = v - c * 65536;
|
|
v = t7 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t7 = v - c * 65536;
|
|
v = t8 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t8 = v - c * 65536;
|
|
v = t9 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t9 = v - c * 65536;
|
|
v = t10 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t10 = v - c * 65536;
|
|
v = t11 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t11 = v - c * 65536;
|
|
v = t12 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t12 = v - c * 65536;
|
|
v = t13 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t13 = v - c * 65536;
|
|
v = t14 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t14 = v - c * 65536;
|
|
v = t15 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t15 = v - c * 65536;
|
|
t0 += c - 1 + 37 * (c - 1);
|
|
c = 1;
|
|
v = t0 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t0 = v - c * 65536;
|
|
v = t1 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t1 = v - c * 65536;
|
|
v = t2 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t2 = v - c * 65536;
|
|
v = t3 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t3 = v - c * 65536;
|
|
v = t4 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t4 = v - c * 65536;
|
|
v = t5 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t5 = v - c * 65536;
|
|
v = t6 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t6 = v - c * 65536;
|
|
v = t7 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t7 = v - c * 65536;
|
|
v = t8 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t8 = v - c * 65536;
|
|
v = t9 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t9 = v - c * 65536;
|
|
v = t10 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t10 = v - c * 65536;
|
|
v = t11 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t11 = v - c * 65536;
|
|
v = t12 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t12 = v - c * 65536;
|
|
v = t13 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t13 = v - c * 65536;
|
|
v = t14 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t14 = v - c * 65536;
|
|
v = t15 + c + 65535;
|
|
c = Math.floor(v / 65536);
|
|
t15 = v - c * 65536;
|
|
t0 += c - 1 + 37 * (c - 1);
|
|
o[0] = t0;
|
|
o[1] = t1;
|
|
o[2] = t2;
|
|
o[3] = t3;
|
|
o[4] = t4;
|
|
o[5] = t5;
|
|
o[6] = t6;
|
|
o[7] = t7;
|
|
o[8] = t8;
|
|
o[9] = t9;
|
|
o[10] = t10;
|
|
o[11] = t11;
|
|
o[12] = t12;
|
|
o[13] = t13;
|
|
o[14] = t14;
|
|
o[15] = t15;
|
|
}
|
|
function S(o, a) {
|
|
M(o, a, a);
|
|
}
|
|
function inv25519(o, i) {
|
|
var c = gf();
|
|
var a;
|
|
for (a = 0; a < 16; a++) c[a] = i[a];
|
|
for (a = 253; a >= 0; a--) {
|
|
S(c, c);
|
|
if (a !== 2 && a !== 4) M(c, c, i);
|
|
}
|
|
for (a = 0; a < 16; a++) o[a] = c[a];
|
|
}
|
|
function pow2523(o, i) {
|
|
var c = gf();
|
|
var a;
|
|
for (a = 0; a < 16; a++) c[a] = i[a];
|
|
for (a = 250; a >= 0; a--) {
|
|
S(c, c);
|
|
if (a !== 1) M(c, c, i);
|
|
}
|
|
for (a = 0; a < 16; a++) o[a] = c[a];
|
|
}
|
|
function crypto_scalarmult(q, n, p) {
|
|
var z = new Uint8Array(32);
|
|
var x = new Float64Array(80), r, i;
|
|
var a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf();
|
|
for (i = 0; i < 31; i++) z[i] = n[i];
|
|
z[31] = n[31] & 127 | 64;
|
|
z[0] &= 248;
|
|
unpack25519(x, p);
|
|
for (i = 0; i < 16; i++) {
|
|
b[i] = x[i];
|
|
d[i] = a[i] = c[i] = 0;
|
|
}
|
|
a[0] = d[0] = 1;
|
|
for (i = 254; i >= 0; --i) {
|
|
r = z[i >>> 3] >>> (i & 7) & 1;
|
|
sel25519(a, b, r);
|
|
sel25519(c, d, r);
|
|
A(e, a, c);
|
|
Z(a, a, c);
|
|
A(c, b, d);
|
|
Z(b, b, d);
|
|
S(d, e);
|
|
S(f, a);
|
|
M(a, c, a);
|
|
M(c, b, e);
|
|
A(e, a, c);
|
|
Z(a, a, c);
|
|
S(b, a);
|
|
Z(c, d, f);
|
|
M(a, c, _121665);
|
|
A(a, a, d);
|
|
M(c, c, a);
|
|
M(a, d, f);
|
|
M(d, b, x);
|
|
S(b, e);
|
|
sel25519(a, b, r);
|
|
sel25519(c, d, r);
|
|
}
|
|
for (i = 0; i < 16; i++) {
|
|
x[i + 16] = a[i];
|
|
x[i + 32] = c[i];
|
|
x[i + 48] = b[i];
|
|
x[i + 64] = d[i];
|
|
}
|
|
var x32 = x.subarray(32);
|
|
var x16 = x.subarray(16);
|
|
inv25519(x32, x32);
|
|
M(x16, x16, x32);
|
|
pack25519(q, x16);
|
|
return 0;
|
|
}
|
|
function crypto_scalarmult_base(q, n) {
|
|
return crypto_scalarmult(q, n, _9);
|
|
}
|
|
function crypto_box_keypair(y, x) {
|
|
randombytes(x, 32);
|
|
return crypto_scalarmult_base(y, x);
|
|
}
|
|
function crypto_box_beforenm(k, y, x) {
|
|
var s = new Uint8Array(32);
|
|
crypto_scalarmult(s, x, y);
|
|
return crypto_core_hsalsa20(k, _0, s, sigma);
|
|
}
|
|
var crypto_box_afternm = crypto_secretbox;
|
|
var crypto_box_open_afternm = crypto_secretbox_open;
|
|
function crypto_box(c, m, d, n, y, x) {
|
|
var k = new Uint8Array(32);
|
|
crypto_box_beforenm(k, y, x);
|
|
return crypto_box_afternm(c, m, d, n, k);
|
|
}
|
|
function crypto_box_open(m, c, d, n, y, x) {
|
|
var k = new Uint8Array(32);
|
|
crypto_box_beforenm(k, y, x);
|
|
return crypto_box_open_afternm(m, c, d, n, k);
|
|
}
|
|
var K = [ 1116352408, 3609767458, 1899447441, 602891725, 3049323471, 3964484399, 3921009573, 2173295548, 961987163, 4081628472, 1508970993, 3053834265, 2453635748, 2937671579, 2870763221, 3664609560, 3624381080, 2734883394, 310598401, 1164996542, 607225278, 1323610764, 1426881987, 3590304994, 1925078388, 4068182383, 2162078206, 991336113, 2614888103, 633803317, 3248222580, 3479774868, 3835390401, 2666613458, 4022224774, 944711139, 264347078, 2341262773, 604807628, 2007800933, 770255983, 1495990901, 1249150122, 1856431235, 1555081692, 3175218132, 1996064986, 2198950837, 2554220882, 3999719339, 2821834349, 766784016, 2952996808, 2566594879, 3210313671, 3203337956, 3336571891, 1034457026, 3584528711, 2466948901, 113926993, 3758326383, 338241895, 168717936, 666307205, 1188179964, 773529912, 1546045734, 1294757372, 1522805485, 1396182291, 2643833823, 1695183700, 2343527390, 1986661051, 1014477480, 2177026350, 1206759142, 2456956037, 344077627, 2730485921, 1290863460, 2820302411, 3158454273, 3259730800, 3505952657, 3345764771, 106217008, 3516065817, 3606008344, 3600352804, 1432725776, 4094571909, 1467031594, 275423344, 851169720, 430227734, 3100823752, 506948616, 1363258195, 659060556, 3750685593, 883997877, 3785050280, 958139571, 3318307427, 1322822218, 3812723403, 1537002063, 2003034995, 1747873779, 3602036899, 1955562222, 1575990012, 2024104815, 1125592928, 2227730452, 2716904306, 2361852424, 442776044, 2428436474, 593698344, 2756734187, 3733110249, 3204031479, 2999351573, 3329325298, 3815920427, 3391569614, 3928383900, 3515267271, 566280711, 3940187606, 3454069534, 4118630271, 4000239992, 116418474, 1914138554, 174292421, 2731055270, 289380356, 3203993006, 460393269, 320620315, 685471733, 587496836, 852142971, 1086792851, 1017036298, 365543100, 1126000580, 2618297676, 1288033470, 3409855158, 1501505948, 4234509866, 1607167915, 987167468, 1816402316, 1246189591 ];
|
|
function crypto_hashblocks_hl(hh, hl, m, n) {
|
|
var wh = new Int32Array(16), wl = new Int32Array(16), bh0, bh1, bh2, bh3, bh4, bh5, bh6, bh7, bl0, bl1, bl2, bl3, bl4, bl5, bl6, bl7, th, tl, i, j, h, l, a, b, c, d;
|
|
var ah0 = hh[0], ah1 = hh[1], ah2 = hh[2], ah3 = hh[3], ah4 = hh[4], ah5 = hh[5], ah6 = hh[6], ah7 = hh[7], al0 = hl[0], al1 = hl[1], al2 = hl[2], al3 = hl[3], al4 = hl[4], al5 = hl[5], al6 = hl[6], al7 = hl[7];
|
|
var pos = 0;
|
|
while (n >= 128) {
|
|
for (i = 0; i < 16; i++) {
|
|
j = 8 * i + pos;
|
|
wh[i] = m[j + 0] << 24 | m[j + 1] << 16 | m[j + 2] << 8 | m[j + 3];
|
|
wl[i] = m[j + 4] << 24 | m[j + 5] << 16 | m[j + 6] << 8 | m[j + 7];
|
|
}
|
|
for (i = 0; i < 80; i++) {
|
|
bh0 = ah0;
|
|
bh1 = ah1;
|
|
bh2 = ah2;
|
|
bh3 = ah3;
|
|
bh4 = ah4;
|
|
bh5 = ah5;
|
|
bh6 = ah6;
|
|
bh7 = ah7;
|
|
bl0 = al0;
|
|
bl1 = al1;
|
|
bl2 = al2;
|
|
bl3 = al3;
|
|
bl4 = al4;
|
|
bl5 = al5;
|
|
bl6 = al6;
|
|
bl7 = al7;
|
|
h = ah7;
|
|
l = al7;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = (ah4 >>> 14 | al4 << 32 - 14) ^ (ah4 >>> 18 | al4 << 32 - 18) ^ (al4 >>> 41 - 32 | ah4 << 32 - (41 - 32));
|
|
l = (al4 >>> 14 | ah4 << 32 - 14) ^ (al4 >>> 18 | ah4 << 32 - 18) ^ (ah4 >>> 41 - 32 | al4 << 32 - (41 - 32));
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
h = ah4 & ah5 ^ ~ah4 & ah6;
|
|
l = al4 & al5 ^ ~al4 & al6;
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
h = K[i * 2];
|
|
l = K[i * 2 + 1];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
h = wh[i % 16];
|
|
l = wl[i % 16];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
th = c & 65535 | d << 16;
|
|
tl = a & 65535 | b << 16;
|
|
h = th;
|
|
l = tl;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = (ah0 >>> 28 | al0 << 32 - 28) ^ (al0 >>> 34 - 32 | ah0 << 32 - (34 - 32)) ^ (al0 >>> 39 - 32 | ah0 << 32 - (39 - 32));
|
|
l = (al0 >>> 28 | ah0 << 32 - 28) ^ (ah0 >>> 34 - 32 | al0 << 32 - (34 - 32)) ^ (ah0 >>> 39 - 32 | al0 << 32 - (39 - 32));
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
h = ah0 & ah1 ^ ah0 & ah2 ^ ah1 & ah2;
|
|
l = al0 & al1 ^ al0 & al2 ^ al1 & al2;
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
bh7 = c & 65535 | d << 16;
|
|
bl7 = a & 65535 | b << 16;
|
|
h = bh3;
|
|
l = bl3;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = th;
|
|
l = tl;
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
bh3 = c & 65535 | d << 16;
|
|
bl3 = a & 65535 | b << 16;
|
|
ah1 = bh0;
|
|
ah2 = bh1;
|
|
ah3 = bh2;
|
|
ah4 = bh3;
|
|
ah5 = bh4;
|
|
ah6 = bh5;
|
|
ah7 = bh6;
|
|
ah0 = bh7;
|
|
al1 = bl0;
|
|
al2 = bl1;
|
|
al3 = bl2;
|
|
al4 = bl3;
|
|
al5 = bl4;
|
|
al6 = bl5;
|
|
al7 = bl6;
|
|
al0 = bl7;
|
|
if (i % 16 === 15) {
|
|
for (j = 0; j < 16; j++) {
|
|
h = wh[j];
|
|
l = wl[j];
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = wh[(j + 9) % 16];
|
|
l = wl[(j + 9) % 16];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
th = wh[(j + 1) % 16];
|
|
tl = wl[(j + 1) % 16];
|
|
h = (th >>> 1 | tl << 32 - 1) ^ (th >>> 8 | tl << 32 - 8) ^ th >>> 7;
|
|
l = (tl >>> 1 | th << 32 - 1) ^ (tl >>> 8 | th << 32 - 8) ^ (tl >>> 7 | th << 32 - 7);
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
th = wh[(j + 14) % 16];
|
|
tl = wl[(j + 14) % 16];
|
|
h = (th >>> 19 | tl << 32 - 19) ^ (tl >>> 61 - 32 | th << 32 - (61 - 32)) ^ th >>> 6;
|
|
l = (tl >>> 19 | th << 32 - 19) ^ (th >>> 61 - 32 | tl << 32 - (61 - 32)) ^ (tl >>> 6 | th << 32 - 6);
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
wh[j] = c & 65535 | d << 16;
|
|
wl[j] = a & 65535 | b << 16;
|
|
}
|
|
}
|
|
}
|
|
h = ah0;
|
|
l = al0;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[0];
|
|
l = hl[0];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[0] = ah0 = c & 65535 | d << 16;
|
|
hl[0] = al0 = a & 65535 | b << 16;
|
|
h = ah1;
|
|
l = al1;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[1];
|
|
l = hl[1];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[1] = ah1 = c & 65535 | d << 16;
|
|
hl[1] = al1 = a & 65535 | b << 16;
|
|
h = ah2;
|
|
l = al2;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[2];
|
|
l = hl[2];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[2] = ah2 = c & 65535 | d << 16;
|
|
hl[2] = al2 = a & 65535 | b << 16;
|
|
h = ah3;
|
|
l = al3;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[3];
|
|
l = hl[3];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[3] = ah3 = c & 65535 | d << 16;
|
|
hl[3] = al3 = a & 65535 | b << 16;
|
|
h = ah4;
|
|
l = al4;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[4];
|
|
l = hl[4];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[4] = ah4 = c & 65535 | d << 16;
|
|
hl[4] = al4 = a & 65535 | b << 16;
|
|
h = ah5;
|
|
l = al5;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[5];
|
|
l = hl[5];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[5] = ah5 = c & 65535 | d << 16;
|
|
hl[5] = al5 = a & 65535 | b << 16;
|
|
h = ah6;
|
|
l = al6;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[6];
|
|
l = hl[6];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[6] = ah6 = c & 65535 | d << 16;
|
|
hl[6] = al6 = a & 65535 | b << 16;
|
|
h = ah7;
|
|
l = al7;
|
|
a = l & 65535;
|
|
b = l >>> 16;
|
|
c = h & 65535;
|
|
d = h >>> 16;
|
|
h = hh[7];
|
|
l = hl[7];
|
|
a += l & 65535;
|
|
b += l >>> 16;
|
|
c += h & 65535;
|
|
d += h >>> 16;
|
|
b += a >>> 16;
|
|
c += b >>> 16;
|
|
d += c >>> 16;
|
|
hh[7] = ah7 = c & 65535 | d << 16;
|
|
hl[7] = al7 = a & 65535 | b << 16;
|
|
pos += 128;
|
|
n -= 128;
|
|
}
|
|
return n;
|
|
}
|
|
function crypto_hash(out, m, n) {
|
|
var hh = new Int32Array(8), hl = new Int32Array(8), x = new Uint8Array(256), i, b = n;
|
|
hh[0] = 1779033703;
|
|
hh[1] = 3144134277;
|
|
hh[2] = 1013904242;
|
|
hh[3] = 2773480762;
|
|
hh[4] = 1359893119;
|
|
hh[5] = 2600822924;
|
|
hh[6] = 528734635;
|
|
hh[7] = 1541459225;
|
|
hl[0] = 4089235720;
|
|
hl[1] = 2227873595;
|
|
hl[2] = 4271175723;
|
|
hl[3] = 1595750129;
|
|
hl[4] = 2917565137;
|
|
hl[5] = 725511199;
|
|
hl[6] = 4215389547;
|
|
hl[7] = 327033209;
|
|
crypto_hashblocks_hl(hh, hl, m, n);
|
|
n %= 128;
|
|
for (i = 0; i < n; i++) x[i] = m[b - n + i];
|
|
x[n] = 128;
|
|
n = 256 - 128 * (n < 112 ? 1 : 0);
|
|
x[n - 9] = 0;
|
|
ts64(x, n - 8, b / 536870912 | 0, b << 3);
|
|
crypto_hashblocks_hl(hh, hl, x, n);
|
|
for (i = 0; i < 8; i++) ts64(out, 8 * i, hh[i], hl[i]);
|
|
return 0;
|
|
}
|
|
function add(p, q) {
|
|
var a = gf(), b = gf(), c = gf(), d = gf(), e = gf(), f = gf(), g = gf(), h = gf(), t = gf();
|
|
Z(a, p[1], p[0]);
|
|
Z(t, q[1], q[0]);
|
|
M(a, a, t);
|
|
A(b, p[0], p[1]);
|
|
A(t, q[0], q[1]);
|
|
M(b, b, t);
|
|
M(c, p[3], q[3]);
|
|
M(c, c, D2);
|
|
M(d, p[2], q[2]);
|
|
A(d, d, d);
|
|
Z(e, b, a);
|
|
Z(f, d, c);
|
|
A(g, d, c);
|
|
A(h, b, a);
|
|
M(p[0], e, f);
|
|
M(p[1], h, g);
|
|
M(p[2], g, f);
|
|
M(p[3], e, h);
|
|
}
|
|
function cswap(p, q, b) {
|
|
var i;
|
|
for (i = 0; i < 4; i++) {
|
|
sel25519(p[i], q[i], b);
|
|
}
|
|
}
|
|
function pack(r, p) {
|
|
var tx = gf(), ty = gf(), zi = gf();
|
|
inv25519(zi, p[2]);
|
|
M(tx, p[0], zi);
|
|
M(ty, p[1], zi);
|
|
pack25519(r, ty);
|
|
r[31] ^= par25519(tx) << 7;
|
|
}
|
|
function scalarmult(p, q, s) {
|
|
var b, i;
|
|
set25519(p[0], gf0);
|
|
set25519(p[1], gf1);
|
|
set25519(p[2], gf1);
|
|
set25519(p[3], gf0);
|
|
for (i = 255; i >= 0; --i) {
|
|
b = s[i / 8 | 0] >> (i & 7) & 1;
|
|
cswap(p, q, b);
|
|
add(q, p);
|
|
add(p, p);
|
|
cswap(p, q, b);
|
|
}
|
|
}
|
|
function scalarbase(p, s) {
|
|
var q = [ gf(), gf(), gf(), gf() ];
|
|
set25519(q[0], X);
|
|
set25519(q[1], Y);
|
|
set25519(q[2], gf1);
|
|
M(q[3], X, Y);
|
|
scalarmult(p, q, s);
|
|
}
|
|
function crypto_sign_keypair(pk, sk, seeded) {
|
|
var d = new Uint8Array(64);
|
|
var p = [ gf(), gf(), gf(), gf() ];
|
|
var i;
|
|
if (!seeded) randombytes(sk, 32);
|
|
crypto_hash(d, sk, 32);
|
|
d[0] &= 248;
|
|
d[31] &= 127;
|
|
d[31] |= 64;
|
|
scalarbase(p, d);
|
|
pack(pk, p);
|
|
for (i = 0; i < 32; i++) sk[i + 32] = pk[i];
|
|
return 0;
|
|
}
|
|
var L = new Float64Array([ 237, 211, 245, 92, 26, 99, 18, 88, 214, 156, 247, 162, 222, 249, 222, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16 ]);
|
|
function modL(r, x) {
|
|
var carry, i, j, k;
|
|
for (i = 63; i >= 32; --i) {
|
|
carry = 0;
|
|
for (j = i - 32, k = i - 12; j < k; ++j) {
|
|
x[j] += carry - 16 * x[i] * L[j - (i - 32)];
|
|
carry = x[j] + 128 >> 8;
|
|
x[j] -= carry * 256;
|
|
}
|
|
x[j] += carry;
|
|
x[i] = 0;
|
|
}
|
|
carry = 0;
|
|
for (j = 0; j < 32; j++) {
|
|
x[j] += carry - (x[31] >> 4) * L[j];
|
|
carry = x[j] >> 8;
|
|
x[j] &= 255;
|
|
}
|
|
for (j = 0; j < 32; j++) x[j] -= carry * L[j];
|
|
for (i = 0; i < 32; i++) {
|
|
x[i + 1] += x[i] >> 8;
|
|
r[i] = x[i] & 255;
|
|
}
|
|
}
|
|
function reduce(r) {
|
|
var x = new Float64Array(64), i;
|
|
for (i = 0; i < 64; i++) x[i] = r[i];
|
|
for (i = 0; i < 64; i++) r[i] = 0;
|
|
modL(r, x);
|
|
}
|
|
function crypto_sign(sm, m, n, sk) {
|
|
var d = new Uint8Array(64), h = new Uint8Array(64), r = new Uint8Array(64);
|
|
var i, j, x = new Float64Array(64);
|
|
var p = [ gf(), gf(), gf(), gf() ];
|
|
crypto_hash(d, sk, 32);
|
|
d[0] &= 248;
|
|
d[31] &= 127;
|
|
d[31] |= 64;
|
|
var smlen = n + 64;
|
|
for (i = 0; i < n; i++) sm[64 + i] = m[i];
|
|
for (i = 0; i < 32; i++) sm[32 + i] = d[32 + i];
|
|
crypto_hash(r, sm.subarray(32), n + 32);
|
|
reduce(r);
|
|
scalarbase(p, r);
|
|
pack(sm, p);
|
|
for (i = 32; i < 64; i++) sm[i] = sk[i];
|
|
crypto_hash(h, sm, n + 64);
|
|
reduce(h);
|
|
for (i = 0; i < 64; i++) x[i] = 0;
|
|
for (i = 0; i < 32; i++) x[i] = r[i];
|
|
for (i = 0; i < 32; i++) {
|
|
for (j = 0; j < 32; j++) {
|
|
x[i + j] += h[i] * d[j];
|
|
}
|
|
}
|
|
modL(sm.subarray(32), x);
|
|
return smlen;
|
|
}
|
|
function unpackneg(r, p) {
|
|
var t = gf(), chk = gf(), num = gf(), den = gf(), den2 = gf(), den4 = gf(), den6 = gf();
|
|
set25519(r[2], gf1);
|
|
unpack25519(r[1], p);
|
|
S(num, r[1]);
|
|
M(den, num, D);
|
|
Z(num, num, r[2]);
|
|
A(den, r[2], den);
|
|
S(den2, den);
|
|
S(den4, den2);
|
|
M(den6, den4, den2);
|
|
M(t, den6, num);
|
|
M(t, t, den);
|
|
pow2523(t, t);
|
|
M(t, t, num);
|
|
M(t, t, den);
|
|
M(t, t, den);
|
|
M(r[0], t, den);
|
|
S(chk, r[0]);
|
|
M(chk, chk, den);
|
|
if (neq25519(chk, num)) M(r[0], r[0], I);
|
|
S(chk, r[0]);
|
|
M(chk, chk, den);
|
|
if (neq25519(chk, num)) return -1;
|
|
if (par25519(r[0]) === p[31] >> 7) Z(r[0], gf0, r[0]);
|
|
M(r[3], r[0], r[1]);
|
|
return 0;
|
|
}
|
|
function crypto_sign_open(m, sm, n, pk) {
|
|
var i, mlen;
|
|
var t = new Uint8Array(32), h = new Uint8Array(64);
|
|
var p = [ gf(), gf(), gf(), gf() ], q = [ gf(), gf(), gf(), gf() ];
|
|
mlen = -1;
|
|
if (n < 64) return -1;
|
|
if (unpackneg(q, pk)) return -1;
|
|
for (i = 0; i < n; i++) m[i] = sm[i];
|
|
for (i = 0; i < 32; i++) m[i + 32] = pk[i];
|
|
crypto_hash(h, m, n);
|
|
reduce(h);
|
|
scalarmult(p, q, h);
|
|
scalarbase(q, sm.subarray(32));
|
|
add(p, q);
|
|
pack(t, p);
|
|
n -= 64;
|
|
if (crypto_verify_32(sm, 0, t, 0)) {
|
|
for (i = 0; i < n; i++) m[i] = 0;
|
|
return -1;
|
|
}
|
|
for (i = 0; i < n; i++) m[i] = sm[i + 64];
|
|
mlen = n;
|
|
return mlen;
|
|
}
|
|
var crypto_secretbox_KEYBYTES = 32, crypto_secretbox_NONCEBYTES = 24, crypto_secretbox_ZEROBYTES = 32, crypto_secretbox_BOXZEROBYTES = 16, crypto_scalarmult_BYTES = 32, crypto_scalarmult_SCALARBYTES = 32, crypto_box_PUBLICKEYBYTES = 32, crypto_box_SECRETKEYBYTES = 32, crypto_box_BEFORENMBYTES = 32, crypto_box_NONCEBYTES = crypto_secretbox_NONCEBYTES, crypto_box_ZEROBYTES = crypto_secretbox_ZEROBYTES, crypto_box_BOXZEROBYTES = crypto_secretbox_BOXZEROBYTES, crypto_sign_BYTES = 64, crypto_sign_PUBLICKEYBYTES = 32, crypto_sign_SECRETKEYBYTES = 64, crypto_sign_SEEDBYTES = 32, crypto_hash_BYTES = 64;
|
|
nacl.lowlevel = {
|
|
crypto_core_hsalsa20,
|
|
crypto_stream_xor,
|
|
crypto_stream,
|
|
crypto_stream_salsa20_xor,
|
|
crypto_stream_salsa20,
|
|
crypto_onetimeauth,
|
|
crypto_onetimeauth_verify,
|
|
crypto_verify_16,
|
|
crypto_verify_32,
|
|
crypto_secretbox,
|
|
crypto_secretbox_open,
|
|
crypto_scalarmult,
|
|
crypto_scalarmult_base,
|
|
crypto_box_beforenm,
|
|
crypto_box_afternm,
|
|
crypto_box,
|
|
crypto_box_open,
|
|
crypto_box_keypair,
|
|
crypto_hash,
|
|
crypto_sign,
|
|
crypto_sign_keypair,
|
|
crypto_sign_open,
|
|
crypto_secretbox_KEYBYTES,
|
|
crypto_secretbox_NONCEBYTES,
|
|
crypto_secretbox_ZEROBYTES,
|
|
crypto_secretbox_BOXZEROBYTES,
|
|
crypto_scalarmult_BYTES,
|
|
crypto_scalarmult_SCALARBYTES,
|
|
crypto_box_PUBLICKEYBYTES,
|
|
crypto_box_SECRETKEYBYTES,
|
|
crypto_box_BEFORENMBYTES,
|
|
crypto_box_NONCEBYTES,
|
|
crypto_box_ZEROBYTES,
|
|
crypto_box_BOXZEROBYTES,
|
|
crypto_sign_BYTES,
|
|
crypto_sign_PUBLICKEYBYTES,
|
|
crypto_sign_SECRETKEYBYTES,
|
|
crypto_sign_SEEDBYTES,
|
|
crypto_hash_BYTES
|
|
};
|
|
function checkLengths(k, n) {
|
|
if (k.length !== crypto_secretbox_KEYBYTES) throw new Error("bad key size");
|
|
if (n.length !== crypto_secretbox_NONCEBYTES) throw new Error("bad nonce size");
|
|
}
|
|
function checkBoxLengths(pk, sk) {
|
|
if (pk.length !== crypto_box_PUBLICKEYBYTES) throw new Error("bad public key size");
|
|
if (sk.length !== crypto_box_SECRETKEYBYTES) throw new Error("bad secret key size");
|
|
}
|
|
function checkArrayTypes() {
|
|
var type = {}.toString, t;
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
if ((t = type.call(arguments[i])) !== "[object Uint8Array]") throw new TypeError("unexpected type " + t + ", use Uint8Array");
|
|
}
|
|
}
|
|
nacl.util = {};
|
|
nacl.util.decodeUTF8 = function(s) {
|
|
var i, d = unescape(encodeURIComponent(s)), b = new Uint8Array(d.length);
|
|
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
|
|
return b;
|
|
};
|
|
nacl.util.encodeUTF8 = function(arr) {
|
|
var i, s = [];
|
|
for (i = 0; i < arr.length; i++) s.push(String.fromCharCode(arr[i]));
|
|
return decodeURIComponent(escape(s.join("")));
|
|
};
|
|
nacl.util.encodeBase64 = function(arr) {
|
|
if (typeof btoa === "undefined") {
|
|
return new Buffer(arr).toString("base64");
|
|
} else {
|
|
var i, s = [], len = arr.length;
|
|
for (i = 0; i < len; i++) s.push(String.fromCharCode(arr[i]));
|
|
return btoa(s.join(""));
|
|
}
|
|
};
|
|
nacl.util.decodeBase64 = function(s) {
|
|
if (typeof atob === "undefined") {
|
|
return new Uint8Array(Array.prototype.slice.call(new Buffer(s, "base64"), 0));
|
|
} else {
|
|
var i, d = atob(s), b = new Uint8Array(d.length);
|
|
for (i = 0; i < d.length; i++) b[i] = d.charCodeAt(i);
|
|
return b;
|
|
}
|
|
};
|
|
nacl.randomBytes = function(n) {
|
|
var b = new Uint8Array(n);
|
|
randombytes(b, n);
|
|
return b;
|
|
};
|
|
nacl.secretbox = function(msg, nonce, key) {
|
|
checkArrayTypes(msg, nonce, key);
|
|
checkLengths(key, nonce);
|
|
var m = new Uint8Array(crypto_secretbox_ZEROBYTES + msg.length);
|
|
var c = new Uint8Array(m.length);
|
|
for (var i = 0; i < msg.length; i++) m[i + crypto_secretbox_ZEROBYTES] = msg[i];
|
|
crypto_secretbox(c, m, m.length, nonce, key);
|
|
return c.subarray(crypto_secretbox_BOXZEROBYTES);
|
|
};
|
|
nacl.secretbox.open = function(box, nonce, key) {
|
|
checkArrayTypes(box, nonce, key);
|
|
checkLengths(key, nonce);
|
|
var c = new Uint8Array(crypto_secretbox_BOXZEROBYTES + box.length);
|
|
var m = new Uint8Array(c.length);
|
|
for (var i = 0; i < box.length; i++) c[i + crypto_secretbox_BOXZEROBYTES] = box[i];
|
|
if (c.length < 32) return false;
|
|
if (crypto_secretbox_open(m, c, c.length, nonce, key) !== 0) return false;
|
|
return m.subarray(crypto_secretbox_ZEROBYTES);
|
|
};
|
|
nacl.secretbox.keyLength = crypto_secretbox_KEYBYTES;
|
|
nacl.secretbox.nonceLength = crypto_secretbox_NONCEBYTES;
|
|
nacl.secretbox.overheadLength = crypto_secretbox_BOXZEROBYTES;
|
|
nacl.scalarMult = function(n, p) {
|
|
checkArrayTypes(n, p);
|
|
if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error("bad n size");
|
|
if (p.length !== crypto_scalarmult_BYTES) throw new Error("bad p size");
|
|
var q = new Uint8Array(crypto_scalarmult_BYTES);
|
|
crypto_scalarmult(q, n, p);
|
|
return q;
|
|
};
|
|
nacl.scalarMult.base = function(n) {
|
|
checkArrayTypes(n);
|
|
if (n.length !== crypto_scalarmult_SCALARBYTES) throw new Error("bad n size");
|
|
var q = new Uint8Array(crypto_scalarmult_BYTES);
|
|
crypto_scalarmult_base(q, n);
|
|
return q;
|
|
};
|
|
nacl.scalarMult.scalarLength = crypto_scalarmult_SCALARBYTES;
|
|
nacl.scalarMult.groupElementLength = crypto_scalarmult_BYTES;
|
|
nacl.box = function(msg, nonce, publicKey, secretKey) {
|
|
var k = nacl.box.before(publicKey, secretKey);
|
|
return nacl.secretbox(msg, nonce, k);
|
|
};
|
|
nacl.box.before = function(publicKey, secretKey) {
|
|
checkArrayTypes(publicKey, secretKey);
|
|
checkBoxLengths(publicKey, secretKey);
|
|
var k = new Uint8Array(crypto_box_BEFORENMBYTES);
|
|
crypto_box_beforenm(k, publicKey, secretKey);
|
|
return k;
|
|
};
|
|
nacl.box.after = nacl.secretbox;
|
|
nacl.box.open = function(msg, nonce, publicKey, secretKey) {
|
|
var k = nacl.box.before(publicKey, secretKey);
|
|
return nacl.secretbox.open(msg, nonce, k);
|
|
};
|
|
nacl.box.open.after = nacl.secretbox.open;
|
|
nacl.box.keyPair = function() {
|
|
var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
|
|
var sk = new Uint8Array(crypto_box_SECRETKEYBYTES);
|
|
crypto_box_keypair(pk, sk);
|
|
return {
|
|
publicKey: pk,
|
|
secretKey: sk
|
|
};
|
|
};
|
|
nacl.box.keyPair.fromSecretKey = function(secretKey) {
|
|
checkArrayTypes(secretKey);
|
|
if (secretKey.length !== crypto_box_SECRETKEYBYTES) throw new Error("bad secret key size");
|
|
var pk = new Uint8Array(crypto_box_PUBLICKEYBYTES);
|
|
crypto_scalarmult_base(pk, secretKey);
|
|
return {
|
|
publicKey: pk,
|
|
secretKey: new Uint8Array(secretKey)
|
|
};
|
|
};
|
|
nacl.box.publicKeyLength = crypto_box_PUBLICKEYBYTES;
|
|
nacl.box.secretKeyLength = crypto_box_SECRETKEYBYTES;
|
|
nacl.box.sharedKeyLength = crypto_box_BEFORENMBYTES;
|
|
nacl.box.nonceLength = crypto_box_NONCEBYTES;
|
|
nacl.box.overheadLength = nacl.secretbox.overheadLength;
|
|
nacl.sign = function(msg, secretKey) {
|
|
checkArrayTypes(msg, secretKey);
|
|
if (secretKey.length !== crypto_sign_SECRETKEYBYTES) throw new Error("bad secret key size");
|
|
var signedMsg = new Uint8Array(crypto_sign_BYTES + msg.length);
|
|
crypto_sign(signedMsg, msg, msg.length, secretKey);
|
|
return signedMsg;
|
|
};
|
|
nacl.sign.open = function(signedMsg, publicKey) {
|
|
if (arguments.length !== 2) throw new Error("nacl.sign.open accepts 2 arguments; did you mean to use nacl.sign.detached.verify?");
|
|
checkArrayTypes(signedMsg, publicKey);
|
|
if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) throw new Error("bad public key size");
|
|
var tmp = new Uint8Array(signedMsg.length);
|
|
var mlen = crypto_sign_open(tmp, signedMsg, signedMsg.length, publicKey);
|
|
if (mlen < 0) return null;
|
|
var m = new Uint8Array(mlen);
|
|
for (var i = 0; i < m.length; i++) m[i] = tmp[i];
|
|
return m;
|
|
};
|
|
nacl.sign.detached = function(msg, secretKey) {
|
|
var signedMsg = nacl.sign(msg, secretKey);
|
|
var sig = new Uint8Array(crypto_sign_BYTES);
|
|
for (var i = 0; i < sig.length; i++) sig[i] = signedMsg[i];
|
|
return sig;
|
|
};
|
|
nacl.sign.detached.verify = function(msg, sig, publicKey) {
|
|
checkArrayTypes(msg, sig, publicKey);
|
|
if (sig.length !== crypto_sign_BYTES) throw new Error("bad signature size");
|
|
if (publicKey.length !== crypto_sign_PUBLICKEYBYTES) throw new Error("bad public key size");
|
|
var sm = new Uint8Array(crypto_sign_BYTES + msg.length);
|
|
var m = new Uint8Array(crypto_sign_BYTES + msg.length);
|
|
var i;
|
|
for (i = 0; i < crypto_sign_BYTES; i++) sm[i] = sig[i];
|
|
for (i = 0; i < msg.length; i++) sm[i + crypto_sign_BYTES] = msg[i];
|
|
return crypto_sign_open(m, sm, sm.length, publicKey) >= 0;
|
|
};
|
|
nacl.sign.keyPair = function() {
|
|
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
|
|
var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
|
|
crypto_sign_keypair(pk, sk);
|
|
return {
|
|
publicKey: pk,
|
|
secretKey: sk
|
|
};
|
|
};
|
|
nacl.sign.keyPair.fromSecretKey = function(secretKey) {
|
|
checkArrayTypes(secretKey);
|
|
if (secretKey.length !== crypto_sign_SECRETKEYBYTES) throw new Error("bad secret key size");
|
|
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
|
|
for (var i = 0; i < pk.length; i++) pk[i] = secretKey[32 + i];
|
|
return {
|
|
publicKey: pk,
|
|
secretKey: new Uint8Array(secretKey)
|
|
};
|
|
};
|
|
nacl.sign.keyPair.fromSeed = function(seed) {
|
|
checkArrayTypes(seed);
|
|
if (seed.length !== crypto_sign_SEEDBYTES) throw new Error("bad seed size");
|
|
var pk = new Uint8Array(crypto_sign_PUBLICKEYBYTES);
|
|
var sk = new Uint8Array(crypto_sign_SECRETKEYBYTES);
|
|
for (var i = 0; i < 32; i++) sk[i] = seed[i];
|
|
crypto_sign_keypair(pk, sk, true);
|
|
return {
|
|
publicKey: pk,
|
|
secretKey: sk
|
|
};
|
|
};
|
|
nacl.sign.publicKeyLength = crypto_sign_PUBLICKEYBYTES;
|
|
nacl.sign.secretKeyLength = crypto_sign_SECRETKEYBYTES;
|
|
nacl.sign.seedLength = crypto_sign_SEEDBYTES;
|
|
nacl.sign.signatureLength = crypto_sign_BYTES;
|
|
nacl.hash = function(msg) {
|
|
checkArrayTypes(msg);
|
|
var h = new Uint8Array(crypto_hash_BYTES);
|
|
crypto_hash(h, msg, msg.length);
|
|
return h;
|
|
};
|
|
nacl.hash.hashLength = crypto_hash_BYTES;
|
|
nacl.verify = function(x, y) {
|
|
checkArrayTypes(x, y);
|
|
if (x.length === 0 || y.length === 0) return false;
|
|
if (x.length !== y.length) return false;
|
|
return vn(x, 0, y, 0, x.length) === 0 ? true : false;
|
|
};
|
|
nacl.setPRNG = function(fn) {
|
|
randombytes = fn;
|
|
};
|
|
(function() {
|
|
var crypto;
|
|
if (typeof window !== "undefined") {
|
|
if (window.crypto && window.crypto.getRandomValues) {
|
|
crypto = window.crypto;
|
|
} else if (window.msCrypto && window.msCrypto.getRandomValues) {
|
|
crypto = window.msCrypto;
|
|
}
|
|
if (crypto) {
|
|
nacl.setPRNG((function(x, n) {
|
|
var i, v = new Uint8Array(n);
|
|
crypto.getRandomValues(v);
|
|
for (i = 0; i < n; i++) x[i] = v[i];
|
|
}));
|
|
}
|
|
} else if (typeof commonjsRequire !== "undefined") {
|
|
crypto = require("crypto");
|
|
if (crypto) {
|
|
nacl.setPRNG((function(x, n) {
|
|
var i, v = crypto.randomBytes(n);
|
|
for (i = 0; i < n; i++) x[i] = v[i];
|
|
}));
|
|
}
|
|
}
|
|
})();
|
|
})(module.exports ? module.exports : window.nacl = window.nacl || {});
|
|
})(naclFast);
|
|
return naclFast.exports;
|
|
}
|
|
var hasRequiredCrypto;
|
|
function requireCrypto() {
|
|
if (hasRequiredCrypto) return crypto.exports;
|
|
hasRequiredCrypto = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Nacl) {
|
|
var Crypto = {
|
|
Nacl
|
|
};
|
|
var encodeBase64 = Nacl.util.encodeBase64;
|
|
var decodeBase64 = Nacl.util.decodeBase64;
|
|
var decodeUTF8 = Nacl.util.decodeUTF8;
|
|
var encodeUTF8 = Nacl.util.encodeUTF8;
|
|
var encodeHex = function(bytes) {
|
|
var hexString = "";
|
|
for (var i = 0; i < bytes.length; i++) {
|
|
if (bytes[i] < 16) {
|
|
hexString += "0";
|
|
}
|
|
hexString += bytes[i].toString(16);
|
|
}
|
|
return hexString;
|
|
};
|
|
var encryptStr = function(str, key) {
|
|
var array = decodeUTF8(str);
|
|
var nonce = Nacl.randomBytes(24);
|
|
var packed = Nacl.secretbox(array, nonce, key);
|
|
if (!packed) {
|
|
throw new Error;
|
|
}
|
|
return encodeBase64(nonce) + "|" + encodeBase64(packed);
|
|
};
|
|
var decryptStr = function(str, key) {
|
|
var arr = str.split("|");
|
|
if (arr.length !== 2) {
|
|
throw new Error;
|
|
}
|
|
var nonce = decodeBase64(arr[0]);
|
|
var packed = decodeBase64(arr[1]);
|
|
var unpacked = Nacl.secretbox.open(packed, nonce, key);
|
|
if (!unpacked) {
|
|
throw new Error;
|
|
}
|
|
return encodeUTF8(unpacked);
|
|
};
|
|
var encrypt = Crypto.encrypt = function(msg, key) {
|
|
return encryptStr(msg, key);
|
|
};
|
|
var decrypt = Crypto.decrypt = function(msg, key) {
|
|
return decryptStr(msg, key);
|
|
};
|
|
var parseKey = Crypto.parseKey = function(str) {
|
|
try {
|
|
var array = decodeBase64(str);
|
|
var hash = Nacl.hash(array);
|
|
var lk = hash.subarray(32);
|
|
return {
|
|
lookupKey: lk,
|
|
cryptKey: hash.subarray(0, 32),
|
|
channel: encodeBase64(lk).substring(0, 10)
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.parseKey] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
var rand64 = Crypto.rand64 = function(bytes) {
|
|
return encodeBase64(Nacl.randomBytes(bytes));
|
|
};
|
|
Crypto.genKey = function() {
|
|
return rand64(18);
|
|
};
|
|
var b64Encode = function(bytes) {
|
|
return encodeBase64(bytes).replace(/\//g, "-").replace(/=+$/g, "");
|
|
};
|
|
var b64Decode = function(str) {
|
|
return decodeBase64(str.replace(/\-/g, "/"));
|
|
};
|
|
Crypto.b64RemoveSlashes = function(str) {
|
|
return str.replace(/\//g, "-");
|
|
};
|
|
Crypto.b64AddSlashes = function(str) {
|
|
return str.replace(/\-/g, "/");
|
|
};
|
|
Crypto.createEncryptor = function(input) {
|
|
var key;
|
|
if (typeof input === "object") {
|
|
var out = {};
|
|
key = input.cryptKey;
|
|
if (!key) {
|
|
throw new Error("NO_DECRYPTION_KEY_PROVIDED");
|
|
}
|
|
if (input.signKey) {
|
|
var signKey = decodeBase64(input.signKey);
|
|
out.encrypt = function(msg) {
|
|
return encodeBase64(Nacl.sign(decodeUTF8(encrypt(msg, key)), signKey));
|
|
};
|
|
}
|
|
out.decrypt = function(msg, validateKey, skipCheck) {
|
|
if (!validateKey && !skipCheck) {
|
|
throw new Error("UNSUPPORTED_DECRYPTION_CONFIGURATION");
|
|
}
|
|
if (validateKey === true && !skipCheck) {
|
|
console.error("UNEXPECTED_CONFIGURATION");
|
|
}
|
|
var validated = skipCheck || typeof validateKey !== "string" ? decodeBase64(msg).subarray(64) : Nacl.sign.open(decodeBase64(msg), decodeBase64(validateKey));
|
|
if (!validated) {
|
|
return;
|
|
}
|
|
return decrypt(encodeUTF8(validated), key);
|
|
};
|
|
return out;
|
|
}
|
|
key = parseKey(input).cryptKey;
|
|
return {
|
|
encrypt: function(msg) {
|
|
return encrypt(msg, key);
|
|
},
|
|
decrypt: function(msg) {
|
|
return decrypt(msg, key);
|
|
}
|
|
};
|
|
};
|
|
Crypto.createEditCryptor = function(keyStr, seed) {
|
|
try {
|
|
if (!keyStr) {
|
|
if (seed && seed.length !== 18) {
|
|
throw new Error("expected supplied seed to have length of 18");
|
|
} else if (!seed) {
|
|
seed = Nacl.randomBytes(18);
|
|
}
|
|
keyStr = encodeBase64(seed);
|
|
}
|
|
var hash = Nacl.hash(decodeBase64(keyStr));
|
|
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
|
|
var cryptKey = hash.subarray(32, 64);
|
|
return {
|
|
editKeyStr: keyStr,
|
|
signKey: encodeBase64(signKp.secretKey),
|
|
validateKey: encodeBase64(signKp.publicKey),
|
|
cryptKey,
|
|
viewKeyStr: b64Encode(cryptKey)
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.createEditCryptor] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
Crypto.createViewCryptor = function(cryptKeyStr) {
|
|
try {
|
|
if (!cryptKeyStr) {
|
|
throw new Error("Cannot open a new pad in read-only mode!");
|
|
}
|
|
return {
|
|
cryptKey: decodeBase64(cryptKeyStr),
|
|
viewKeyStr: cryptKeyStr
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.createViewCryptor] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
var createViewCryptor2 = Crypto.createViewCryptor2 = function(viewKeyStr, password) {
|
|
try {
|
|
if (!viewKeyStr) {
|
|
throw new Error("Cannot open a new pad in read-only mode!");
|
|
}
|
|
var seed = b64Decode(viewKeyStr);
|
|
var superSeed = seed;
|
|
if (password) {
|
|
var pwKey = decodeUTF8(password);
|
|
superSeed = new Uint8Array(seed.length + pwKey.length);
|
|
superSeed.set(pwKey);
|
|
superSeed.set(seed, pwKey.length);
|
|
}
|
|
var hash = Nacl.hash(superSeed);
|
|
var chanId = hash.subarray(0, 16);
|
|
var cryptKey = hash.subarray(16, 48);
|
|
var signKp2 = Nacl.sign.keyPair.fromSeed(hash.subarray(32, 64));
|
|
return {
|
|
viewKeyStr,
|
|
cryptKey,
|
|
chanId: b64Encode(chanId),
|
|
secondarySignKey: encodeBase64(signKp2.secretKey),
|
|
secondaryValidateKey: encodeBase64(signKp2.publicKey)
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.createViewCryptor2] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
Crypto.createEditCryptor2 = function(keyStr, seed, password) {
|
|
try {
|
|
if (!keyStr) {
|
|
if (seed && seed.length !== 18) {
|
|
throw new Error("expected supplied seed to have length of 18");
|
|
} else if (!seed) {
|
|
seed = Nacl.randomBytes(18);
|
|
}
|
|
keyStr = b64Encode(seed);
|
|
}
|
|
if (!seed) {
|
|
seed = b64Decode(keyStr);
|
|
}
|
|
var superSeed = seed;
|
|
if (password) {
|
|
var pwKey = decodeUTF8(password);
|
|
superSeed = new Uint8Array(seed.length + pwKey.length);
|
|
superSeed.set(pwKey);
|
|
superSeed.set(seed, pwKey.length);
|
|
}
|
|
var hash = Nacl.hash(superSeed);
|
|
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
|
|
var secondary = Nacl.hash(signKp.secretKey).subarray(0, Nacl.secretbox.keyLength);
|
|
var seed2 = hash.subarray(32, 64);
|
|
var viewKeyStr = b64Encode(seed2);
|
|
var viewCryptor = createViewCryptor2(viewKeyStr, password);
|
|
return {
|
|
editKeyStr: keyStr,
|
|
viewKeyStr,
|
|
signKey: encodeBase64(signKp.secretKey),
|
|
validateKey: encodeBase64(signKp.publicKey),
|
|
cryptKey: viewCryptor.cryptKey,
|
|
secondaryKey: encodeBase64(secondary),
|
|
chanId: viewCryptor.chanId,
|
|
secondarySignKey: viewCryptor.secondarySignKey,
|
|
secondaryValidateKey: viewCryptor.secondaryValidateKey
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.createEditCryptor2] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
Crypto.createFileCryptor2 = function(keyStr, password) {
|
|
try {
|
|
var seed;
|
|
if (!keyStr) {
|
|
seed = Nacl.randomBytes(18);
|
|
keyStr = b64Encode(seed);
|
|
}
|
|
if (!seed) {
|
|
seed = b64Decode(keyStr);
|
|
}
|
|
var superSeed = seed;
|
|
if (password) {
|
|
var pwKey = decodeUTF8(password);
|
|
superSeed = new Uint8Array(seed.length + pwKey.length);
|
|
superSeed.set(pwKey);
|
|
superSeed.set(seed, pwKey.length);
|
|
}
|
|
var hash = Nacl.hash(superSeed);
|
|
var chanId = hash.subarray(0, 24);
|
|
var cryptKey = hash.subarray(24, 56);
|
|
return {
|
|
fileKeyStr: keyStr,
|
|
cryptKey,
|
|
chanId: b64Encode(chanId)
|
|
};
|
|
} catch (err) {
|
|
console.error("[chainpad-crypto.createFileCryptor2] invalid string supplied");
|
|
throw err;
|
|
}
|
|
};
|
|
var Curve = Crypto.Curve = {};
|
|
var u8_concat = function(A) {
|
|
var length = 0;
|
|
A.forEach((function(a) {
|
|
length += a.length;
|
|
}));
|
|
var total = new Uint8Array(length);
|
|
var offset = 0;
|
|
A.forEach((function(a) {
|
|
total.set(a, offset);
|
|
offset += a.length;
|
|
}));
|
|
return total;
|
|
};
|
|
Curve.encrypt = function(message, secret) {
|
|
var buffer = decodeUTF8(message);
|
|
var nonce = Nacl.randomBytes(24);
|
|
var box = Nacl.box.after(buffer, nonce, secret);
|
|
return encodeBase64(nonce) + "|" + encodeBase64(box);
|
|
};
|
|
Curve.decrypt = function(packed, secret) {
|
|
var unpacked = packed.split("|");
|
|
var nonce = decodeBase64(unpacked[0]);
|
|
var box = decodeBase64(unpacked[1]);
|
|
var message = Nacl.box.open.after(box, nonce, secret);
|
|
if (message === false) {
|
|
return null;
|
|
}
|
|
return encodeUTF8(message);
|
|
};
|
|
Curve.signAndEncrypt = function(msg, cryptKey, signKey) {
|
|
var packed = Curve.encrypt(msg, cryptKey);
|
|
return encodeBase64(Nacl.sign(decodeUTF8(packed), signKey));
|
|
};
|
|
Curve.openSigned = function(msg, cryptKey) {
|
|
var content = decodeBase64(msg).subarray(64);
|
|
return Curve.decrypt(encodeUTF8(content), cryptKey);
|
|
};
|
|
Curve.deriveKeys = function(theirs, mine) {
|
|
try {
|
|
var pub = decodeBase64(theirs);
|
|
var secret = decodeBase64(mine);
|
|
var sharedSecret = Nacl.box.before(pub, secret);
|
|
var salt = decodeUTF8("CryptPad.signingKeyGenerationSalt");
|
|
var hash = Nacl.hash(u8_concat([ salt, sharedSecret ]));
|
|
var signKp = Nacl.sign.keyPair.fromSeed(hash.subarray(0, 32));
|
|
var cryptKey = hash.subarray(32, 64);
|
|
return {
|
|
cryptKey: encodeBase64(cryptKey),
|
|
signKey: encodeBase64(signKp.secretKey),
|
|
validateKey: encodeBase64(signKp.publicKey)
|
|
};
|
|
} catch (e) {
|
|
console.error("invalid keys or other problem deriving keys");
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
};
|
|
Curve.createEncryptor = function(keys) {
|
|
if (!keys || typeof keys !== "object") {
|
|
return void console.error("invalid input for createEncryptor");
|
|
}
|
|
var cryptKey = decodeBase64(keys.cryptKey);
|
|
var signKey = decodeBase64(keys.signKey);
|
|
var validateKey = decodeBase64(keys.validateKey);
|
|
return {
|
|
encrypt: function(msg) {
|
|
return Curve.signAndEncrypt(msg, cryptKey, signKey);
|
|
},
|
|
decrypt: function(packed) {
|
|
return Curve.openSigned(packed, cryptKey, validateKey);
|
|
}
|
|
};
|
|
};
|
|
var u8_slice = function(A, start, end) {
|
|
return new Uint8Array(Array.prototype.slice.call(A, start, end));
|
|
};
|
|
var Mailbox = Crypto.Mailbox = {};
|
|
var asymmetric_encrypt = function(u8_plain, keys) {
|
|
var u8_nonce = Nacl.randomBytes(Nacl.box.nonceLength);
|
|
var u8_cipher = Nacl.box(u8_plain, u8_nonce, keys.their_public, keys.my_private);
|
|
var u8_bundle = u8_concat([ u8_nonce, keys.my_public, u8_cipher ]);
|
|
return u8_bundle;
|
|
};
|
|
var asymmetric_decrypt = function(u8_bundle, keys) {
|
|
var u8_nonce = u8_slice(u8_bundle, 0, Nacl.box.nonceLength);
|
|
var u8_sender_public = u8_slice(u8_bundle, Nacl.box.nonceLength, Nacl.box.nonceLength + Nacl.box.publicKeyLength);
|
|
var u8_cipher = u8_slice(u8_bundle, Nacl.box.nonceLength + Nacl.box.publicKeyLength);
|
|
var u8_plain = Nacl.box.open(u8_cipher, u8_nonce, keys.their_public || u8_sender_public, keys.my_private);
|
|
if (!u8_plain) {
|
|
throw new Error("E_DECRYPTION_FAILURE");
|
|
}
|
|
return {
|
|
content: u8_plain,
|
|
author: u8_sender_public
|
|
};
|
|
};
|
|
var sealSecretLetter = Mailbox.sealSecretLetter = function(plain, keys) {
|
|
var u8_plain = decodeUTF8(plain);
|
|
var u8_letter = asymmetric_encrypt(u8_plain, {
|
|
their_public: keys.their_public,
|
|
my_private: keys.my_private,
|
|
my_public: keys.my_public
|
|
});
|
|
var u8_ephemeral_keypair = keys.ephemeral_keypair || Nacl.box.keyPair();
|
|
var u8_sealed = asymmetric_encrypt(u8_letter, {
|
|
their_public: keys.their_public,
|
|
my_private: u8_ephemeral_keypair.secretKey,
|
|
my_public: u8_ephemeral_keypair.publicKey
|
|
});
|
|
if (keys.signingKey) {
|
|
u8_sealed = Nacl.sign(u8_sealed, keys.signingKey);
|
|
}
|
|
return encodeBase64(u8_sealed);
|
|
};
|
|
Mailbox.openOwnSecretLetter = function(b64_bundle, keys) {
|
|
var u8_bundle = decodeBase64(b64_bundle);
|
|
if (keys.validateKey) {
|
|
u8_bundle = u8_bundle.subarray(64);
|
|
}
|
|
var letter = asymmetric_decrypt(u8_bundle, {
|
|
my_private: keys.ephemeral_private,
|
|
their_public: keys.their_public
|
|
});
|
|
var u8_plain = asymmetric_decrypt(letter.content, {
|
|
my_private: keys.my_private,
|
|
their_public: keys.their_public
|
|
});
|
|
return {
|
|
content: encodeUTF8(u8_plain.content),
|
|
author: encodeBase64(u8_plain.author)
|
|
};
|
|
};
|
|
var openSecretLetter = Mailbox.openSecretLetter = function(b64_bundle, keys) {
|
|
var u8_bundle = decodeBase64(b64_bundle);
|
|
if (keys.validateKey) {
|
|
u8_bundle = u8_bundle.subarray(64);
|
|
}
|
|
var letter = asymmetric_decrypt(u8_bundle, {
|
|
my_private: keys.my_private
|
|
});
|
|
var u8_plain = asymmetric_decrypt(letter.content, {
|
|
my_private: keys.my_private
|
|
});
|
|
return {
|
|
content: encodeUTF8(u8_plain.content),
|
|
author: encodeBase64(u8_plain.author)
|
|
};
|
|
};
|
|
Mailbox.createEncryptor = function(keys) {
|
|
if (!keys || typeof keys !== "object") {
|
|
return void console.error("invalid Mailbox.createEncryptor keys");
|
|
}
|
|
[ "curvePublic", "curvePrivate" ].forEach((function(k) {
|
|
if (typeof keys[k] !== "string") {
|
|
console.log(k);
|
|
throw new Error("Expected key was not present");
|
|
}
|
|
}));
|
|
var u8_my_private = decodeBase64(keys.curvePrivate);
|
|
var u8_my_public = decodeBase64(keys.curvePublic);
|
|
var signingKey = keys.signingKey ? decodeBase64(keys.signingKey) : undefined;
|
|
var validateKey = keys.validateKey ? decodeBase64(keys.validateKey) : undefined;
|
|
return {
|
|
encrypt: function(plain, recipient) {
|
|
var u8_their_public = decodeBase64(recipient);
|
|
try {
|
|
var sealed = sealSecretLetter(plain, {
|
|
signingKey,
|
|
ephemeral_keypair: keys.ephemeral_keypair,
|
|
their_public: u8_their_public,
|
|
my_private: u8_my_private,
|
|
my_public: u8_my_public
|
|
});
|
|
return sealed;
|
|
} catch (e) {
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
},
|
|
decrypt: function(cipher) {
|
|
try {
|
|
return openSecretLetter(cipher, {
|
|
validateKey,
|
|
my_private: u8_my_private
|
|
});
|
|
} catch (e) {
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
}
|
|
};
|
|
};
|
|
var Team = Crypto.Team = {};
|
|
var encryptForTeam = function(plain, keys) {
|
|
var u8_plain = decodeUTF8(plain);
|
|
var u8_inner = asymmetric_encrypt(u8_plain, {
|
|
their_public: keys.team_curve_public,
|
|
my_private: keys.my_curve_private,
|
|
my_public: keys.my_curve_public
|
|
});
|
|
var u8_ephemeral_keypair = Nacl.box.keyPair();
|
|
var u8_outer = asymmetric_encrypt(u8_inner, {
|
|
their_public: keys.team_curve_public,
|
|
my_private: u8_ephemeral_keypair.secretKey,
|
|
my_public: u8_ephemeral_keypair.publicKey
|
|
});
|
|
return encodeBase64(Nacl.sign(u8_outer, keys.team_ed_private));
|
|
};
|
|
var decryptForTeam = function(b64_bundle, keys, skipValidation) {
|
|
var u8_bundle = decodeBase64(b64_bundle);
|
|
var u8_outer;
|
|
if (skipValidation === true) {
|
|
u8_outer = u8_slice(u8_bundle, 64);
|
|
} else {
|
|
u8_outer = Nacl.sign.open(u8_bundle, keys.team_ed_public);
|
|
}
|
|
if (u8_outer === null) {
|
|
throw new Error("E_VALIDATION_FAILURE");
|
|
}
|
|
var inner = asymmetric_decrypt(u8_outer, {
|
|
my_private: keys.team_curve_private
|
|
});
|
|
var u8_plain = asymmetric_decrypt(inner.content, {
|
|
my_private: keys.team_curve_private
|
|
});
|
|
return {
|
|
content: encodeUTF8(u8_plain.content),
|
|
author: encodeBase64(u8_plain.author)
|
|
};
|
|
};
|
|
var team_key_map = {
|
|
teamCurvePublic: "team_curve_public",
|
|
teamCurvePrivate: "team_curve_private",
|
|
myCurvePublic: "my_curve_public",
|
|
myCurvePrivate: "my_curve_private",
|
|
teamEdPublic: "team_ed_public",
|
|
teamEdPrivate: "team_ed_private"
|
|
};
|
|
var team_can_decrypt = function(K) {
|
|
return Boolean(K.team_curve_private && K.team_curve_private.length === Nacl.box.secretKeyLength && K.team_ed_public && K.team_ed_public.length === Nacl.sign.publicKeyLength);
|
|
};
|
|
var team_can_encrypt = function(K) {
|
|
return Boolean(K.my_curve_private && K.my_curve_private.length === Nacl.box.secretKeyLength && K.my_curve_public && K.my_curve_public.length === Nacl.box.publicKeyLength && K.team_curve_public && K.team_curve_public.length === Nacl.box.publicKeyLength && K.team_ed_private && K.team_ed_private.length === Nacl.sign.secretKeyLength);
|
|
};
|
|
var team_validate_own_keys = function(K) {
|
|
return Boolean(K.curvePublic && decodeBase64(K.curvePublic).length === Nacl.box.publicKeyLength && K.curvePrivate && decodeBase64(K.curvePrivate).length === Nacl.box.secretKeyLength);
|
|
};
|
|
var u8_stretch = function(u8) {
|
|
var hashed = Nacl.hash(u8);
|
|
return [ u8_slice(hashed, 0, 32), u8_slice(hashed, 32) ];
|
|
};
|
|
var merge = function(o1, o2) {
|
|
var o3 = JSON.parse(JSON.stringify(o1));
|
|
Object.keys(o2).forEach((function(k) {
|
|
o3[k] = o2[k];
|
|
}));
|
|
return o3;
|
|
};
|
|
var u8_deriveGuestKeys = function(u8_seed2) {
|
|
var stretched = u8_stretch(u8_seed2);
|
|
var teamCurve = Nacl.box.keyPair.fromSecretKey(stretched[0]);
|
|
var u8_channel = u8_slice(stretched[1], 0, 16);
|
|
return {
|
|
channel: encodeHex(u8_channel),
|
|
teamCurvePublic: encodeBase64(teamCurve.publicKey),
|
|
teamCurvePrivate: encodeBase64(teamCurve.secretKey),
|
|
viewKeyStr: Crypto.b64RemoveSlashes(encodeBase64(u8_seed2))
|
|
};
|
|
};
|
|
Team.deriveGuestKeys = function(seed2) {
|
|
return u8_deriveGuestKeys(decodeBase64(Crypto.b64AddSlashes(seed2)));
|
|
};
|
|
Team.createSeed = function() {
|
|
return Crypto.b64AddSlashes(encodeBase64(Nacl.randomBytes(18)));
|
|
};
|
|
Team.deriveMemberKeys = function(seed1, myKeys) {
|
|
var u8_seed1;
|
|
try {
|
|
u8_seed1 = decodeBase64(Crypto.b64AddSlashes(seed1));
|
|
if (u8_seed1.length < 18) {
|
|
throw new Error("INVALID_SEED");
|
|
}
|
|
} catch (err) {
|
|
throw err;
|
|
}
|
|
if (!team_validate_own_keys(myKeys)) {
|
|
throw new Error("INVALID_OWN_KEYS");
|
|
}
|
|
var stretched = u8_stretch(u8_seed1);
|
|
var teamEd = Nacl.sign.keyPair.fromSeed(stretched[0]);
|
|
var guestKeys = u8_deriveGuestKeys(stretched[1]);
|
|
return merge({
|
|
myCurvePublic: myKeys.curvePublic,
|
|
myCurvePrivate: myKeys.curvePrivate,
|
|
teamEdPrivate: encodeBase64(teamEd.secretKey),
|
|
teamEdPublic: encodeBase64(teamEd.publicKey)
|
|
}, guestKeys);
|
|
};
|
|
Team.createEncryptor = function(keys) {
|
|
var u8_keys = {};
|
|
Object.keys(team_key_map).forEach((function(k) {
|
|
if (!keys[k]) {
|
|
return;
|
|
}
|
|
try {
|
|
u8_keys[team_key_map[k]] = decodeBase64(keys[k]);
|
|
} catch (err) {
|
|
console.log(k);
|
|
throw new Error("INVALID_KEY_SUPPLIED");
|
|
}
|
|
}));
|
|
var out = {};
|
|
if (team_can_encrypt(u8_keys)) {
|
|
out.encrypt = function(plain) {
|
|
try {
|
|
return encryptForTeam(plain, u8_keys);
|
|
} catch (e) {
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
if (team_can_decrypt(u8_keys)) {
|
|
out.decrypt = function(cipher, skipValidation) {
|
|
try {
|
|
return decryptForTeam(cipher, u8_keys, skipValidation);
|
|
} catch (e) {
|
|
console.error(e);
|
|
return null;
|
|
}
|
|
};
|
|
}
|
|
if (Object.keys(out).length === 0) {
|
|
throw new Error("INVALID_TEAM_CONFIGURATION");
|
|
}
|
|
return out;
|
|
};
|
|
return Crypto;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireNaclFast());
|
|
} else {
|
|
window.chainpad_crypto = factory(window.nacl);
|
|
}
|
|
})();
|
|
})(crypto);
|
|
return crypto.exports;
|
|
}
|
|
var cryptoExports = requireCrypto();
|
|
var networkConfig$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredNetworkConfig;
|
|
function requireNetworkConfig() {
|
|
if (hasRequiredNetworkConfig) return networkConfig$1.exports;
|
|
hasRequiredNetworkConfig = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (ApiConfig = {}) => {
|
|
var Config = {};
|
|
Config.setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
};
|
|
Config.getWebsocketURL = function(origin) {
|
|
var path = ApiConfig.websocketPath || "/cryptpad_websocket";
|
|
if (/^ws{1,2}:\/\//.test(path)) {
|
|
return path;
|
|
}
|
|
var l = new URL(origin || self?.location?.href);
|
|
if (origin) {
|
|
l.href = origin;
|
|
}
|
|
var protocol = l.protocol.replace(/http/, "ws");
|
|
var host = l.host;
|
|
var url = protocol + "//" + host + path;
|
|
return url;
|
|
};
|
|
return Config;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
}
|
|
})();
|
|
})(networkConfig$1);
|
|
return networkConfig$1.exports;
|
|
}
|
|
var networkConfigExports = requireNetworkConfig();
|
|
var networkConfig = getDefaultExportFromCjs(networkConfigExports);
|
|
var NetworkConfig = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: networkConfig
|
|
}, [ networkConfigExports ]);
|
|
var commonConstants$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCommonConstants;
|
|
function requireCommonConstants() {
|
|
if (hasRequiredCommonConstants) return commonConstants$1.exports;
|
|
hasRequiredCommonConstants = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = function(AppConfig = {}) {
|
|
return {
|
|
setCustomize: data => {
|
|
AppConfig = data.AppConfig;
|
|
},
|
|
userHashKey: "User_hash",
|
|
userNameKey: "User_name",
|
|
blockHashKey: "Block_hash",
|
|
fileHashKey: "FS_hash",
|
|
sessionJWT: "Session_JWT",
|
|
ssoSeed: "SSO_seed",
|
|
displayNameKey: "cryptpad.username",
|
|
oldStorageKey: "CryptPad_RECENTPADS",
|
|
storageKey: "filesData",
|
|
tokenKey: "loginToken",
|
|
prefersDriveRedirectKey: "prefersDriveRedirect",
|
|
isPremiumKey: "isPremiumUser",
|
|
displayPadCreationScreen: "displayPadCreationScreen",
|
|
deprecatedKey: "deprecated",
|
|
MAX_TEAMS_SLOTS: AppConfig.maxTeamsSlots || 5,
|
|
MAX_TEAMS_OWNED: AppConfig.maxOwnedTeams || 5,
|
|
MAX_PREMIUM_TEAMS_SLOTS: Math.max(AppConfig.maxTeamsSlots || 0, AppConfig.maxPremiumTeamsSlots || 0) || 5,
|
|
MAX_PREMIUM_TEAMS_OWNED: Math.max(AppConfig.maxOwnedTeams || 0, AppConfig.maxPremiumTeamsOwned || 0) || 5,
|
|
criticalApps: [ "profile", "settings", "debug", "admin", "support", "notifications", "calendar", "moderation", "oldadmin" ],
|
|
earlyAccessApps: []
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined);
|
|
}
|
|
})();
|
|
})(commonConstants$1);
|
|
return commonConstants$1.exports;
|
|
}
|
|
var commonConstantsExports = requireCommonConstants();
|
|
var commonConstants = getDefaultExportFromCjs(commonConstantsExports);
|
|
var Constants = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: commonConstants
|
|
}, [ commonConstantsExports ]);
|
|
var commonHash = {
|
|
exports: {}
|
|
};
|
|
var commonUtil$1 = {
|
|
exports: {}
|
|
};
|
|
var commonUtil = commonUtil$1.exports;
|
|
var hasRequiredCommonUtil;
|
|
function requireCommonUtil() {
|
|
if (hasRequiredCommonUtil) return commonUtil$1.exports;
|
|
hasRequiredCommonUtil = 1;
|
|
(function(module) {
|
|
(function(window) {
|
|
var Util = {};
|
|
window.atob = window.atob || function(str) {
|
|
return Buffer.from(str, "base64").toString("binary");
|
|
};
|
|
window.btoa = window.btoa || function(str) {
|
|
return Buffer.from(str, "binary").toString("base64");
|
|
};
|
|
Util.slice = function(A, start, end) {
|
|
return Array.prototype.slice.call(A, start, end);
|
|
};
|
|
Util.u8ToBase64 = (u8, cb) => {
|
|
const reader = new FileReader;
|
|
reader.onload = () => {
|
|
let res = reader.result;
|
|
let trim = res.slice(res.indexOf(",") + 1);
|
|
cb(trim);
|
|
};
|
|
reader.readAsDataURL(new Blob([ u8 ]));
|
|
};
|
|
Util.shuffleArray = function(a) {
|
|
for (var i = a.length - 1; i > 0; i--) {
|
|
var j = Math.floor(Math.random() * (i + 1));
|
|
var tmp = a[i];
|
|
a[i] = a[j];
|
|
a[j] = tmp;
|
|
}
|
|
};
|
|
Util.bake = function(f, args) {
|
|
if (typeof args === "undefined") {
|
|
args = [];
|
|
}
|
|
if (!Array.isArray(args)) {
|
|
args = [ args ];
|
|
}
|
|
return function() {
|
|
return f.apply(null, args);
|
|
};
|
|
};
|
|
Util.both = function(pre, post) {
|
|
if (typeof pre !== "function") {
|
|
throw new Error("INVALID_USAGE");
|
|
}
|
|
if (typeof post !== "function") {
|
|
post = function(x) {
|
|
return x;
|
|
};
|
|
}
|
|
return function() {
|
|
pre.apply(null, arguments);
|
|
return post.apply(null, arguments);
|
|
};
|
|
};
|
|
Util.clone = function(o) {
|
|
if (o === undefined || o === null) {
|
|
return o;
|
|
}
|
|
return JSON.parse(JSON.stringify(o));
|
|
};
|
|
Util.serializeError = function(err) {
|
|
if (!(err instanceof Error)) {
|
|
return err;
|
|
}
|
|
var ser = {};
|
|
Object.getOwnPropertyNames(err).forEach((function(key) {
|
|
ser[key] = err[key];
|
|
}));
|
|
return ser;
|
|
};
|
|
Util.tryParse = function(s) {
|
|
try {
|
|
return JSON.parse(s);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
};
|
|
Util.mkAsync = function(f, ms) {
|
|
if (typeof f !== "function") {
|
|
throw new Error("EXPECTED_FUNCTION");
|
|
}
|
|
return function() {
|
|
var args = Array.prototype.slice.call(arguments);
|
|
setTimeout((function() {
|
|
f.apply(null, args);
|
|
}), ms);
|
|
};
|
|
};
|
|
Util.mkEvent = function(once) {
|
|
var handlers = [];
|
|
var fired = false;
|
|
let promiseResolve;
|
|
const promise = new Promise((resolve => {
|
|
promiseResolve = resolve;
|
|
}));
|
|
return {
|
|
reg: function(cb) {
|
|
if (once && fired) {
|
|
return void setTimeout(cb);
|
|
}
|
|
handlers.push(cb);
|
|
},
|
|
unreg: function(cb) {
|
|
if (handlers.indexOf(cb) === -1) {
|
|
return void console.log("event handler was already unregistered");
|
|
}
|
|
handlers.splice(handlers.indexOf(cb), 1);
|
|
},
|
|
fire: function() {
|
|
if (once && fired) {
|
|
return;
|
|
}
|
|
var args = Array.prototype.slice.call(arguments);
|
|
if (!fired) {
|
|
promiseResolve.apply(null, args);
|
|
}
|
|
fired = true;
|
|
handlers.forEach((function(h) {
|
|
h.apply(null, args);
|
|
}));
|
|
},
|
|
promise
|
|
};
|
|
};
|
|
Util.mkTimeout = function(_f, ms) {
|
|
ms = ms || 0;
|
|
var f = Util.once(_f);
|
|
var timeout = setTimeout((function() {
|
|
f("TIMEOUT");
|
|
}), ms);
|
|
return Util.both(f, (function() {
|
|
clearTimeout(timeout);
|
|
}));
|
|
};
|
|
Util.onClickEnter = function($element, handler, cfg) {
|
|
$element.on("click keydown", (function(e) {
|
|
var isClick = e.type === "click";
|
|
var isEnter = e.type === "keydown" && e.which === 13;
|
|
var isSpace = e.type === "keydown" && e.which === 32 && cfg && cfg.space;
|
|
if (!isClick && !isEnter && !isSpace) {
|
|
return;
|
|
}
|
|
if (e.type === "keydown") {
|
|
e.preventDefault();
|
|
}
|
|
handler(e);
|
|
}));
|
|
};
|
|
Util.response = function(errorHandler) {
|
|
var pending = {};
|
|
var timeouts = {};
|
|
if (typeof errorHandler !== "function") {
|
|
errorHandler = function(label) {
|
|
throw new Error(label);
|
|
};
|
|
}
|
|
var clear = function(id) {
|
|
clearTimeout(timeouts[id]);
|
|
delete timeouts[id];
|
|
delete pending[id];
|
|
};
|
|
var expect = function(id, fn, ms) {
|
|
if (typeof id !== "string") {
|
|
errorHandler("EXPECTED_STRING");
|
|
}
|
|
if (typeof fn !== "function") {
|
|
errorHandler("EXPECTED_CALLBACK");
|
|
}
|
|
pending[id] = fn;
|
|
if (typeof ms === "number" && ms) {
|
|
timeouts[id] = setTimeout((function() {
|
|
if (typeof pending[id] === "function") {
|
|
pending[id]("TIMEOUT");
|
|
}
|
|
clear(id);
|
|
}), ms);
|
|
}
|
|
};
|
|
var handle = function(id, args) {
|
|
var fn = pending[id];
|
|
if (typeof fn !== "function") {
|
|
return void errorHandler("MISSING_CALLBACK", {
|
|
id,
|
|
args
|
|
});
|
|
}
|
|
try {
|
|
fn.apply(null, Array.isArray(args) ? args : [ args ]);
|
|
} catch (err) {
|
|
errorHandler("HANDLER_ERROR", {
|
|
error: err,
|
|
id,
|
|
args
|
|
});
|
|
}
|
|
clear(id);
|
|
};
|
|
return {
|
|
clear,
|
|
expected: function(id) {
|
|
return Boolean(pending[id]);
|
|
},
|
|
expectation: function(id) {
|
|
return pending[id];
|
|
},
|
|
expect,
|
|
handle,
|
|
_pending: pending
|
|
};
|
|
};
|
|
Util.inc = function(map, key, val) {
|
|
map[key] = (map[key] || 0) + (typeof val === "number" ? val : 1);
|
|
};
|
|
Util.values = function(obj) {
|
|
return Object.keys(obj).map((function(k) {
|
|
return obj[k];
|
|
}));
|
|
};
|
|
Util.find = function(map, path) {
|
|
var l = path.length;
|
|
for (var i = 0; i < l; i++) {
|
|
if (typeof map[path[i]] === "undefined") {
|
|
return;
|
|
}
|
|
map = map[path[i]];
|
|
}
|
|
return map;
|
|
};
|
|
Util.uid = function() {
|
|
return Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER)).toString(32).replace(/\./g, "");
|
|
};
|
|
Util.guid = function(map) {
|
|
var id = Util.uid();
|
|
if (typeof map[id] === "undefined") {
|
|
return id;
|
|
}
|
|
return Util.guid(map);
|
|
};
|
|
Util.fixHTML = function(str) {
|
|
if (!str) {
|
|
return "";
|
|
}
|
|
return str.replace(/[<>&"']/g, (function(x) {
|
|
return {
|
|
"<": "<",
|
|
">": ">",
|
|
"&": "&",
|
|
'"': """,
|
|
"'": "'"
|
|
}[x];
|
|
}));
|
|
};
|
|
Util.hexToBase64 = function(hex) {
|
|
var hexArray = hex.replace(/\r|\n/g, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ");
|
|
var byteString = String.fromCharCode.apply(null, hexArray);
|
|
return window.btoa(byteString).replace(/\//g, "-").replace(/=+$/, "");
|
|
};
|
|
Util.base64ToHex = function(b64String) {
|
|
var hexArray = [];
|
|
window.atob(b64String.replace(/-/g, "/")).split("").forEach((function(e) {
|
|
var h = e.charCodeAt(0).toString(16);
|
|
if (h.length === 1) {
|
|
h = "0" + h;
|
|
}
|
|
hexArray.push(h);
|
|
}));
|
|
return hexArray.join("");
|
|
};
|
|
Util.uint8ArrayToHex = function(bytes) {
|
|
var hexString = "";
|
|
for (var i = 0; i < bytes.length; i++) {
|
|
if (bytes[i] < 16) {
|
|
hexString += "0";
|
|
}
|
|
hexString += bytes[i].toString(16);
|
|
}
|
|
return hexString;
|
|
};
|
|
Util.hexToUint8Array = function(hexString) {
|
|
var bytes = new Uint8Array(Math.ceil(hexString.length / 2));
|
|
for (var i = 0; i < bytes.length; i++) {
|
|
bytes[i] = parseInt(hexString.substr(i * 2, 2), 16);
|
|
}
|
|
return bytes;
|
|
};
|
|
Util.uint8ArrayJoin = function(AA) {
|
|
var l = 0;
|
|
var i = 0;
|
|
for (;i < AA.length; i++) {
|
|
l += AA[i].length;
|
|
}
|
|
var C = new Uint8Array(l);
|
|
i = 0;
|
|
for (var offset = 0; i < AA.length; i++) {
|
|
C.set(AA[i], offset);
|
|
offset += AA[i].length;
|
|
}
|
|
return C;
|
|
};
|
|
Util.escapeKeyCharacters = function(key) {
|
|
return key && key.replace && key.replace(/\//g, "-");
|
|
};
|
|
Util.unescapeKeyCharacters = function(key) {
|
|
return key.replace(/\-/g, "/");
|
|
};
|
|
Util.deduplicateString = function(array) {
|
|
var a = array.slice();
|
|
for (var i = 0; i < a.length; i++) {
|
|
for (var j = i + 1; j < a.length; j++) {
|
|
if (a[i] === a[j]) {
|
|
a.splice(j--, 1);
|
|
}
|
|
}
|
|
}
|
|
return a;
|
|
};
|
|
Util.fixFileName = function(filename) {
|
|
return filename.replace(/ /g, "-").replace(/[\/\?]/g, "_").replace(/_+/g, "_");
|
|
};
|
|
var oneKilobyte = 1024;
|
|
var oneMegabyte = 1024 * oneKilobyte;
|
|
var oneGigabyte = 1024 * oneMegabyte;
|
|
Util.bytesToGigabytes = function(bytes) {
|
|
return Math.ceil(bytes / oneGigabyte * 100) / 100;
|
|
};
|
|
Util.bytesToMegabytes = function(bytes) {
|
|
return Math.ceil(bytes / oneMegabyte * 100) / 100;
|
|
};
|
|
Util.bytesToKilobytes = function(bytes) {
|
|
return Math.ceil(bytes / oneKilobyte * 100) / 100;
|
|
};
|
|
Util.magnitudeOfBytes = function(bytes) {
|
|
if (bytes >= oneGigabyte) {
|
|
return "GB";
|
|
} else if (bytes >= oneMegabyte) {
|
|
return "MB";
|
|
} else {
|
|
return "KB";
|
|
}
|
|
};
|
|
var getCacheKey = function(src) {
|
|
var _src = src.replace(/(\/)*$/, "");
|
|
var idx = _src.lastIndexOf("/");
|
|
var cacheKey = _src.slice(idx + 1);
|
|
if (!/^[a-f0-9]{48}$/.test(cacheKey)) {
|
|
cacheKey = undefined;
|
|
}
|
|
return cacheKey;
|
|
};
|
|
Util.getBlock = function(src, opt, cb) {
|
|
var CB = Util.once(Util.mkAsync(cb));
|
|
var headers = {};
|
|
if (typeof opt.bearer === "string" && opt.bearer) {
|
|
headers.authorization = `Bearer ${opt.bearer}`;
|
|
}
|
|
fetch(src, {
|
|
method: "GET",
|
|
credentials: "include",
|
|
headers
|
|
}).then((response => {
|
|
if (response.ok) {
|
|
return void CB(void 0, response);
|
|
}
|
|
if (response.status === 401 || response.status === 404) {
|
|
response.json().then((data => {
|
|
CB(response.status, data);
|
|
})).catch((() => {
|
|
CB(response.status);
|
|
}));
|
|
return;
|
|
}
|
|
CB(response.status, response);
|
|
})).catch((error => {
|
|
CB(error);
|
|
}));
|
|
};
|
|
Util.fetchApi = function(origin, type, ignoreCache, cb) {
|
|
const url = new URL(origin);
|
|
url.pathname = `api/${type}`;
|
|
let href = url.href + (ignoreCache ? "?" + +new Date : "");
|
|
if (typeof self !== "undefined" && self.crypto) {
|
|
fetch(href).then((res => {
|
|
if (!res.ok) {
|
|
throw new Error(`Fetch error: ${res.status}`);
|
|
}
|
|
return res.text();
|
|
})).then((body => {
|
|
cb(JSON.parse(body.slice(27, -5)));
|
|
})).catch((err => {
|
|
console.error(err.message);
|
|
cb({});
|
|
}));
|
|
} else if (typeof commonjsRequire !== "undefined") {
|
|
const H = url.protocol === "http:" ? require("node:http") : require("node:https");
|
|
H.get(url.href, (res => {
|
|
let body = "";
|
|
res.on("data", (data => {
|
|
body += data;
|
|
}));
|
|
res.on("end", (() => {
|
|
try {
|
|
cb(JSON.parse(body.slice(27, -5)));
|
|
} catch (e) {
|
|
console.error(e);
|
|
cb({});
|
|
}
|
|
}));
|
|
}));
|
|
}
|
|
};
|
|
Util.fetch = function(src, cb, progress, cache) {
|
|
var CB = Util.once(Util.mkAsync(cb));
|
|
var cacheKey = getCacheKey(src);
|
|
var getBlobCache = function(id, cb) {
|
|
if (!cache || typeof cache.getBlobCache !== "function") {
|
|
return void cb("EINVAL");
|
|
}
|
|
cache.getBlobCache(id, cb);
|
|
};
|
|
var setBlobCache = function(id, u8, cb) {
|
|
if (!cache || typeof cache.setBlobCache !== "function") {
|
|
return void cb("EINVAL");
|
|
}
|
|
cache.setBlobCache(id, u8, cb);
|
|
};
|
|
var xhr;
|
|
var fetch = function() {
|
|
xhr = new XMLHttpRequest;
|
|
xhr.open("GET", src, true);
|
|
if (progress) {
|
|
xhr.addEventListener("progress", (function(evt) {
|
|
if (evt.lengthComputable) {
|
|
var percentComplete = evt.loaded / evt.total;
|
|
progress(percentComplete);
|
|
}
|
|
}), false);
|
|
}
|
|
xhr.responseType = "arraybuffer";
|
|
xhr.onerror = function(err) {
|
|
CB(err);
|
|
};
|
|
xhr.onload = function() {
|
|
if (/^4/.test("" + this.status)) {
|
|
return CB("XHR_ERROR");
|
|
}
|
|
var arrayBuffer = xhr.response;
|
|
if (arrayBuffer) {
|
|
var u8 = new Uint8Array(arrayBuffer);
|
|
if (cacheKey) {
|
|
return void setBlobCache(cacheKey, u8, (function() {
|
|
CB(null, u8);
|
|
}));
|
|
}
|
|
return void CB(void 0, u8);
|
|
}
|
|
CB("ENOENT");
|
|
};
|
|
xhr.send(null);
|
|
};
|
|
if (!cacheKey) {
|
|
return void fetch();
|
|
}
|
|
getBlobCache(cacheKey, (function(err, u8) {
|
|
if (err || !u8) {
|
|
return void fetch();
|
|
}
|
|
CB(void 0, u8);
|
|
}));
|
|
return {
|
|
cancel: function() {
|
|
if (xhr && xhr.abort) {
|
|
xhr.abort();
|
|
}
|
|
}
|
|
};
|
|
};
|
|
Util.dataURIToBlob = function(dataURI) {
|
|
var byteString = atob(dataURI.split(",")[1]);
|
|
var mimeString = dataURI.split(",")[0].split(":")[1].split(";")[0];
|
|
var ab = new ArrayBuffer(byteString.length);
|
|
var ia = new Uint8Array(ab);
|
|
for (var i = 0; i < byteString.length; i++) {
|
|
ia[i] = byteString.charCodeAt(i);
|
|
}
|
|
var bb = new Blob([ ab ], {
|
|
type: mimeString
|
|
});
|
|
return bb;
|
|
};
|
|
Util.throttle = function(f, ms) {
|
|
var last = 0;
|
|
var to;
|
|
var args;
|
|
var defer = function(delay) {
|
|
to = setTimeout((function() {
|
|
to = undefined;
|
|
var now = +new Date;
|
|
var diff = now - last;
|
|
if (diff < ms) {
|
|
return void defer(ms - diff);
|
|
}
|
|
f.apply(null, args);
|
|
}), delay);
|
|
};
|
|
var g = function() {
|
|
last = +new Date;
|
|
args = Util.slice(arguments);
|
|
if (to) {
|
|
return;
|
|
}
|
|
defer(ms);
|
|
};
|
|
g.clear = function() {
|
|
clearTimeout(to);
|
|
to = undefined;
|
|
};
|
|
return g;
|
|
};
|
|
Util.notAgainForAnother = function(f, t) {
|
|
if (typeof f !== "function" || typeof t !== "number") {
|
|
throw new Error("invalid inputs");
|
|
}
|
|
var last = null;
|
|
return function() {
|
|
var now = +new Date;
|
|
if (last && now <= last + t) {
|
|
return t - (now - last);
|
|
}
|
|
last = now;
|
|
f.apply(null, Util.slice(arguments));
|
|
return null;
|
|
};
|
|
};
|
|
Util.createRandomInteger = function() {
|
|
return Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
};
|
|
Util.noop = function() {};
|
|
Util.once = function(f, g) {
|
|
return function() {
|
|
if (!f) {
|
|
return;
|
|
}
|
|
f.apply(this, Array.prototype.slice.call(arguments));
|
|
f = g;
|
|
};
|
|
};
|
|
Util.blobToImage = function(blob, cb) {
|
|
var reader = new FileReader;
|
|
reader.onloadend = function() {
|
|
cb(reader.result);
|
|
};
|
|
reader.readAsDataURL(blob);
|
|
};
|
|
Util.blobURLToImage = function(url, cb) {
|
|
var xhr = new XMLHttpRequest;
|
|
xhr.onload = function() {
|
|
var reader = new FileReader;
|
|
reader.onloadend = function() {
|
|
cb(reader.result);
|
|
};
|
|
reader.readAsDataURL(xhr.response);
|
|
};
|
|
xhr.open("GET", url);
|
|
xhr.responseType = "blob";
|
|
xhr.send();
|
|
};
|
|
Util.isObject = function(o) {
|
|
return typeof o === "object" && Object.prototype.toString.call(o) === "[object Object]";
|
|
};
|
|
Util.isCircular = function(o) {
|
|
try {
|
|
JSON.stringify(o);
|
|
return false;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
};
|
|
Util.extend = function(a, b) {
|
|
if (!Util.isObject(a) || !Util.isObject(b)) {
|
|
return void console.log("Extend only works with 2 objects");
|
|
}
|
|
if (Util.isCircular(b)) {
|
|
return void console.log("Extend doesn't accept circular objects");
|
|
}
|
|
for (var k in b) {
|
|
if (Util.isObject(b[k])) {
|
|
a[k] = Util.isObject(a[k]) ? a[k] : {};
|
|
Util.extend(a[k], b[k]);
|
|
continue;
|
|
}
|
|
if (Array.isArray(b[k])) {
|
|
a[k] = b[k].slice();
|
|
continue;
|
|
}
|
|
a[k] = b[k];
|
|
}
|
|
};
|
|
Util.isChecked = function(el) {
|
|
if (!el) {
|
|
return false;
|
|
}
|
|
if (typeof el.tagName !== "undefined") {
|
|
return Boolean(el.checked);
|
|
}
|
|
if (typeof el.prop === "function") {
|
|
return Boolean(el.prop("checked"));
|
|
}
|
|
return false;
|
|
};
|
|
Util.hexToRGB = function(hex) {
|
|
var h = hex.replace(/^#/, "");
|
|
return [ parseInt(h.slice(0, 2), 16), parseInt(h.slice(2, 4), 16), parseInt(h.slice(4, 6), 16) ];
|
|
};
|
|
Util.rgbToHex = function(rgb) {
|
|
return `#${rgb.match(/^rgb\((\d+),\s*(\d+),\s*(\d+)\)$/).slice(1).map((n => parseInt(n, 10).toString(16).padStart(2, "0"))).join("")}`;
|
|
};
|
|
Util.isSmallScreen = function() {
|
|
return window.innerHeight < 800 || window.innerWidth < 800;
|
|
};
|
|
Util.stripTags = function(text) {
|
|
var div = document.createElement("div");
|
|
div.innerHTML = text;
|
|
return div.innerText;
|
|
};
|
|
Util.parseFilename = function(filename) {
|
|
if (!filename || !filename.trim()) {
|
|
return {};
|
|
}
|
|
var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(filename) || [];
|
|
return {
|
|
name: parsedName[1],
|
|
ext: parsedName[2]
|
|
};
|
|
};
|
|
Util.isPlainTextFile = function(type, name) {
|
|
if (type && type.indexOf("text/") === 0) {
|
|
return true;
|
|
}
|
|
var parsedName = Util.parseFilename(name);
|
|
if (!type && name && !parsedName.ext) {
|
|
return true;
|
|
}
|
|
if (type === "application/x-javascript") {
|
|
return true;
|
|
}
|
|
if (type === "application/xml") {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
Util.isSpreadsheet = function(type, name) {
|
|
return type && (type === "application/vnd.oasis.opendocument.spreadsheet" || type === "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") || name && (name.endsWith(".xlsx") || name.endsWith(".ods"));
|
|
};
|
|
Util.isOfficeDoc = function(type, name) {
|
|
return type && (type === "application/vnd.oasis.opendocument.text" || type === "application/vnd.openxmlformats-officedocument.wordprocessingml.document") || name && (name.endsWith(".docx") || name.endsWith(".odt"));
|
|
};
|
|
Util.isPresentation = function(type, name) {
|
|
return type && (type === "application/vnd.oasis.opendocument.presentation" || type === "application/vnd.openxmlformats-officedocument.presentationml.presentation") || name && (name.endsWith(".pptx") || name.endsWith(".odp"));
|
|
};
|
|
Util.isValidURL = function(str) {
|
|
var pattern = new RegExp("^(https?:\\/\\/)" + "((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|" + "((\\d{1,3}\\.){3}\\d{1,3}))" + "(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*" + "(\\?[;&a-z\\d%_.~+=-]*)?");
|
|
return !!pattern.test(str);
|
|
};
|
|
var emoji_patt = /([\uD800-\uDBFF][\uDC00-\uDFFF])/;
|
|
var isEmoji = function(str) {
|
|
return emoji_patt.test(str);
|
|
};
|
|
var emojiStringToArray = function(str) {
|
|
var split = str.split(emoji_patt);
|
|
var arr = [];
|
|
for (var i = 0; i < split.length; i++) {
|
|
var char = split[i];
|
|
if (char !== "") {
|
|
arr.push(char);
|
|
}
|
|
}
|
|
return arr;
|
|
};
|
|
Util.getFirstCharacter = function(str) {
|
|
if (!str || !str.trim()) {
|
|
return "?";
|
|
}
|
|
var emojis = emojiStringToArray(str);
|
|
return isEmoji(emojis[0]) ? emojis[0] : str[0];
|
|
};
|
|
Util.getRandomColor = function(light) {
|
|
var getColor = function() {
|
|
if (light) {
|
|
return Math.floor(Math.random() * 156) + 70;
|
|
}
|
|
return Math.floor(Math.random() * 200) + 25;
|
|
};
|
|
return "#" + getColor().toString(16) + getColor().toString(16) + getColor().toString(16);
|
|
};
|
|
Util.checkRestrictedApp = function(app, AppConfig, earlyTypes, plan, loggedIn) {
|
|
if (Array.isArray(earlyTypes) && earlyTypes.includes(app) && !AppConfig.enableEarlyAccess) {
|
|
return -2;
|
|
}
|
|
var premiumTypes = AppConfig.premiumTypes;
|
|
if (!Array.isArray(premiumTypes) || !premiumTypes.includes(app)) {
|
|
return 2;
|
|
}
|
|
if (!loggedIn) {
|
|
return -1;
|
|
}
|
|
return plan ? 1 : 0;
|
|
};
|
|
var supportsSharedArrayBuffers = function() {
|
|
try {
|
|
return Object.prototype.toString.call(new window.WebAssembly.Memory({
|
|
shared: true,
|
|
initial: 0,
|
|
maximum: 0
|
|
}).buffer) === "[object SharedArrayBuffer]";
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
return false;
|
|
};
|
|
Util.supportsWasm = function() {
|
|
return !(typeof Atomics === "undefined" || !supportsSharedArrayBuffers() || typeof WebAssembly === "undefined");
|
|
};
|
|
Util.getKeysArray = function(length) {
|
|
return [ ...Array(length).keys() ];
|
|
};
|
|
Util.getVersionFromUrlArgs = urlArgs => {
|
|
let arr = /ver=([0-9.]+)(-[0-9]*)?/.exec(urlArgs);
|
|
let ver = Array.isArray(arr) && arr[1];
|
|
return ver || undefined;
|
|
};
|
|
Util.Saferphore = {
|
|
create: resourceCount => {
|
|
var queue = [];
|
|
var check;
|
|
var mkRa = function() {
|
|
var outerCalled = 0;
|
|
return function(func) {
|
|
if (outerCalled++) {
|
|
throw new Error("returnAfter() called multiple times");
|
|
}
|
|
var called = 0;
|
|
return function() {
|
|
if (called++) {
|
|
throw new Error("returnAfter wrapped callback called multiple times");
|
|
}
|
|
if (func) {
|
|
func.apply(null, arguments);
|
|
}
|
|
resourceCount++;
|
|
check();
|
|
};
|
|
};
|
|
};
|
|
check = function() {
|
|
if (resourceCount < 0) {
|
|
throw new Error("(resourceCount < 0) should never happen");
|
|
}
|
|
if (resourceCount === 0 || queue.length === 0) {
|
|
return;
|
|
}
|
|
resourceCount--;
|
|
queue.shift()(mkRa());
|
|
};
|
|
return {
|
|
take: function(func) {
|
|
queue.push(func);
|
|
check();
|
|
}
|
|
};
|
|
}
|
|
};
|
|
if (module.exports) {
|
|
module.exports = Util;
|
|
} else {
|
|
window.CryptPad_Util = Util;
|
|
}
|
|
})(typeof self !== "undefined" ? self : commonUtil);
|
|
})(commonUtil$1);
|
|
return commonUtil$1.exports;
|
|
}
|
|
var commonSigningKeys = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCommonSigningKeys;
|
|
function requireCommonSigningKeys() {
|
|
if (hasRequiredCommonSigningKeys) return commonSigningKeys.exports;
|
|
hasRequiredCommonSigningKeys = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function() {
|
|
var Keys = {};
|
|
var unescape = function(s) {
|
|
return s.replace(/-/g, "/");
|
|
};
|
|
var parseNewUser = function(userString) {
|
|
if (!/^\[.*?@.*\]$/.test(userString)) {
|
|
return;
|
|
}
|
|
var temp = userString.slice(1, -1);
|
|
var domain, username, pubkey;
|
|
temp = temp.replace(/\/([a-zA-Z0-9+-]{43}=)$/, (function(all, k) {
|
|
pubkey = unescape(k);
|
|
return "";
|
|
}));
|
|
if (!pubkey) {
|
|
return;
|
|
}
|
|
var index = temp.lastIndexOf("@");
|
|
if (index < 1) {
|
|
return;
|
|
}
|
|
domain = temp.slice(index + 1);
|
|
username = temp.slice(0, index);
|
|
return {
|
|
domain,
|
|
user: username,
|
|
pubkey
|
|
};
|
|
};
|
|
var isValidUser = function(parsed) {
|
|
if (!parsed) {
|
|
return;
|
|
}
|
|
if (!(parsed.domain && parsed.user && parsed.pubkey)) {
|
|
return;
|
|
}
|
|
return true;
|
|
};
|
|
Keys.parseUser = function(user) {
|
|
var parsed = parseNewUser(user);
|
|
if (isValidUser(parsed)) {
|
|
return parsed;
|
|
}
|
|
var domain, username, pubkey;
|
|
user.replace(/^https*:\/\/([^\/]+)\/user\/#\/1\/([^\/]+)\/([a-zA-Z0-9+-]{43}=)$/, (function(a, d, u, k) {
|
|
domain = d;
|
|
username = u;
|
|
pubkey = unescape(k);
|
|
return "";
|
|
}));
|
|
if (!domain) {
|
|
throw new Error("Could not parse user id [" + user + "]");
|
|
}
|
|
return {
|
|
domain,
|
|
user: username,
|
|
pubkey
|
|
};
|
|
};
|
|
Keys.serialize = function(origin, username, pubkey) {
|
|
return "[" + username + "@" + origin.replace(/https*:\/\//, "") + "/" + pubkey.replace(/\//g, "-") + "]";
|
|
};
|
|
Keys.canonicalize = function(input) {
|
|
if (typeof input !== "string") {
|
|
return;
|
|
}
|
|
if (input.length === 44) {
|
|
return unescape(input);
|
|
}
|
|
try {
|
|
return Keys.parseUser(input).pubkey;
|
|
} catch (err) {
|
|
return;
|
|
}
|
|
};
|
|
return Keys;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
}
|
|
})();
|
|
})(commonSigningKeys);
|
|
return commonSigningKeys.exports;
|
|
}
|
|
var hasRequiredCommonHash;
|
|
function requireCommonHash() {
|
|
if (hasRequiredCommonHash) return commonHash.exports;
|
|
hasRequiredCommonHash = 1;
|
|
(function(module) {
|
|
(function(window) {
|
|
var factory = function(Util, Crypto, Keys, Nacl) {
|
|
var Hash = window.CryptPad_Hash = {};
|
|
var uint8ArrayToHex = Util.uint8ArrayToHex;
|
|
var hexToBase64 = Util.hexToBase64;
|
|
var base64ToHex = Util.base64ToHex;
|
|
Hash.encodeBase64 = Nacl.util.encodeBase64;
|
|
Hash.decodeBase64 = Nacl.util.decodeBase64;
|
|
Hash.hashChannelList = function(list) {
|
|
return Nacl.util.encodeBase64(Nacl.hash(Nacl.util.decodeUTF8(JSON.stringify(list))));
|
|
};
|
|
Hash.generateSignPair = function() {
|
|
var ed = Nacl.sign.keyPair();
|
|
var makeSafe = function(key) {
|
|
return Crypto.b64RemoveSlashes(key).replace(/=+$/g, "");
|
|
};
|
|
return {
|
|
validateKey: Hash.encodeBase64(ed.publicKey),
|
|
signKey: Hash.encodeBase64(ed.secretKey),
|
|
safeValidateKey: makeSafe(Hash.encodeBase64(ed.publicKey)),
|
|
safeSignKey: makeSafe(Hash.encodeBase64(ed.secretKey))
|
|
};
|
|
};
|
|
Hash.getSignPublicFromPrivate = function(edPrivateSafeStr) {
|
|
var edPrivateStr = Crypto.b64AddSlashes(edPrivateSafeStr);
|
|
var privateKey = Nacl.util.decodeBase64(edPrivateStr);
|
|
var keyPair = Nacl.sign.keyPair.fromSecretKey(privateKey);
|
|
return Nacl.util.encodeBase64(keyPair.publicKey);
|
|
};
|
|
Hash.getCurvePublicFromPrivate = function(curvePrivateSafeStr) {
|
|
var curvePrivateStr = Crypto.b64AddSlashes(curvePrivateSafeStr);
|
|
var privateKey = Nacl.util.decodeBase64(curvePrivateStr);
|
|
var keyPair = Nacl.box.keyPair.fromSecretKey(privateKey);
|
|
return Nacl.util.encodeBase64(keyPair.publicKey);
|
|
};
|
|
var getEditHashFromKeys = Hash.getEditHashFromKeys = function(secret) {
|
|
var version = secret.version;
|
|
var data = secret.keys;
|
|
if (version === 0) {
|
|
return secret.channel + secret.key;
|
|
}
|
|
if (version === 1) {
|
|
if (!data.editKeyStr) {
|
|
return;
|
|
}
|
|
return "/1/edit/" + hexToBase64(secret.channel) + "/" + Crypto.b64RemoveSlashes(data.editKeyStr) + "/";
|
|
}
|
|
if (version === 2) {
|
|
if (!data.editKeyStr) {
|
|
return;
|
|
}
|
|
var pass = secret.password ? "p/" : "";
|
|
return "/2/" + secret.type + "/edit/" + Crypto.b64RemoveSlashes(data.editKeyStr) + "/" + pass;
|
|
}
|
|
};
|
|
var getViewHashFromKeys = Hash.getViewHashFromKeys = function(secret) {
|
|
var version = secret.version;
|
|
var data = secret.keys;
|
|
if (version === 0) {
|
|
return;
|
|
}
|
|
if (version === 1) {
|
|
if (!data.viewKeyStr) {
|
|
return;
|
|
}
|
|
return "/1/view/" + hexToBase64(secret.channel) + "/" + Crypto.b64RemoveSlashes(data.viewKeyStr) + "/";
|
|
}
|
|
if (version === 2) {
|
|
if (!data.viewKeyStr) {
|
|
return;
|
|
}
|
|
var pass = secret.password ? "p/" : "";
|
|
return "/2/" + secret.type + "/view/" + Crypto.b64RemoveSlashes(data.viewKeyStr) + "/" + pass;
|
|
}
|
|
};
|
|
Hash.getHiddenHashFromKeys = function(type, secret, opts) {
|
|
opts = opts || {};
|
|
var canEdit = secret.keys && secret.keys.editKeyStr || secret.key;
|
|
var mode = !opts.view && canEdit ? "edit/" : "view/";
|
|
var pass = secret.password ? "p/" : "";
|
|
if (secret.keys && secret.keys.fileKeyStr) {
|
|
mode = "";
|
|
}
|
|
var hash = "/3/" + type + "/" + mode + secret.channel + "/" + pass;
|
|
var hashData = Hash.parseTypeHash(type, hash);
|
|
if (hashData && hashData.getHash) {
|
|
return hashData.getHash(opts || {});
|
|
}
|
|
return hash;
|
|
};
|
|
var getFileHashFromKeys = Hash.getFileHashFromKeys = function(secret) {
|
|
var version = secret.version;
|
|
var data = secret.keys;
|
|
if (version === 0) {
|
|
return;
|
|
}
|
|
if (version === 1) {
|
|
return "/1/" + hexToBase64(secret.channel) + "/" + Crypto.b64RemoveSlashes(data.fileKeyStr) + "/";
|
|
}
|
|
if (version === 2) {
|
|
if (!data.fileKeyStr) {
|
|
return;
|
|
}
|
|
var pass = secret.password ? "p/" : "";
|
|
return "/2/" + secret.type + "/" + Crypto.b64RemoveSlashes(data.fileKeyStr) + "/" + pass;
|
|
}
|
|
};
|
|
Hash.getPublicSigningKeyString = Keys.serialize;
|
|
var fixDuplicateSlashes = function(s) {
|
|
return s.replace(/\/+/g, "/");
|
|
};
|
|
Hash.ephemeralChannelLength = 34;
|
|
Hash.createChannelId = function(ephemeral) {
|
|
var id = uint8ArrayToHex(Crypto.Nacl.randomBytes(ephemeral ? 17 : 16));
|
|
if ([ 32, 34 ].indexOf(id.length) === -1 || /[^a-f0-9]/.test(id)) {
|
|
throw new Error("channel ids must consist of 32 hex characters");
|
|
}
|
|
return id;
|
|
};
|
|
Hash.getChannelIdFromKey = function(publicKey) {
|
|
if (!publicKey) {
|
|
return;
|
|
}
|
|
return uint8ArrayToHex(Hash.decodeBase64(publicKey).subarray(0, 16));
|
|
};
|
|
Hash.getBoxPublicFromSecret = function(priv) {
|
|
if (!priv) {
|
|
return;
|
|
}
|
|
var u8_priv = Hash.decodeBase64(priv);
|
|
var pair = Nacl.box.keyPair.fromSecretKey(u8_priv);
|
|
return Hash.encodeBase64(pair.publicKey);
|
|
};
|
|
Hash.checkBoxKeyPair = function(priv, pub) {
|
|
if (!pub || !priv) {
|
|
return false;
|
|
}
|
|
var u8_priv = Hash.decodeBase64(priv);
|
|
var pair = Nacl.box.keyPair.fromSecretKey(u8_priv);
|
|
return pub === Hash.encodeBase64(pair.publicKey);
|
|
};
|
|
Hash.createRandomHash = function(type, password) {
|
|
var cryptor;
|
|
if (type === "file") {
|
|
cryptor = Crypto.createFileCryptor2(void 0, password);
|
|
return getFileHashFromKeys({
|
|
password: Boolean(password),
|
|
version: 2,
|
|
type,
|
|
keys: cryptor
|
|
});
|
|
}
|
|
cryptor = Crypto.createEditCryptor2(void 0, void 0, password);
|
|
return getEditHashFromKeys({
|
|
password: Boolean(password),
|
|
version: 2,
|
|
type,
|
|
keys: cryptor
|
|
});
|
|
};
|
|
var getLoginOpts = function(hashArr) {
|
|
var k;
|
|
hashArr.some((function(data) {
|
|
if (/^login=/.test(data)) {
|
|
k = data.slice(6);
|
|
return true;
|
|
}
|
|
}));
|
|
return k || "";
|
|
};
|
|
var getNewPadOpts = function(hashArr) {
|
|
var k;
|
|
hashArr.some((function(data) {
|
|
if (/^newpad=/.test(data)) {
|
|
k = data.slice(7);
|
|
return true;
|
|
}
|
|
}));
|
|
return k || "";
|
|
};
|
|
var getVersionHash = function(hashArr) {
|
|
var k;
|
|
hashArr.some((function(data) {
|
|
if (/^hash=/.test(data)) {
|
|
k = data.slice(5);
|
|
return true;
|
|
}
|
|
}));
|
|
return k ? Crypto.b64AddSlashes(k) : "";
|
|
};
|
|
var getAuditorKey = function(hashArr) {
|
|
var k;
|
|
hashArr.some((function(data) {
|
|
if (/^auditor=/.test(data)) {
|
|
k = data.slice(8);
|
|
return true;
|
|
}
|
|
}));
|
|
return k ? Crypto.b64AddSlashes(k) : "";
|
|
};
|
|
var getOwnerKey = function(hashArr) {
|
|
var k;
|
|
hashArr.some((function(data) {
|
|
if (data.length === 86) {
|
|
k = data;
|
|
return true;
|
|
}
|
|
}));
|
|
return k;
|
|
};
|
|
var parseTypeHash = Hash.parseTypeHash = function(type, hash) {
|
|
if (!hash) {
|
|
return;
|
|
}
|
|
var options = [];
|
|
var parsed = {};
|
|
var hashArr = fixDuplicateSlashes(hash).split("/");
|
|
var addOptions = function() {
|
|
parsed.password = options.indexOf("p") !== -1;
|
|
parsed.present = options.indexOf("present") !== -1;
|
|
parsed.embed = options.indexOf("embed") !== -1;
|
|
parsed.versionHash = getVersionHash(options);
|
|
parsed.auditorKey = getAuditorKey(options);
|
|
parsed.newPadOpts = getNewPadOpts(options);
|
|
parsed.loginOpts = getLoginOpts(options);
|
|
parsed.ownerKey = getOwnerKey(options);
|
|
};
|
|
if (hashArr[1] && hashArr[1] === "4") {
|
|
parsed.getHash = function(opts) {
|
|
if (!opts || !Object.keys(opts).length) {
|
|
return "";
|
|
}
|
|
var hash = "/4/" + type + "/";
|
|
if (opts.newPadOpts) {
|
|
hash += "newpad=" + opts.newPadOpts + "/";
|
|
}
|
|
if (opts.loginOpts) {
|
|
hash += "login=" + opts.loginOpts + "/";
|
|
}
|
|
return hash;
|
|
};
|
|
parsed.getOptions = function() {
|
|
var options = {};
|
|
if (parsed.newPadOpts) {
|
|
options.newPadOpts = parsed.newPadOpts;
|
|
}
|
|
if (parsed.loginOpts) {
|
|
options.loginOpts = parsed.loginOpts;
|
|
}
|
|
return options;
|
|
};
|
|
parsed.version = 4;
|
|
parsed.app = hashArr[2];
|
|
options = hashArr.slice(3);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
if ([ "media", "file", "user", "invite" ].indexOf(type) === -1) {
|
|
parsed.type = "pad";
|
|
parsed.getHash = function() {
|
|
return hash;
|
|
};
|
|
parsed.getOptions = function() {
|
|
return {
|
|
embed: parsed.embed,
|
|
present: parsed.present,
|
|
ownerKey: parsed.ownerKey,
|
|
versionHash: parsed.versionHash,
|
|
auditorKey: parsed.auditorKey,
|
|
newPadOpts: parsed.newPadOpts,
|
|
loginOpts: parsed.loginOpts,
|
|
password: parsed.password
|
|
};
|
|
};
|
|
if (hash.slice(0, 1) !== "/" && hash.length >= 56) {
|
|
parsed.channel = hash.slice(0, 32);
|
|
parsed.key = hash.slice(32, 56);
|
|
parsed.version = 0;
|
|
return parsed;
|
|
}
|
|
parsed.getHash = function(opts) {
|
|
var hash = hashArr.slice(0, 5).join("/") + "/";
|
|
var owner = typeof opts.ownerKey !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
|
if (owner) {
|
|
hash += owner + "/";
|
|
}
|
|
if (parsed.password || opts.password) {
|
|
hash += "p/";
|
|
}
|
|
if (opts.embed) {
|
|
hash += "embed/";
|
|
}
|
|
if (opts.present) {
|
|
hash += "present/";
|
|
}
|
|
var versionHash = typeof opts.versionHash !== "undefined" ? opts.versionHash : parsed.versionHash;
|
|
if (versionHash) {
|
|
hash += "hash=" + Crypto.b64RemoveSlashes(versionHash) + "/";
|
|
}
|
|
var auditorKey = typeof opts.auditorKey !== "undefined" ? opts.auditorKey : parsed.auditorKey;
|
|
if (auditorKey) {
|
|
hash += "auditor=" + Crypto.b64RemoveSlashes(auditorKey) + "/";
|
|
}
|
|
if (opts.newPadOpts) {
|
|
hash += "newpad=" + opts.newPadOpts + "/";
|
|
}
|
|
if (opts.loginOpts) {
|
|
hash += "login=" + opts.loginOpts + "/";
|
|
}
|
|
return hash;
|
|
};
|
|
if (hashArr[1] && hashArr[1] === "1") {
|
|
parsed.version = 1;
|
|
parsed.mode = hashArr[2];
|
|
parsed.channel = hashArr[3];
|
|
parsed.key = Crypto.b64AddSlashes(hashArr[4]);
|
|
options = hashArr.slice(5);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
if (hashArr[1] && hashArr[1] === "2") {
|
|
parsed.version = 2;
|
|
parsed.app = hashArr[2];
|
|
parsed.mode = hashArr[3];
|
|
parsed.key = hashArr[4];
|
|
options = hashArr.slice(5);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
if (hashArr[1] && hashArr[1] === "3") {
|
|
parsed.version = 3;
|
|
parsed.app = hashArr[2];
|
|
parsed.mode = hashArr[3];
|
|
parsed.channel = hashArr[4];
|
|
options = hashArr.slice(5);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
return parsed;
|
|
}
|
|
parsed.getHash = function() {
|
|
return hashArr.join("/");
|
|
};
|
|
if ([ "media", "file" ].indexOf(type) !== -1) {
|
|
parsed.type = "file";
|
|
parsed.getOptions = function() {
|
|
return {
|
|
embed: parsed.embed,
|
|
present: parsed.present,
|
|
ownerKey: parsed.ownerKey,
|
|
newPadOpts: parsed.newPadOpts,
|
|
loginOpts: parsed.loginOpts,
|
|
password: parsed.password
|
|
};
|
|
};
|
|
parsed.getHash = function(opts) {
|
|
var hash = hashArr.slice(0, 4).join("/") + "/";
|
|
var owner = typeof opts.ownerKey !== "undefined" ? opts.ownerKey : parsed.ownerKey;
|
|
if (owner) {
|
|
hash += owner + "/";
|
|
}
|
|
if (parsed.password || opts.password) {
|
|
hash += "p/";
|
|
}
|
|
if (opts.embed) {
|
|
hash += "embed/";
|
|
}
|
|
if (opts.present) {
|
|
hash += "present/";
|
|
}
|
|
if (opts.newPadOpts) {
|
|
hash += "newpad=" + opts.newPadOpts + "/";
|
|
}
|
|
if (opts.loginOpts) {
|
|
hash += "login=" + opts.loginOpts + "/";
|
|
}
|
|
return hash;
|
|
};
|
|
if (hashArr[1] && hashArr[1] === "1") {
|
|
parsed.version = 1;
|
|
parsed.channel = hashArr[2].replace(/-/g, "/");
|
|
parsed.key = hashArr[3].replace(/-/g, "/");
|
|
options = hashArr.slice(4);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
if (hashArr[1] && hashArr[1] === "2") {
|
|
parsed.version = 2;
|
|
parsed.app = hashArr[2];
|
|
parsed.key = hashArr[3];
|
|
options = hashArr.slice(4);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
if (hashArr[1] && hashArr[1] === "3") {
|
|
parsed.version = 3;
|
|
parsed.app = hashArr[2];
|
|
parsed.channel = hashArr[3];
|
|
options = hashArr.slice(4);
|
|
addOptions();
|
|
return parsed;
|
|
}
|
|
return parsed;
|
|
}
|
|
if ([ "user" ].indexOf(type) !== -1) {
|
|
parsed.type = "user";
|
|
if (hashArr[1] && hashArr[1] === "1") {
|
|
parsed.version = 1;
|
|
parsed.user = hashArr[2];
|
|
parsed.pubkey = hashArr[3].replace(/-/g, "/");
|
|
return parsed;
|
|
}
|
|
return parsed;
|
|
}
|
|
if ([ "invite" ].indexOf(type) !== -1) {
|
|
parsed.type = "invite";
|
|
if (hashArr[1] && hashArr[1] === "2") {
|
|
parsed.version = 2;
|
|
parsed.app = hashArr[2];
|
|
parsed.mode = hashArr[3];
|
|
parsed.key = hashArr[4];
|
|
options = hashArr.slice(5);
|
|
parsed.password = options.indexOf("p") !== -1;
|
|
return parsed;
|
|
}
|
|
return parsed;
|
|
}
|
|
return;
|
|
};
|
|
var parsePadUrl = Hash.parsePadUrl = function(href) {
|
|
var patt = /^https*:\/\/([^\/]*)\/(.*?)\//i;
|
|
var ret = {};
|
|
if (!href) {
|
|
return ret;
|
|
}
|
|
if (href.slice(-1) !== "/" && href.slice(-1) !== "#") {
|
|
href += "/";
|
|
}
|
|
href = href.replace(/\/\?[^#]+#/, "/#");
|
|
var idx;
|
|
var getHash = function(opts) {
|
|
if (!opts || !Object.keys(opts).length) {
|
|
return "";
|
|
}
|
|
var hash = "/4/" + ret.type + "/";
|
|
if (opts.newPadOpts) {
|
|
hash += "newpad=" + opts.newPadOpts + "/";
|
|
}
|
|
if (opts.loginOpts) {
|
|
hash += "login=" + opts.loginOpts + "/";
|
|
}
|
|
return hash;
|
|
};
|
|
ret.getUrl = function(options) {
|
|
options = options || {};
|
|
var url = "/";
|
|
if (!ret.type) {
|
|
return url;
|
|
}
|
|
url += ret.type + "/";
|
|
if (!ret.hashData && options && Object.keys(options).length) {
|
|
return url + "#" + getHash(options);
|
|
}
|
|
if (!ret.hashData) {
|
|
return url;
|
|
}
|
|
var hash = ret.hashData.getHash(options);
|
|
url += "#" + hash;
|
|
return url;
|
|
};
|
|
ret.getOptions = function() {
|
|
if (!ret.hashData || !ret.hashData.getOptions) {
|
|
return {};
|
|
}
|
|
return ret.hashData.getOptions();
|
|
};
|
|
if (!/^https*:\/\//.test(href)) {
|
|
if (!/^\/($|[^\/])/.test(href)) {
|
|
return ret;
|
|
}
|
|
idx = href.indexOf("/#");
|
|
ret.type = href.slice(1, idx);
|
|
if (idx === -1) {
|
|
return ret;
|
|
}
|
|
ret.hash = href.slice(idx + 2);
|
|
ret.hashData = parseTypeHash(ret.type, ret.hash);
|
|
return ret;
|
|
}
|
|
href.replace(patt, (function(a, domain, type) {
|
|
ret.domain = domain;
|
|
ret.type = type;
|
|
return "";
|
|
}));
|
|
idx = href.indexOf("/#");
|
|
if (idx === -1) {
|
|
return ret;
|
|
}
|
|
ret.hash = href.slice(idx + 2);
|
|
ret.hashData = parseTypeHash(ret.type, ret.hash);
|
|
return ret;
|
|
};
|
|
Hash.hashToHref = function(hash, type) {
|
|
return "/" + type + "/#" + hash;
|
|
};
|
|
Hash.hrefToHash = function(href) {
|
|
var parsed = Hash.parsePadUrl(href);
|
|
return parsed.hash;
|
|
};
|
|
Hash.getRelativeHref = function(href) {
|
|
if (!href) {
|
|
return;
|
|
}
|
|
if (href.indexOf("#") === -1) {
|
|
return;
|
|
}
|
|
var parsed = parsePadUrl(href);
|
|
return "/" + parsed.type + "/#" + parsed.hash;
|
|
};
|
|
Hash.getSecrets = function(type, secretHash, password) {
|
|
var secret = {};
|
|
var generate = function() {
|
|
secret.keys = Crypto.createEditCryptor2(void 0, void 0, password);
|
|
secret.channel = base64ToHex(secret.keys.chanId);
|
|
secret.version = 2;
|
|
secret.type = type;
|
|
};
|
|
if (!secretHash) {
|
|
generate();
|
|
return secret;
|
|
} else {
|
|
var parsed;
|
|
var hash;
|
|
if (secretHash) {
|
|
if (!type) {
|
|
throw new Error("getSecrets with a hash requires a type parameter");
|
|
}
|
|
parsed = parseTypeHash(type, secretHash);
|
|
hash = secretHash;
|
|
}
|
|
if (hash.length === 0) {
|
|
generate();
|
|
return secret;
|
|
}
|
|
if (parsed.version === 0) {
|
|
secret.channel = parsed.channel;
|
|
secret.key = parsed.key;
|
|
secret.version = 0;
|
|
} else if (parsed.version === 1) {
|
|
secret.version = 1;
|
|
if (parsed.type === "pad") {
|
|
secret.channel = base64ToHex(parsed.channel);
|
|
if (parsed.mode === "edit") {
|
|
secret.keys = Crypto.createEditCryptor(parsed.key);
|
|
secret.key = secret.keys.editKeyStr;
|
|
if (secret.channel.length !== 32 || secret.key.length !== 24) {
|
|
throw new Error("The channel key and/or the encryption key is invalid");
|
|
}
|
|
} else if (parsed.mode === "view") {
|
|
secret.keys = Crypto.createViewCryptor(parsed.key);
|
|
if (secret.channel.length !== 32) {
|
|
throw new Error("The channel key is invalid");
|
|
}
|
|
}
|
|
} else if (parsed.type === "file") {
|
|
secret.channel = base64ToHex(parsed.channel);
|
|
secret.keys = {
|
|
fileKeyStr: parsed.key,
|
|
cryptKey: Nacl.util.decodeBase64(parsed.key)
|
|
};
|
|
} else if (parsed.type === "user") {
|
|
throw new Error("User hashes can't be opened (yet)");
|
|
}
|
|
} else if (parsed.version === 2) {
|
|
secret.version = 2;
|
|
secret.type = type;
|
|
secret.password = password;
|
|
if (parsed.type === "pad") {
|
|
if (parsed.mode === "edit") {
|
|
secret.keys = Crypto.createEditCryptor2(parsed.key, void 0, password);
|
|
secret.channel = base64ToHex(secret.keys.chanId);
|
|
secret.key = secret.keys.editKeyStr;
|
|
if (secret.channel.length !== 32 || secret.key.length !== 24) {
|
|
throw new Error("The channel key and/or the encryption key is invalid");
|
|
}
|
|
} else if (parsed.mode === "view") {
|
|
secret.keys = Crypto.createViewCryptor2(parsed.key, password);
|
|
secret.channel = base64ToHex(secret.keys.chanId);
|
|
if (secret.channel.length !== 32) {
|
|
throw new Error("The channel key is invalid");
|
|
}
|
|
}
|
|
} else if (parsed.type === "file") {
|
|
secret.keys = Crypto.createFileCryptor2(parsed.key, password);
|
|
secret.channel = base64ToHex(secret.keys.chanId);
|
|
secret.key = secret.keys.fileKeyStr;
|
|
if (secret.channel.length !== 48 || secret.key.length !== 24) {
|
|
throw new Error("The channel key and/or the encryption key is invalid");
|
|
}
|
|
} else if (parsed.type === "user") {
|
|
throw new Error("User hashes can't be opened (yet)");
|
|
}
|
|
}
|
|
}
|
|
return secret;
|
|
};
|
|
Hash.getHashes = function(secret) {
|
|
var hashes = {};
|
|
secret = JSON.parse(JSON.stringify(secret));
|
|
if (!secret.keys && !secret.key) {
|
|
return hashes;
|
|
} else if (!secret.keys) {
|
|
secret.keys = {};
|
|
}
|
|
if (secret.keys.editKeyStr || secret.version === 0 && secret.key) {
|
|
hashes.editHash = getEditHashFromKeys(secret);
|
|
}
|
|
if (secret.keys.viewKeyStr) {
|
|
hashes.viewHash = getViewHashFromKeys(secret);
|
|
}
|
|
if (secret.keys.fileKeyStr) {
|
|
hashes.fileHash = getFileHashFromKeys(secret);
|
|
}
|
|
return hashes;
|
|
};
|
|
Hash.getFormData = function(secret, hash, password) {
|
|
secret = secret || Hash.getSecrets("form", hash, password);
|
|
var keys = secret && secret.keys;
|
|
var secondary = keys && keys.secondaryKey;
|
|
if (!secondary) {
|
|
return;
|
|
}
|
|
var curvePair = Nacl.box.keyPair.fromSecretKey(Nacl.util.decodeUTF8(secondary).slice(0, 32));
|
|
var ret = {};
|
|
ret.form_public = Nacl.util.encodeBase64(curvePair.publicKey);
|
|
var privateKey = ret.form_private = Nacl.util.encodeBase64(curvePair.secretKey);
|
|
var auditorHash = Hash.getViewHashFromKeys({
|
|
version: 1,
|
|
channel: secret.channel,
|
|
keys: {
|
|
viewKeyStr: Nacl.util.encodeBase64(keys.cryptKey)
|
|
}
|
|
});
|
|
var _parsed = Hash.parseTypeHash("pad", auditorHash);
|
|
ret.form_auditorHash = _parsed.getHash({
|
|
auditorKey: privateKey
|
|
});
|
|
return ret;
|
|
};
|
|
Hash.hrefToHexChannelId = function(href, password) {
|
|
var parsed = Hash.parsePadUrl(href);
|
|
if (!parsed || !parsed.hash) {
|
|
return;
|
|
}
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
|
|
return secret.channel;
|
|
};
|
|
Hash.getBlobPathFromHex = function(id) {
|
|
return "/blob/" + id.slice(0, 2) + "/" + id;
|
|
};
|
|
Hash.serializeHash = function(hash) {
|
|
if (hash && hash.slice(-1) !== "/") {
|
|
hash += "/";
|
|
}
|
|
return hash;
|
|
};
|
|
Hash.createInviteUrl = function(curvePublic, channel) {
|
|
channel = channel || Hash.createChannelId();
|
|
return window.location.origin + "/invite/#/1/" + channel + "/" + curvePublic.replace(/\//g, "-") + "/";
|
|
};
|
|
Hash.isValidChannel = function(channelId) {
|
|
return /^[a-zA-Z0-9]{32,48}$/.test(channelId);
|
|
};
|
|
Hash.isValidHref = function(href) {
|
|
if (!href) {
|
|
return;
|
|
}
|
|
var parsed = Hash.parsePadUrl(href);
|
|
if (!parsed) {
|
|
return;
|
|
}
|
|
if (!parsed.type) {
|
|
return;
|
|
}
|
|
if (parsed.hash) {
|
|
if (!parsed.hashData) {
|
|
return;
|
|
}
|
|
if (typeof parsed.hashData.version === "undefined") {
|
|
return;
|
|
}
|
|
if (parsed.hashData.type === "pad" || parsed.hashData.type === "file") {
|
|
if (!parsed.hashData.key && !parsed.hashData.channel) {
|
|
return;
|
|
}
|
|
if (parsed.hashData.key && !/^[a-zA-Z0-9+-/=]+$/.test(parsed.hashData.key)) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
return parsed;
|
|
};
|
|
Hash.decodeDataOptions = function(opts) {
|
|
var b64 = decodeURIComponent(opts);
|
|
var str = Nacl.util.encodeUTF8(Nacl.util.decodeBase64(b64));
|
|
return Util.tryParse(str) || {};
|
|
};
|
|
Hash.encodeDataOptions = function(opts) {
|
|
var str = JSON.stringify(opts);
|
|
var b64 = Nacl.util.encodeBase64(Nacl.util.decodeUTF8(str));
|
|
return encodeURIComponent(b64);
|
|
};
|
|
Hash.getNewPadURL = function(href, opts) {
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var options = parsed.getOptions();
|
|
options.newPadOpts = Hash.encodeDataOptions(opts);
|
|
return parsed.getUrl(options);
|
|
};
|
|
Hash.getLoginURL = function(href, opts) {
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var options = parsed.getOptions();
|
|
options.loginOpts = Hash.encodeDataOptions(opts);
|
|
return parsed.getUrl(options);
|
|
};
|
|
return Hash;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCrypto(), requireCommonSigningKeys(), requireNaclFast());
|
|
}
|
|
})(typeof window !== "undefined" ? window : {});
|
|
})(commonHash);
|
|
return commonHash.exports;
|
|
}
|
|
var commonHashExports = requireCommonHash();
|
|
var commonUtilExports = requireCommonUtil();
|
|
var cacheStore$1 = {
|
|
exports: {}
|
|
};
|
|
var localforage = {
|
|
exports: {}
|
|
};
|
|
/*!
|
|
localForage -- Offline Storage, Improved
|
|
Version 1.10.0
|
|
https://localforage.github.io/localForage
|
|
(c) 2013-2017 Mozilla, Apache License 2.0
|
|
*/ var hasRequiredLocalforage;
|
|
function requireLocalforage() {
|
|
if (hasRequiredLocalforage) return localforage.exports;
|
|
hasRequiredLocalforage = 1;
|
|
(function(module, exports) {
|
|
(function(f) {
|
|
{
|
|
module.exports = f();
|
|
}
|
|
})((function() {
|
|
return function e(t, n, r) {
|
|
function s(o, u) {
|
|
if (!n[o]) {
|
|
if (!t[o]) {
|
|
var a = typeof commonjsRequire == "function" && commonjsRequire;
|
|
if (!u && a) return a(o, !0);
|
|
if (i) return i(o, !0);
|
|
var f = new Error("Cannot find module '" + o + "'");
|
|
throw f.code = "MODULE_NOT_FOUND", f;
|
|
}
|
|
var l = n[o] = {
|
|
exports: {}
|
|
};
|
|
t[o][0].call(l.exports, (function(e) {
|
|
var n = t[o][1][e];
|
|
return s(n ? n : e);
|
|
}), l, l.exports, e, t, n, r);
|
|
}
|
|
return n[o].exports;
|
|
}
|
|
var i = typeof commonjsRequire == "function" && commonjsRequire;
|
|
for (var o = 0; o < r.length; o++) s(r[o]);
|
|
return s;
|
|
}({
|
|
1: [ function(_dereq_, module, exports) {
|
|
(function(global) {
|
|
var Mutation = global.MutationObserver || global.WebKitMutationObserver;
|
|
var scheduleDrain;
|
|
{
|
|
if (Mutation) {
|
|
var called = 0;
|
|
var observer = new Mutation(nextTick);
|
|
var element = global.document.createTextNode("");
|
|
observer.observe(element, {
|
|
characterData: true
|
|
});
|
|
scheduleDrain = function() {
|
|
element.data = called = ++called % 2;
|
|
};
|
|
} else if (!global.setImmediate && typeof global.MessageChannel !== "undefined") {
|
|
var channel = new global.MessageChannel;
|
|
channel.port1.onmessage = nextTick;
|
|
scheduleDrain = function() {
|
|
channel.port2.postMessage(0);
|
|
};
|
|
} else if ("document" in global && "onreadystatechange" in global.document.createElement("script")) {
|
|
scheduleDrain = function() {
|
|
var scriptEl = global.document.createElement("script");
|
|
scriptEl.onreadystatechange = function() {
|
|
nextTick();
|
|
scriptEl.onreadystatechange = null;
|
|
scriptEl.parentNode.removeChild(scriptEl);
|
|
scriptEl = null;
|
|
};
|
|
global.document.documentElement.appendChild(scriptEl);
|
|
};
|
|
} else {
|
|
scheduleDrain = function() {
|
|
setTimeout(nextTick, 0);
|
|
};
|
|
}
|
|
}
|
|
var draining;
|
|
var queue = [];
|
|
function nextTick() {
|
|
draining = true;
|
|
var i, oldQueue;
|
|
var len = queue.length;
|
|
while (len) {
|
|
oldQueue = queue;
|
|
queue = [];
|
|
i = -1;
|
|
while (++i < len) {
|
|
oldQueue[i]();
|
|
}
|
|
len = queue.length;
|
|
}
|
|
draining = false;
|
|
}
|
|
module.exports = immediate;
|
|
function immediate(task) {
|
|
if (queue.push(task) === 1 && !draining) {
|
|
scheduleDrain();
|
|
}
|
|
}
|
|
}).call(this, typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
|
|
}, {} ],
|
|
2: [ function(_dereq_, module, exports) {
|
|
var immediate = _dereq_(1);
|
|
function INTERNAL() {}
|
|
var handlers = {};
|
|
var REJECTED = [ "REJECTED" ];
|
|
var FULFILLED = [ "FULFILLED" ];
|
|
var PENDING = [ "PENDING" ];
|
|
module.exports = Promise;
|
|
function Promise(resolver) {
|
|
if (typeof resolver !== "function") {
|
|
throw new TypeError("resolver must be a function");
|
|
}
|
|
this.state = PENDING;
|
|
this.queue = [];
|
|
this.outcome = void 0;
|
|
if (resolver !== INTERNAL) {
|
|
safelyResolveThenable(this, resolver);
|
|
}
|
|
}
|
|
Promise.prototype["catch"] = function(onRejected) {
|
|
return this.then(null, onRejected);
|
|
};
|
|
Promise.prototype.then = function(onFulfilled, onRejected) {
|
|
if (typeof onFulfilled !== "function" && this.state === FULFILLED || typeof onRejected !== "function" && this.state === REJECTED) {
|
|
return this;
|
|
}
|
|
var promise = new this.constructor(INTERNAL);
|
|
if (this.state !== PENDING) {
|
|
var resolver = this.state === FULFILLED ? onFulfilled : onRejected;
|
|
unwrap(promise, resolver, this.outcome);
|
|
} else {
|
|
this.queue.push(new QueueItem(promise, onFulfilled, onRejected));
|
|
}
|
|
return promise;
|
|
};
|
|
function QueueItem(promise, onFulfilled, onRejected) {
|
|
this.promise = promise;
|
|
if (typeof onFulfilled === "function") {
|
|
this.onFulfilled = onFulfilled;
|
|
this.callFulfilled = this.otherCallFulfilled;
|
|
}
|
|
if (typeof onRejected === "function") {
|
|
this.onRejected = onRejected;
|
|
this.callRejected = this.otherCallRejected;
|
|
}
|
|
}
|
|
QueueItem.prototype.callFulfilled = function(value) {
|
|
handlers.resolve(this.promise, value);
|
|
};
|
|
QueueItem.prototype.otherCallFulfilled = function(value) {
|
|
unwrap(this.promise, this.onFulfilled, value);
|
|
};
|
|
QueueItem.prototype.callRejected = function(value) {
|
|
handlers.reject(this.promise, value);
|
|
};
|
|
QueueItem.prototype.otherCallRejected = function(value) {
|
|
unwrap(this.promise, this.onRejected, value);
|
|
};
|
|
function unwrap(promise, func, value) {
|
|
immediate((function() {
|
|
var returnValue;
|
|
try {
|
|
returnValue = func(value);
|
|
} catch (e) {
|
|
return handlers.reject(promise, e);
|
|
}
|
|
if (returnValue === promise) {
|
|
handlers.reject(promise, new TypeError("Cannot resolve promise with itself"));
|
|
} else {
|
|
handlers.resolve(promise, returnValue);
|
|
}
|
|
}));
|
|
}
|
|
handlers.resolve = function(self, value) {
|
|
var result = tryCatch(getThen, value);
|
|
if (result.status === "error") {
|
|
return handlers.reject(self, result.value);
|
|
}
|
|
var thenable = result.value;
|
|
if (thenable) {
|
|
safelyResolveThenable(self, thenable);
|
|
} else {
|
|
self.state = FULFILLED;
|
|
self.outcome = value;
|
|
var i = -1;
|
|
var len = self.queue.length;
|
|
while (++i < len) {
|
|
self.queue[i].callFulfilled(value);
|
|
}
|
|
}
|
|
return self;
|
|
};
|
|
handlers.reject = function(self, error) {
|
|
self.state = REJECTED;
|
|
self.outcome = error;
|
|
var i = -1;
|
|
var len = self.queue.length;
|
|
while (++i < len) {
|
|
self.queue[i].callRejected(error);
|
|
}
|
|
return self;
|
|
};
|
|
function getThen(obj) {
|
|
var then = obj && obj.then;
|
|
if (obj && (typeof obj === "object" || typeof obj === "function") && typeof then === "function") {
|
|
return function appyThen() {
|
|
then.apply(obj, arguments);
|
|
};
|
|
}
|
|
}
|
|
function safelyResolveThenable(self, thenable) {
|
|
var called = false;
|
|
function onError(value) {
|
|
if (called) {
|
|
return;
|
|
}
|
|
called = true;
|
|
handlers.reject(self, value);
|
|
}
|
|
function onSuccess(value) {
|
|
if (called) {
|
|
return;
|
|
}
|
|
called = true;
|
|
handlers.resolve(self, value);
|
|
}
|
|
function tryToUnwrap() {
|
|
thenable(onSuccess, onError);
|
|
}
|
|
var result = tryCatch(tryToUnwrap);
|
|
if (result.status === "error") {
|
|
onError(result.value);
|
|
}
|
|
}
|
|
function tryCatch(func, value) {
|
|
var out = {};
|
|
try {
|
|
out.value = func(value);
|
|
out.status = "success";
|
|
} catch (e) {
|
|
out.status = "error";
|
|
out.value = e;
|
|
}
|
|
return out;
|
|
}
|
|
Promise.resolve = resolve;
|
|
function resolve(value) {
|
|
if (value instanceof this) {
|
|
return value;
|
|
}
|
|
return handlers.resolve(new this(INTERNAL), value);
|
|
}
|
|
Promise.reject = reject;
|
|
function reject(reason) {
|
|
var promise = new this(INTERNAL);
|
|
return handlers.reject(promise, reason);
|
|
}
|
|
Promise.all = all;
|
|
function all(iterable) {
|
|
var self = this;
|
|
if (Object.prototype.toString.call(iterable) !== "[object Array]") {
|
|
return this.reject(new TypeError("must be an array"));
|
|
}
|
|
var len = iterable.length;
|
|
var called = false;
|
|
if (!len) {
|
|
return this.resolve([]);
|
|
}
|
|
var values = new Array(len);
|
|
var resolved = 0;
|
|
var i = -1;
|
|
var promise = new this(INTERNAL);
|
|
while (++i < len) {
|
|
allResolver(iterable[i], i);
|
|
}
|
|
return promise;
|
|
function allResolver(value, i) {
|
|
self.resolve(value).then(resolveFromAll, (function(error) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.reject(promise, error);
|
|
}
|
|
}));
|
|
function resolveFromAll(outValue) {
|
|
values[i] = outValue;
|
|
if (++resolved === len && !called) {
|
|
called = true;
|
|
handlers.resolve(promise, values);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Promise.race = race;
|
|
function race(iterable) {
|
|
var self = this;
|
|
if (Object.prototype.toString.call(iterable) !== "[object Array]") {
|
|
return this.reject(new TypeError("must be an array"));
|
|
}
|
|
var len = iterable.length;
|
|
var called = false;
|
|
if (!len) {
|
|
return this.resolve([]);
|
|
}
|
|
var i = -1;
|
|
var promise = new this(INTERNAL);
|
|
while (++i < len) {
|
|
resolver(iterable[i]);
|
|
}
|
|
return promise;
|
|
function resolver(value) {
|
|
self.resolve(value).then((function(response) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.resolve(promise, response);
|
|
}
|
|
}), (function(error) {
|
|
if (!called) {
|
|
called = true;
|
|
handlers.reject(promise, error);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}, {
|
|
1: 1
|
|
} ],
|
|
3: [ function(_dereq_, module, exports) {
|
|
(function(global) {
|
|
if (typeof global.Promise !== "function") {
|
|
global.Promise = _dereq_(2);
|
|
}
|
|
}).call(this, typeof commonjsGlobal !== "undefined" ? commonjsGlobal : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {});
|
|
}, {
|
|
2: 2
|
|
} ],
|
|
4: [ function(_dereq_, module, exports) {
|
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function(obj) {
|
|
return typeof obj;
|
|
} : function(obj) {
|
|
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
|
};
|
|
function _classCallCheck(instance, Constructor) {
|
|
if (!(instance instanceof Constructor)) {
|
|
throw new TypeError("Cannot call a class as a function");
|
|
}
|
|
}
|
|
function getIDB() {
|
|
try {
|
|
if (typeof indexedDB !== "undefined") {
|
|
return indexedDB;
|
|
}
|
|
if (typeof webkitIndexedDB !== "undefined") {
|
|
return webkitIndexedDB;
|
|
}
|
|
if (typeof mozIndexedDB !== "undefined") {
|
|
return mozIndexedDB;
|
|
}
|
|
if (typeof OIndexedDB !== "undefined") {
|
|
return OIndexedDB;
|
|
}
|
|
if (typeof msIndexedDB !== "undefined") {
|
|
return msIndexedDB;
|
|
}
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
}
|
|
var idb = getIDB();
|
|
function isIndexedDBValid() {
|
|
try {
|
|
if (!idb || !idb.open) {
|
|
return false;
|
|
}
|
|
var isSafari = typeof openDatabase !== "undefined" && /(Safari|iPhone|iPad|iPod)/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent) && !/BlackBerry/.test(navigator.platform);
|
|
var hasFetch = typeof fetch === "function" && fetch.toString().indexOf("[native code") !== -1;
|
|
return (!isSafari || hasFetch) && typeof indexedDB !== "undefined" && typeof IDBKeyRange !== "undefined";
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
function createBlob(parts, properties) {
|
|
parts = parts || [];
|
|
properties = properties || {};
|
|
try {
|
|
return new Blob(parts, properties);
|
|
} catch (e) {
|
|
if (e.name !== "TypeError") {
|
|
throw e;
|
|
}
|
|
var Builder = typeof BlobBuilder !== "undefined" ? BlobBuilder : typeof MSBlobBuilder !== "undefined" ? MSBlobBuilder : typeof MozBlobBuilder !== "undefined" ? MozBlobBuilder : WebKitBlobBuilder;
|
|
var builder = new Builder;
|
|
for (var i = 0; i < parts.length; i += 1) {
|
|
builder.append(parts[i]);
|
|
}
|
|
return builder.getBlob(properties.type);
|
|
}
|
|
}
|
|
if (typeof Promise === "undefined") {
|
|
_dereq_(3);
|
|
}
|
|
var Promise$1 = Promise;
|
|
function executeCallback(promise, callback) {
|
|
if (callback) {
|
|
promise.then((function(result) {
|
|
callback(null, result);
|
|
}), (function(error) {
|
|
callback(error);
|
|
}));
|
|
}
|
|
}
|
|
function executeTwoCallbacks(promise, callback, errorCallback) {
|
|
if (typeof callback === "function") {
|
|
promise.then(callback);
|
|
}
|
|
if (typeof errorCallback === "function") {
|
|
promise["catch"](errorCallback);
|
|
}
|
|
}
|
|
function normalizeKey(key) {
|
|
if (typeof key !== "string") {
|
|
console.warn(key + " used as a key, but it is not a string.");
|
|
key = String(key);
|
|
}
|
|
return key;
|
|
}
|
|
function getCallback() {
|
|
if (arguments.length && typeof arguments[arguments.length - 1] === "function") {
|
|
return arguments[arguments.length - 1];
|
|
}
|
|
}
|
|
var DETECT_BLOB_SUPPORT_STORE = "local-forage-detect-blob-support";
|
|
var supportsBlobs = void 0;
|
|
var dbContexts = {};
|
|
var toString = Object.prototype.toString;
|
|
var READ_ONLY = "readonly";
|
|
var READ_WRITE = "readwrite";
|
|
function _binStringToArrayBuffer(bin) {
|
|
var length = bin.length;
|
|
var buf = new ArrayBuffer(length);
|
|
var arr = new Uint8Array(buf);
|
|
for (var i = 0; i < length; i++) {
|
|
arr[i] = bin.charCodeAt(i);
|
|
}
|
|
return buf;
|
|
}
|
|
function _checkBlobSupportWithoutCaching(idb) {
|
|
return new Promise$1((function(resolve) {
|
|
var txn = idb.transaction(DETECT_BLOB_SUPPORT_STORE, READ_WRITE);
|
|
var blob = createBlob([ "" ]);
|
|
txn.objectStore(DETECT_BLOB_SUPPORT_STORE).put(blob, "key");
|
|
txn.onabort = function(e) {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
resolve(false);
|
|
};
|
|
txn.oncomplete = function() {
|
|
var matchedChrome = navigator.userAgent.match(/Chrome\/(\d+)/);
|
|
var matchedEdge = navigator.userAgent.match(/Edge\//);
|
|
resolve(matchedEdge || !matchedChrome || parseInt(matchedChrome[1], 10) >= 43);
|
|
};
|
|
}))["catch"]((function() {
|
|
return false;
|
|
}));
|
|
}
|
|
function _checkBlobSupport(idb) {
|
|
if (typeof supportsBlobs === "boolean") {
|
|
return Promise$1.resolve(supportsBlobs);
|
|
}
|
|
return _checkBlobSupportWithoutCaching(idb).then((function(value) {
|
|
supportsBlobs = value;
|
|
return supportsBlobs;
|
|
}));
|
|
}
|
|
function _deferReadiness(dbInfo) {
|
|
var dbContext = dbContexts[dbInfo.name];
|
|
var deferredOperation = {};
|
|
deferredOperation.promise = new Promise$1((function(resolve, reject) {
|
|
deferredOperation.resolve = resolve;
|
|
deferredOperation.reject = reject;
|
|
}));
|
|
dbContext.deferredOperations.push(deferredOperation);
|
|
if (!dbContext.dbReady) {
|
|
dbContext.dbReady = deferredOperation.promise;
|
|
} else {
|
|
dbContext.dbReady = dbContext.dbReady.then((function() {
|
|
return deferredOperation.promise;
|
|
}));
|
|
}
|
|
}
|
|
function _advanceReadiness(dbInfo) {
|
|
var dbContext = dbContexts[dbInfo.name];
|
|
var deferredOperation = dbContext.deferredOperations.pop();
|
|
if (deferredOperation) {
|
|
deferredOperation.resolve();
|
|
return deferredOperation.promise;
|
|
}
|
|
}
|
|
function _rejectReadiness(dbInfo, err) {
|
|
var dbContext = dbContexts[dbInfo.name];
|
|
var deferredOperation = dbContext.deferredOperations.pop();
|
|
if (deferredOperation) {
|
|
deferredOperation.reject(err);
|
|
return deferredOperation.promise;
|
|
}
|
|
}
|
|
function _getConnection(dbInfo, upgradeNeeded) {
|
|
return new Promise$1((function(resolve, reject) {
|
|
dbContexts[dbInfo.name] = dbContexts[dbInfo.name] || createDbContext();
|
|
if (dbInfo.db) {
|
|
if (upgradeNeeded) {
|
|
_deferReadiness(dbInfo);
|
|
dbInfo.db.close();
|
|
} else {
|
|
return resolve(dbInfo.db);
|
|
}
|
|
}
|
|
var dbArgs = [ dbInfo.name ];
|
|
if (upgradeNeeded) {
|
|
dbArgs.push(dbInfo.version);
|
|
}
|
|
var openreq = idb.open.apply(idb, dbArgs);
|
|
if (upgradeNeeded) {
|
|
openreq.onupgradeneeded = function(e) {
|
|
var db = openreq.result;
|
|
try {
|
|
db.createObjectStore(dbInfo.storeName);
|
|
if (e.oldVersion <= 1) {
|
|
db.createObjectStore(DETECT_BLOB_SUPPORT_STORE);
|
|
}
|
|
} catch (ex) {
|
|
if (ex.name === "ConstraintError") {
|
|
console.warn('The database "' + dbInfo.name + '"' + " has been upgraded from version " + e.oldVersion + " to version " + e.newVersion + ', but the storage "' + dbInfo.storeName + '" already exists.');
|
|
} else {
|
|
throw ex;
|
|
}
|
|
}
|
|
};
|
|
}
|
|
openreq.onerror = function(e) {
|
|
e.preventDefault();
|
|
reject(openreq.error);
|
|
};
|
|
openreq.onsuccess = function() {
|
|
var db = openreq.result;
|
|
db.onversionchange = function(e) {
|
|
e.target.close();
|
|
};
|
|
resolve(db);
|
|
_advanceReadiness(dbInfo);
|
|
};
|
|
}));
|
|
}
|
|
function _getOriginalConnection(dbInfo) {
|
|
return _getConnection(dbInfo, false);
|
|
}
|
|
function _getUpgradedConnection(dbInfo) {
|
|
return _getConnection(dbInfo, true);
|
|
}
|
|
function _isUpgradeNeeded(dbInfo, defaultVersion) {
|
|
if (!dbInfo.db) {
|
|
return true;
|
|
}
|
|
var isNewStore = !dbInfo.db.objectStoreNames.contains(dbInfo.storeName);
|
|
var isDowngrade = dbInfo.version < dbInfo.db.version;
|
|
var isUpgrade = dbInfo.version > dbInfo.db.version;
|
|
if (isDowngrade) {
|
|
if (dbInfo.version !== defaultVersion) {
|
|
console.warn('The database "' + dbInfo.name + '"' + " can't be downgraded from version " + dbInfo.db.version + " to version " + dbInfo.version + ".");
|
|
}
|
|
dbInfo.version = dbInfo.db.version;
|
|
}
|
|
if (isUpgrade || isNewStore) {
|
|
if (isNewStore) {
|
|
var incVersion = dbInfo.db.version + 1;
|
|
if (incVersion > dbInfo.version) {
|
|
dbInfo.version = incVersion;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
function _encodeBlob(blob) {
|
|
return new Promise$1((function(resolve, reject) {
|
|
var reader = new FileReader;
|
|
reader.onerror = reject;
|
|
reader.onloadend = function(e) {
|
|
var base64 = btoa(e.target.result || "");
|
|
resolve({
|
|
__local_forage_encoded_blob: true,
|
|
data: base64,
|
|
type: blob.type
|
|
});
|
|
};
|
|
reader.readAsBinaryString(blob);
|
|
}));
|
|
}
|
|
function _decodeBlob(encodedBlob) {
|
|
var arrayBuff = _binStringToArrayBuffer(atob(encodedBlob.data));
|
|
return createBlob([ arrayBuff ], {
|
|
type: encodedBlob.type
|
|
});
|
|
}
|
|
function _isEncodedBlob(value) {
|
|
return value && value.__local_forage_encoded_blob;
|
|
}
|
|
function _fullyReady(callback) {
|
|
var self = this;
|
|
var promise = self._initReady().then((function() {
|
|
var dbContext = dbContexts[self._dbInfo.name];
|
|
if (dbContext && dbContext.dbReady) {
|
|
return dbContext.dbReady;
|
|
}
|
|
}));
|
|
executeTwoCallbacks(promise, callback, callback);
|
|
return promise;
|
|
}
|
|
function _tryReconnect(dbInfo) {
|
|
_deferReadiness(dbInfo);
|
|
var dbContext = dbContexts[dbInfo.name];
|
|
var forages = dbContext.forages;
|
|
for (var i = 0; i < forages.length; i++) {
|
|
var forage = forages[i];
|
|
if (forage._dbInfo.db) {
|
|
forage._dbInfo.db.close();
|
|
forage._dbInfo.db = null;
|
|
}
|
|
}
|
|
dbInfo.db = null;
|
|
return _getOriginalConnection(dbInfo).then((function(db) {
|
|
dbInfo.db = db;
|
|
if (_isUpgradeNeeded(dbInfo)) {
|
|
return _getUpgradedConnection(dbInfo);
|
|
}
|
|
return db;
|
|
})).then((function(db) {
|
|
dbInfo.db = dbContext.db = db;
|
|
for (var i = 0; i < forages.length; i++) {
|
|
forages[i]._dbInfo.db = db;
|
|
}
|
|
}))["catch"]((function(err) {
|
|
_rejectReadiness(dbInfo, err);
|
|
throw err;
|
|
}));
|
|
}
|
|
function createTransaction(dbInfo, mode, callback, retries) {
|
|
if (retries === undefined) {
|
|
retries = 1;
|
|
}
|
|
try {
|
|
var tx = dbInfo.db.transaction(dbInfo.storeName, mode);
|
|
callback(null, tx);
|
|
} catch (err) {
|
|
if (retries > 0 && (!dbInfo.db || err.name === "InvalidStateError" || err.name === "NotFoundError")) {
|
|
return Promise$1.resolve().then((function() {
|
|
if (!dbInfo.db || err.name === "NotFoundError" && !dbInfo.db.objectStoreNames.contains(dbInfo.storeName) && dbInfo.version <= dbInfo.db.version) {
|
|
if (dbInfo.db) {
|
|
dbInfo.version = dbInfo.db.version + 1;
|
|
}
|
|
return _getUpgradedConnection(dbInfo);
|
|
}
|
|
})).then((function() {
|
|
return _tryReconnect(dbInfo).then((function() {
|
|
createTransaction(dbInfo, mode, callback, retries - 1);
|
|
}));
|
|
}))["catch"](callback);
|
|
}
|
|
callback(err);
|
|
}
|
|
}
|
|
function createDbContext() {
|
|
return {
|
|
forages: [],
|
|
db: null,
|
|
dbReady: null,
|
|
deferredOperations: []
|
|
};
|
|
}
|
|
function _initStorage(options) {
|
|
var self = this;
|
|
var dbInfo = {
|
|
db: null
|
|
};
|
|
if (options) {
|
|
for (var i in options) {
|
|
dbInfo[i] = options[i];
|
|
}
|
|
}
|
|
var dbContext = dbContexts[dbInfo.name];
|
|
if (!dbContext) {
|
|
dbContext = createDbContext();
|
|
dbContexts[dbInfo.name] = dbContext;
|
|
}
|
|
dbContext.forages.push(self);
|
|
if (!self._initReady) {
|
|
self._initReady = self.ready;
|
|
self.ready = _fullyReady;
|
|
}
|
|
var initPromises = [];
|
|
function ignoreErrors() {
|
|
return Promise$1.resolve();
|
|
}
|
|
for (var j = 0; j < dbContext.forages.length; j++) {
|
|
var forage = dbContext.forages[j];
|
|
if (forage !== self) {
|
|
initPromises.push(forage._initReady()["catch"](ignoreErrors));
|
|
}
|
|
}
|
|
var forages = dbContext.forages.slice(0);
|
|
return Promise$1.all(initPromises).then((function() {
|
|
dbInfo.db = dbContext.db;
|
|
return _getOriginalConnection(dbInfo);
|
|
})).then((function(db) {
|
|
dbInfo.db = db;
|
|
if (_isUpgradeNeeded(dbInfo, self._defaultConfig.version)) {
|
|
return _getUpgradedConnection(dbInfo);
|
|
}
|
|
return db;
|
|
})).then((function(db) {
|
|
dbInfo.db = dbContext.db = db;
|
|
self._dbInfo = dbInfo;
|
|
for (var k = 0; k < forages.length; k++) {
|
|
var forage = forages[k];
|
|
if (forage !== self) {
|
|
forage._dbInfo.db = dbInfo.db;
|
|
forage._dbInfo.version = dbInfo.version;
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
function getItem(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_ONLY, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store.get(key);
|
|
req.onsuccess = function() {
|
|
var value = req.result;
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
if (_isEncodedBlob(value)) {
|
|
value = _decodeBlob(value);
|
|
}
|
|
resolve(value);
|
|
};
|
|
req.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function iterate(iterator, callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_ONLY, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store.openCursor();
|
|
var iterationNumber = 1;
|
|
req.onsuccess = function() {
|
|
var cursor = req.result;
|
|
if (cursor) {
|
|
var value = cursor.value;
|
|
if (_isEncodedBlob(value)) {
|
|
value = _decodeBlob(value);
|
|
}
|
|
var result = iterator(value, cursor.key, iterationNumber++);
|
|
if (result !== void 0) {
|
|
resolve(result);
|
|
} else {
|
|
cursor["continue"]();
|
|
}
|
|
} else {
|
|
resolve();
|
|
}
|
|
};
|
|
req.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function setItem(key, value, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
var dbInfo;
|
|
self.ready().then((function() {
|
|
dbInfo = self._dbInfo;
|
|
if (toString.call(value) === "[object Blob]") {
|
|
return _checkBlobSupport(dbInfo.db).then((function(blobSupport) {
|
|
if (blobSupport) {
|
|
return value;
|
|
}
|
|
return _encodeBlob(value);
|
|
}));
|
|
}
|
|
return value;
|
|
})).then((function(value) {
|
|
createTransaction(self._dbInfo, READ_WRITE, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
if (value === null) {
|
|
value = undefined;
|
|
}
|
|
var req = store.put(value, key);
|
|
transaction.oncomplete = function() {
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
resolve(value);
|
|
};
|
|
transaction.onabort = transaction.onerror = function() {
|
|
var err = req.error ? req.error : req.transaction.error;
|
|
reject(err);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function removeItem(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_WRITE, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store["delete"](key);
|
|
transaction.oncomplete = function() {
|
|
resolve();
|
|
};
|
|
transaction.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
transaction.onabort = function() {
|
|
var err = req.error ? req.error : req.transaction.error;
|
|
reject(err);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function clear(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_WRITE, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store.clear();
|
|
transaction.oncomplete = function() {
|
|
resolve();
|
|
};
|
|
transaction.onabort = transaction.onerror = function() {
|
|
var err = req.error ? req.error : req.transaction.error;
|
|
reject(err);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function length(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_ONLY, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store.count();
|
|
req.onsuccess = function() {
|
|
resolve(req.result);
|
|
};
|
|
req.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function key(n, callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
if (n < 0) {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_ONLY, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var advanced = false;
|
|
var req = store.openKeyCursor();
|
|
req.onsuccess = function() {
|
|
var cursor = req.result;
|
|
if (!cursor) {
|
|
resolve(null);
|
|
return;
|
|
}
|
|
if (n === 0) {
|
|
resolve(cursor.key);
|
|
} else {
|
|
if (!advanced) {
|
|
advanced = true;
|
|
cursor.advance(n);
|
|
} else {
|
|
resolve(cursor.key);
|
|
}
|
|
}
|
|
};
|
|
req.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function keys(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
createTransaction(self._dbInfo, READ_ONLY, (function(err, transaction) {
|
|
if (err) {
|
|
return reject(err);
|
|
}
|
|
try {
|
|
var store = transaction.objectStore(self._dbInfo.storeName);
|
|
var req = store.openKeyCursor();
|
|
var keys = [];
|
|
req.onsuccess = function() {
|
|
var cursor = req.result;
|
|
if (!cursor) {
|
|
resolve(keys);
|
|
return;
|
|
}
|
|
keys.push(cursor.key);
|
|
cursor["continue"]();
|
|
};
|
|
req.onerror = function() {
|
|
reject(req.error);
|
|
};
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function dropInstance(options, callback) {
|
|
callback = getCallback.apply(this, arguments);
|
|
var currentConfig = this.config();
|
|
options = typeof options !== "function" && options || {};
|
|
if (!options.name) {
|
|
options.name = options.name || currentConfig.name;
|
|
options.storeName = options.storeName || currentConfig.storeName;
|
|
}
|
|
var self = this;
|
|
var promise;
|
|
if (!options.name) {
|
|
promise = Promise$1.reject("Invalid arguments");
|
|
} else {
|
|
var isCurrentDb = options.name === currentConfig.name && self._dbInfo.db;
|
|
var dbPromise = isCurrentDb ? Promise$1.resolve(self._dbInfo.db) : _getOriginalConnection(options).then((function(db) {
|
|
var dbContext = dbContexts[options.name];
|
|
var forages = dbContext.forages;
|
|
dbContext.db = db;
|
|
for (var i = 0; i < forages.length; i++) {
|
|
forages[i]._dbInfo.db = db;
|
|
}
|
|
return db;
|
|
}));
|
|
if (!options.storeName) {
|
|
promise = dbPromise.then((function(db) {
|
|
_deferReadiness(options);
|
|
var dbContext = dbContexts[options.name];
|
|
var forages = dbContext.forages;
|
|
db.close();
|
|
for (var i = 0; i < forages.length; i++) {
|
|
var forage = forages[i];
|
|
forage._dbInfo.db = null;
|
|
}
|
|
var dropDBPromise = new Promise$1((function(resolve, reject) {
|
|
var req = idb.deleteDatabase(options.name);
|
|
req.onerror = function() {
|
|
var db = req.result;
|
|
if (db) {
|
|
db.close();
|
|
}
|
|
reject(req.error);
|
|
};
|
|
req.onblocked = function() {
|
|
console.warn('dropInstance blocked for database "' + options.name + '" until all open connections are closed');
|
|
};
|
|
req.onsuccess = function() {
|
|
var db = req.result;
|
|
if (db) {
|
|
db.close();
|
|
}
|
|
resolve(db);
|
|
};
|
|
}));
|
|
return dropDBPromise.then((function(db) {
|
|
dbContext.db = db;
|
|
for (var i = 0; i < forages.length; i++) {
|
|
var _forage = forages[i];
|
|
_advanceReadiness(_forage._dbInfo);
|
|
}
|
|
}))["catch"]((function(err) {
|
|
(_rejectReadiness(options, err) || Promise$1.resolve())["catch"]((function() {}));
|
|
throw err;
|
|
}));
|
|
}));
|
|
} else {
|
|
promise = dbPromise.then((function(db) {
|
|
if (!db.objectStoreNames.contains(options.storeName)) {
|
|
return;
|
|
}
|
|
var newVersion = db.version + 1;
|
|
_deferReadiness(options);
|
|
var dbContext = dbContexts[options.name];
|
|
var forages = dbContext.forages;
|
|
db.close();
|
|
for (var i = 0; i < forages.length; i++) {
|
|
var forage = forages[i];
|
|
forage._dbInfo.db = null;
|
|
forage._dbInfo.version = newVersion;
|
|
}
|
|
var dropObjectPromise = new Promise$1((function(resolve, reject) {
|
|
var req = idb.open(options.name, newVersion);
|
|
req.onerror = function(err) {
|
|
var db = req.result;
|
|
db.close();
|
|
reject(err);
|
|
};
|
|
req.onupgradeneeded = function() {
|
|
var db = req.result;
|
|
db.deleteObjectStore(options.storeName);
|
|
};
|
|
req.onsuccess = function() {
|
|
var db = req.result;
|
|
db.close();
|
|
resolve(db);
|
|
};
|
|
}));
|
|
return dropObjectPromise.then((function(db) {
|
|
dbContext.db = db;
|
|
for (var j = 0; j < forages.length; j++) {
|
|
var _forage2 = forages[j];
|
|
_forage2._dbInfo.db = db;
|
|
_advanceReadiness(_forage2._dbInfo);
|
|
}
|
|
}))["catch"]((function(err) {
|
|
(_rejectReadiness(options, err) || Promise$1.resolve())["catch"]((function() {}));
|
|
throw err;
|
|
}));
|
|
}));
|
|
}
|
|
}
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
var asyncStorage = {
|
|
_driver: "asyncStorage",
|
|
_initStorage,
|
|
_support: isIndexedDBValid(),
|
|
iterate,
|
|
getItem,
|
|
setItem,
|
|
removeItem,
|
|
clear,
|
|
length,
|
|
key,
|
|
keys,
|
|
dropInstance
|
|
};
|
|
function isWebSQLValid() {
|
|
return typeof openDatabase === "function";
|
|
}
|
|
var BASE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
var BLOB_TYPE_PREFIX = "~~local_forage_type~";
|
|
var BLOB_TYPE_PREFIX_REGEX = /^~~local_forage_type~([^~]+)~/;
|
|
var SERIALIZED_MARKER = "__lfsc__:";
|
|
var SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER.length;
|
|
var TYPE_ARRAYBUFFER = "arbf";
|
|
var TYPE_BLOB = "blob";
|
|
var TYPE_INT8ARRAY = "si08";
|
|
var TYPE_UINT8ARRAY = "ui08";
|
|
var TYPE_UINT8CLAMPEDARRAY = "uic8";
|
|
var TYPE_INT16ARRAY = "si16";
|
|
var TYPE_INT32ARRAY = "si32";
|
|
var TYPE_UINT16ARRAY = "ur16";
|
|
var TYPE_UINT32ARRAY = "ui32";
|
|
var TYPE_FLOAT32ARRAY = "fl32";
|
|
var TYPE_FLOAT64ARRAY = "fl64";
|
|
var TYPE_SERIALIZED_MARKER_LENGTH = SERIALIZED_MARKER_LENGTH + TYPE_ARRAYBUFFER.length;
|
|
var toString$1 = Object.prototype.toString;
|
|
function stringToBuffer(serializedString) {
|
|
var bufferLength = serializedString.length * .75;
|
|
var len = serializedString.length;
|
|
var i;
|
|
var p = 0;
|
|
var encoded1, encoded2, encoded3, encoded4;
|
|
if (serializedString[serializedString.length - 1] === "=") {
|
|
bufferLength--;
|
|
if (serializedString[serializedString.length - 2] === "=") {
|
|
bufferLength--;
|
|
}
|
|
}
|
|
var buffer = new ArrayBuffer(bufferLength);
|
|
var bytes = new Uint8Array(buffer);
|
|
for (i = 0; i < len; i += 4) {
|
|
encoded1 = BASE_CHARS.indexOf(serializedString[i]);
|
|
encoded2 = BASE_CHARS.indexOf(serializedString[i + 1]);
|
|
encoded3 = BASE_CHARS.indexOf(serializedString[i + 2]);
|
|
encoded4 = BASE_CHARS.indexOf(serializedString[i + 3]);
|
|
bytes[p++] = encoded1 << 2 | encoded2 >> 4;
|
|
bytes[p++] = (encoded2 & 15) << 4 | encoded3 >> 2;
|
|
bytes[p++] = (encoded3 & 3) << 6 | encoded4 & 63;
|
|
}
|
|
return buffer;
|
|
}
|
|
function bufferToString(buffer) {
|
|
var bytes = new Uint8Array(buffer);
|
|
var base64String = "";
|
|
var i;
|
|
for (i = 0; i < bytes.length; i += 3) {
|
|
base64String += BASE_CHARS[bytes[i] >> 2];
|
|
base64String += BASE_CHARS[(bytes[i] & 3) << 4 | bytes[i + 1] >> 4];
|
|
base64String += BASE_CHARS[(bytes[i + 1] & 15) << 2 | bytes[i + 2] >> 6];
|
|
base64String += BASE_CHARS[bytes[i + 2] & 63];
|
|
}
|
|
if (bytes.length % 3 === 2) {
|
|
base64String = base64String.substring(0, base64String.length - 1) + "=";
|
|
} else if (bytes.length % 3 === 1) {
|
|
base64String = base64String.substring(0, base64String.length - 2) + "==";
|
|
}
|
|
return base64String;
|
|
}
|
|
function serialize(value, callback) {
|
|
var valueType = "";
|
|
if (value) {
|
|
valueType = toString$1.call(value);
|
|
}
|
|
if (value && (valueType === "[object ArrayBuffer]" || value.buffer && toString$1.call(value.buffer) === "[object ArrayBuffer]")) {
|
|
var buffer;
|
|
var marker = SERIALIZED_MARKER;
|
|
if (value instanceof ArrayBuffer) {
|
|
buffer = value;
|
|
marker += TYPE_ARRAYBUFFER;
|
|
} else {
|
|
buffer = value.buffer;
|
|
if (valueType === "[object Int8Array]") {
|
|
marker += TYPE_INT8ARRAY;
|
|
} else if (valueType === "[object Uint8Array]") {
|
|
marker += TYPE_UINT8ARRAY;
|
|
} else if (valueType === "[object Uint8ClampedArray]") {
|
|
marker += TYPE_UINT8CLAMPEDARRAY;
|
|
} else if (valueType === "[object Int16Array]") {
|
|
marker += TYPE_INT16ARRAY;
|
|
} else if (valueType === "[object Uint16Array]") {
|
|
marker += TYPE_UINT16ARRAY;
|
|
} else if (valueType === "[object Int32Array]") {
|
|
marker += TYPE_INT32ARRAY;
|
|
} else if (valueType === "[object Uint32Array]") {
|
|
marker += TYPE_UINT32ARRAY;
|
|
} else if (valueType === "[object Float32Array]") {
|
|
marker += TYPE_FLOAT32ARRAY;
|
|
} else if (valueType === "[object Float64Array]") {
|
|
marker += TYPE_FLOAT64ARRAY;
|
|
} else {
|
|
callback(new Error("Failed to get type for BinaryArray"));
|
|
}
|
|
}
|
|
callback(marker + bufferToString(buffer));
|
|
} else if (valueType === "[object Blob]") {
|
|
var fileReader = new FileReader;
|
|
fileReader.onload = function() {
|
|
var str = BLOB_TYPE_PREFIX + value.type + "~" + bufferToString(this.result);
|
|
callback(SERIALIZED_MARKER + TYPE_BLOB + str);
|
|
};
|
|
fileReader.readAsArrayBuffer(value);
|
|
} else {
|
|
try {
|
|
callback(JSON.stringify(value));
|
|
} catch (e) {
|
|
console.error("Couldn't convert value into a JSON string: ", value);
|
|
callback(null, e);
|
|
}
|
|
}
|
|
}
|
|
function deserialize(value) {
|
|
if (value.substring(0, SERIALIZED_MARKER_LENGTH) !== SERIALIZED_MARKER) {
|
|
return JSON.parse(value);
|
|
}
|
|
var serializedString = value.substring(TYPE_SERIALIZED_MARKER_LENGTH);
|
|
var type = value.substring(SERIALIZED_MARKER_LENGTH, TYPE_SERIALIZED_MARKER_LENGTH);
|
|
var blobType;
|
|
if (type === TYPE_BLOB && BLOB_TYPE_PREFIX_REGEX.test(serializedString)) {
|
|
var matcher = serializedString.match(BLOB_TYPE_PREFIX_REGEX);
|
|
blobType = matcher[1];
|
|
serializedString = serializedString.substring(matcher[0].length);
|
|
}
|
|
var buffer = stringToBuffer(serializedString);
|
|
switch (type) {
|
|
case TYPE_ARRAYBUFFER:
|
|
return buffer;
|
|
|
|
case TYPE_BLOB:
|
|
return createBlob([ buffer ], {
|
|
type: blobType
|
|
});
|
|
|
|
case TYPE_INT8ARRAY:
|
|
return new Int8Array(buffer);
|
|
|
|
case TYPE_UINT8ARRAY:
|
|
return new Uint8Array(buffer);
|
|
|
|
case TYPE_UINT8CLAMPEDARRAY:
|
|
return new Uint8ClampedArray(buffer);
|
|
|
|
case TYPE_INT16ARRAY:
|
|
return new Int16Array(buffer);
|
|
|
|
case TYPE_UINT16ARRAY:
|
|
return new Uint16Array(buffer);
|
|
|
|
case TYPE_INT32ARRAY:
|
|
return new Int32Array(buffer);
|
|
|
|
case TYPE_UINT32ARRAY:
|
|
return new Uint32Array(buffer);
|
|
|
|
case TYPE_FLOAT32ARRAY:
|
|
return new Float32Array(buffer);
|
|
|
|
case TYPE_FLOAT64ARRAY:
|
|
return new Float64Array(buffer);
|
|
|
|
default:
|
|
throw new Error("Unkown type: " + type);
|
|
}
|
|
}
|
|
var localforageSerializer = {
|
|
serialize,
|
|
deserialize,
|
|
stringToBuffer,
|
|
bufferToString
|
|
};
|
|
function createDbTable(t, dbInfo, callback, errorCallback) {
|
|
t.executeSql("CREATE TABLE IF NOT EXISTS " + dbInfo.storeName + " " + "(id INTEGER PRIMARY KEY, key unique, value)", [], callback, errorCallback);
|
|
}
|
|
function _initStorage$1(options) {
|
|
var self = this;
|
|
var dbInfo = {
|
|
db: null
|
|
};
|
|
if (options) {
|
|
for (var i in options) {
|
|
dbInfo[i] = typeof options[i] !== "string" ? options[i].toString() : options[i];
|
|
}
|
|
}
|
|
var dbInfoPromise = new Promise$1((function(resolve, reject) {
|
|
try {
|
|
dbInfo.db = openDatabase(dbInfo.name, String(dbInfo.version), dbInfo.description, dbInfo.size);
|
|
} catch (e) {
|
|
return reject(e);
|
|
}
|
|
dbInfo.db.transaction((function(t) {
|
|
createDbTable(t, dbInfo, (function() {
|
|
self._dbInfo = dbInfo;
|
|
resolve();
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}), reject);
|
|
}));
|
|
dbInfo.serializer = localforageSerializer;
|
|
return dbInfoPromise;
|
|
}
|
|
function tryExecuteSql(t, dbInfo, sqlStatement, args, callback, errorCallback) {
|
|
t.executeSql(sqlStatement, args, callback, (function(t, error) {
|
|
if (error.code === error.SYNTAX_ERR) {
|
|
t.executeSql("SELECT name FROM sqlite_master " + "WHERE type='table' AND name = ?", [ dbInfo.storeName ], (function(t, results) {
|
|
if (!results.rows.length) {
|
|
createDbTable(t, dbInfo, (function() {
|
|
t.executeSql(sqlStatement, args, callback, errorCallback);
|
|
}), errorCallback);
|
|
} else {
|
|
errorCallback(t, error);
|
|
}
|
|
}), errorCallback);
|
|
} else {
|
|
errorCallback(t, error);
|
|
}
|
|
}), errorCallback);
|
|
}
|
|
function getItem$1(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName + " WHERE key = ? LIMIT 1", [ key ], (function(t, results) {
|
|
var result = results.rows.length ? results.rows.item(0).value : null;
|
|
if (result) {
|
|
result = dbInfo.serializer.deserialize(result);
|
|
}
|
|
resolve(result);
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function iterate$1(iterator, callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "SELECT * FROM " + dbInfo.storeName, [], (function(t, results) {
|
|
var rows = results.rows;
|
|
var length = rows.length;
|
|
for (var i = 0; i < length; i++) {
|
|
var item = rows.item(i);
|
|
var result = item.value;
|
|
if (result) {
|
|
result = dbInfo.serializer.deserialize(result);
|
|
}
|
|
result = iterator(result, item.key, i + 1);
|
|
if (result !== void 0) {
|
|
resolve(result);
|
|
return;
|
|
}
|
|
}
|
|
resolve();
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function _setItem(key, value, callback, retriesLeft) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
var originalValue = value;
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.serializer.serialize(value, (function(value, error) {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "INSERT OR REPLACE INTO " + dbInfo.storeName + " " + "(key, value) VALUES (?, ?)", [ key, value ], (function() {
|
|
resolve(originalValue);
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}), (function(sqlError) {
|
|
if (sqlError.code === sqlError.QUOTA_ERR) {
|
|
if (retriesLeft > 0) {
|
|
resolve(_setItem.apply(self, [ key, originalValue, callback, retriesLeft - 1 ]));
|
|
return;
|
|
}
|
|
reject(sqlError);
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function setItem$1(key, value, callback) {
|
|
return _setItem.apply(this, [ key, value, callback, 1 ]);
|
|
}
|
|
function removeItem$1(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName + " WHERE key = ?", [ key ], (function() {
|
|
resolve();
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function clear$1(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "DELETE FROM " + dbInfo.storeName, [], (function() {
|
|
resolve();
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function length$1(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "SELECT COUNT(key) as c FROM " + dbInfo.storeName, [], (function(t, results) {
|
|
var result = results.rows.item(0).c;
|
|
resolve(result);
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function key$1(n, callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName + " WHERE id = ? LIMIT 1", [ n + 1 ], (function(t, results) {
|
|
var result = results.rows.length ? results.rows.item(0).key : null;
|
|
resolve(result);
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function keys$1(callback) {
|
|
var self = this;
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.db.transaction((function(t) {
|
|
tryExecuteSql(t, dbInfo, "SELECT key FROM " + dbInfo.storeName, [], (function(t, results) {
|
|
var keys = [];
|
|
for (var i = 0; i < results.rows.length; i++) {
|
|
keys.push(results.rows.item(i).key);
|
|
}
|
|
resolve(keys);
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}))["catch"](reject);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function getAllStoreNames(db) {
|
|
return new Promise$1((function(resolve, reject) {
|
|
db.transaction((function(t) {
|
|
t.executeSql("SELECT name FROM sqlite_master " + "WHERE type='table' AND name <> '__WebKitDatabaseInfoTable__'", [], (function(t, results) {
|
|
var storeNames = [];
|
|
for (var i = 0; i < results.rows.length; i++) {
|
|
storeNames.push(results.rows.item(i).name);
|
|
}
|
|
resolve({
|
|
db,
|
|
storeNames
|
|
});
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}), (function(sqlError) {
|
|
reject(sqlError);
|
|
}));
|
|
}));
|
|
}
|
|
function dropInstance$1(options, callback) {
|
|
callback = getCallback.apply(this, arguments);
|
|
var currentConfig = this.config();
|
|
options = typeof options !== "function" && options || {};
|
|
if (!options.name) {
|
|
options.name = options.name || currentConfig.name;
|
|
options.storeName = options.storeName || currentConfig.storeName;
|
|
}
|
|
var self = this;
|
|
var promise;
|
|
if (!options.name) {
|
|
promise = Promise$1.reject("Invalid arguments");
|
|
} else {
|
|
promise = new Promise$1((function(resolve) {
|
|
var db;
|
|
if (options.name === currentConfig.name) {
|
|
db = self._dbInfo.db;
|
|
} else {
|
|
db = openDatabase(options.name, "", "", 0);
|
|
}
|
|
if (!options.storeName) {
|
|
resolve(getAllStoreNames(db));
|
|
} else {
|
|
resolve({
|
|
db,
|
|
storeNames: [ options.storeName ]
|
|
});
|
|
}
|
|
})).then((function(operationInfo) {
|
|
return new Promise$1((function(resolve, reject) {
|
|
operationInfo.db.transaction((function(t) {
|
|
function dropTable(storeName) {
|
|
return new Promise$1((function(resolve, reject) {
|
|
t.executeSql("DROP TABLE IF EXISTS " + storeName, [], (function() {
|
|
resolve();
|
|
}), (function(t, error) {
|
|
reject(error);
|
|
}));
|
|
}));
|
|
}
|
|
var operations = [];
|
|
for (var i = 0, len = operationInfo.storeNames.length; i < len; i++) {
|
|
operations.push(dropTable(operationInfo.storeNames[i]));
|
|
}
|
|
Promise$1.all(operations).then((function() {
|
|
resolve();
|
|
}))["catch"]((function(e) {
|
|
reject(e);
|
|
}));
|
|
}), (function(sqlError) {
|
|
reject(sqlError);
|
|
}));
|
|
}));
|
|
}));
|
|
}
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
var webSQLStorage = {
|
|
_driver: "webSQLStorage",
|
|
_initStorage: _initStorage$1,
|
|
_support: isWebSQLValid(),
|
|
iterate: iterate$1,
|
|
getItem: getItem$1,
|
|
setItem: setItem$1,
|
|
removeItem: removeItem$1,
|
|
clear: clear$1,
|
|
length: length$1,
|
|
key: key$1,
|
|
keys: keys$1,
|
|
dropInstance: dropInstance$1
|
|
};
|
|
function isLocalStorageValid() {
|
|
try {
|
|
return typeof localStorage !== "undefined" && "setItem" in localStorage && !!localStorage.setItem;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
}
|
|
function _getKeyPrefix(options, defaultConfig) {
|
|
var keyPrefix = options.name + "/";
|
|
if (options.storeName !== defaultConfig.storeName) {
|
|
keyPrefix += options.storeName + "/";
|
|
}
|
|
return keyPrefix;
|
|
}
|
|
function checkIfLocalStorageThrows() {
|
|
var localStorageTestKey = "_localforage_support_test";
|
|
try {
|
|
localStorage.setItem(localStorageTestKey, true);
|
|
localStorage.removeItem(localStorageTestKey);
|
|
return false;
|
|
} catch (e) {
|
|
return true;
|
|
}
|
|
}
|
|
function _isLocalStorageUsable() {
|
|
return !checkIfLocalStorageThrows() || localStorage.length > 0;
|
|
}
|
|
function _initStorage$2(options) {
|
|
var self = this;
|
|
var dbInfo = {};
|
|
if (options) {
|
|
for (var i in options) {
|
|
dbInfo[i] = options[i];
|
|
}
|
|
}
|
|
dbInfo.keyPrefix = _getKeyPrefix(options, self._defaultConfig);
|
|
if (!_isLocalStorageUsable()) {
|
|
return Promise$1.reject();
|
|
}
|
|
self._dbInfo = dbInfo;
|
|
dbInfo.serializer = localforageSerializer;
|
|
return Promise$1.resolve();
|
|
}
|
|
function clear$2(callback) {
|
|
var self = this;
|
|
var promise = self.ready().then((function() {
|
|
var keyPrefix = self._dbInfo.keyPrefix;
|
|
for (var i = localStorage.length - 1; i >= 0; i--) {
|
|
var key = localStorage.key(i);
|
|
if (key.indexOf(keyPrefix) === 0) {
|
|
localStorage.removeItem(key);
|
|
}
|
|
}
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function getItem$2(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
var result = localStorage.getItem(dbInfo.keyPrefix + key);
|
|
if (result) {
|
|
result = dbInfo.serializer.deserialize(result);
|
|
}
|
|
return result;
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function iterate$2(iterator, callback) {
|
|
var self = this;
|
|
var promise = self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
var keyPrefix = dbInfo.keyPrefix;
|
|
var keyPrefixLength = keyPrefix.length;
|
|
var length = localStorage.length;
|
|
var iterationNumber = 1;
|
|
for (var i = 0; i < length; i++) {
|
|
var key = localStorage.key(i);
|
|
if (key.indexOf(keyPrefix) !== 0) {
|
|
continue;
|
|
}
|
|
var value = localStorage.getItem(key);
|
|
if (value) {
|
|
value = dbInfo.serializer.deserialize(value);
|
|
}
|
|
value = iterator(value, key.substring(keyPrefixLength), iterationNumber++);
|
|
if (value !== void 0) {
|
|
return value;
|
|
}
|
|
}
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function key$2(n, callback) {
|
|
var self = this;
|
|
var promise = self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
var result;
|
|
try {
|
|
result = localStorage.key(n);
|
|
} catch (error) {
|
|
result = null;
|
|
}
|
|
if (result) {
|
|
result = result.substring(dbInfo.keyPrefix.length);
|
|
}
|
|
return result;
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function keys$2(callback) {
|
|
var self = this;
|
|
var promise = self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
var length = localStorage.length;
|
|
var keys = [];
|
|
for (var i = 0; i < length; i++) {
|
|
var itemKey = localStorage.key(i);
|
|
if (itemKey.indexOf(dbInfo.keyPrefix) === 0) {
|
|
keys.push(itemKey.substring(dbInfo.keyPrefix.length));
|
|
}
|
|
}
|
|
return keys;
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function length$2(callback) {
|
|
var self = this;
|
|
var promise = self.keys().then((function(keys) {
|
|
return keys.length;
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function removeItem$2(key, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = self.ready().then((function() {
|
|
var dbInfo = self._dbInfo;
|
|
localStorage.removeItem(dbInfo.keyPrefix + key);
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function setItem$2(key, value, callback) {
|
|
var self = this;
|
|
key = normalizeKey(key);
|
|
var promise = self.ready().then((function() {
|
|
if (value === undefined) {
|
|
value = null;
|
|
}
|
|
var originalValue = value;
|
|
return new Promise$1((function(resolve, reject) {
|
|
var dbInfo = self._dbInfo;
|
|
dbInfo.serializer.serialize(value, (function(value, error) {
|
|
if (error) {
|
|
reject(error);
|
|
} else {
|
|
try {
|
|
localStorage.setItem(dbInfo.keyPrefix + key, value);
|
|
resolve(originalValue);
|
|
} catch (e) {
|
|
if (e.name === "QuotaExceededError" || e.name === "NS_ERROR_DOM_QUOTA_REACHED") {
|
|
reject(e);
|
|
}
|
|
reject(e);
|
|
}
|
|
}
|
|
}));
|
|
}));
|
|
}));
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
function dropInstance$2(options, callback) {
|
|
callback = getCallback.apply(this, arguments);
|
|
options = typeof options !== "function" && options || {};
|
|
if (!options.name) {
|
|
var currentConfig = this.config();
|
|
options.name = options.name || currentConfig.name;
|
|
options.storeName = options.storeName || currentConfig.storeName;
|
|
}
|
|
var self = this;
|
|
var promise;
|
|
if (!options.name) {
|
|
promise = Promise$1.reject("Invalid arguments");
|
|
} else {
|
|
promise = new Promise$1((function(resolve) {
|
|
if (!options.storeName) {
|
|
resolve(options.name + "/");
|
|
} else {
|
|
resolve(_getKeyPrefix(options, self._defaultConfig));
|
|
}
|
|
})).then((function(keyPrefix) {
|
|
for (var i = localStorage.length - 1; i >= 0; i--) {
|
|
var key = localStorage.key(i);
|
|
if (key.indexOf(keyPrefix) === 0) {
|
|
localStorage.removeItem(key);
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
executeCallback(promise, callback);
|
|
return promise;
|
|
}
|
|
var localStorageWrapper = {
|
|
_driver: "localStorageWrapper",
|
|
_initStorage: _initStorage$2,
|
|
_support: isLocalStorageValid(),
|
|
iterate: iterate$2,
|
|
getItem: getItem$2,
|
|
setItem: setItem$2,
|
|
removeItem: removeItem$2,
|
|
clear: clear$2,
|
|
length: length$2,
|
|
key: key$2,
|
|
keys: keys$2,
|
|
dropInstance: dropInstance$2
|
|
};
|
|
var sameValue = function sameValue(x, y) {
|
|
return x === y || typeof x === "number" && typeof y === "number" && isNaN(x) && isNaN(y);
|
|
};
|
|
var includes = function includes(array, searchElement) {
|
|
var len = array.length;
|
|
var i = 0;
|
|
while (i < len) {
|
|
if (sameValue(array[i], searchElement)) {
|
|
return true;
|
|
}
|
|
i++;
|
|
}
|
|
return false;
|
|
};
|
|
var isArray = Array.isArray || function(arg) {
|
|
return Object.prototype.toString.call(arg) === "[object Array]";
|
|
};
|
|
var DefinedDrivers = {};
|
|
var DriverSupport = {};
|
|
var DefaultDrivers = {
|
|
INDEXEDDB: asyncStorage,
|
|
WEBSQL: webSQLStorage,
|
|
LOCALSTORAGE: localStorageWrapper
|
|
};
|
|
var DefaultDriverOrder = [ DefaultDrivers.INDEXEDDB._driver, DefaultDrivers.WEBSQL._driver, DefaultDrivers.LOCALSTORAGE._driver ];
|
|
var OptionalDriverMethods = [ "dropInstance" ];
|
|
var LibraryMethods = [ "clear", "getItem", "iterate", "key", "keys", "length", "removeItem", "setItem" ].concat(OptionalDriverMethods);
|
|
var DefaultConfig = {
|
|
description: "",
|
|
driver: DefaultDriverOrder.slice(),
|
|
name: "localforage",
|
|
size: 4980736,
|
|
storeName: "keyvaluepairs",
|
|
version: 1
|
|
};
|
|
function callWhenReady(localForageInstance, libraryMethod) {
|
|
localForageInstance[libraryMethod] = function() {
|
|
var _args = arguments;
|
|
return localForageInstance.ready().then((function() {
|
|
return localForageInstance[libraryMethod].apply(localForageInstance, _args);
|
|
}));
|
|
};
|
|
}
|
|
function extend() {
|
|
for (var i = 1; i < arguments.length; i++) {
|
|
var arg = arguments[i];
|
|
if (arg) {
|
|
for (var _key in arg) {
|
|
if (arg.hasOwnProperty(_key)) {
|
|
if (isArray(arg[_key])) {
|
|
arguments[0][_key] = arg[_key].slice();
|
|
} else {
|
|
arguments[0][_key] = arg[_key];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return arguments[0];
|
|
}
|
|
var LocalForage = function() {
|
|
function LocalForage(options) {
|
|
_classCallCheck(this, LocalForage);
|
|
for (var driverTypeKey in DefaultDrivers) {
|
|
if (DefaultDrivers.hasOwnProperty(driverTypeKey)) {
|
|
var driver = DefaultDrivers[driverTypeKey];
|
|
var driverName = driver._driver;
|
|
this[driverTypeKey] = driverName;
|
|
if (!DefinedDrivers[driverName]) {
|
|
this.defineDriver(driver);
|
|
}
|
|
}
|
|
}
|
|
this._defaultConfig = extend({}, DefaultConfig);
|
|
this._config = extend({}, this._defaultConfig, options);
|
|
this._driverSet = null;
|
|
this._initDriver = null;
|
|
this._ready = false;
|
|
this._dbInfo = null;
|
|
this._wrapLibraryMethodsWithReady();
|
|
this.setDriver(this._config.driver)["catch"]((function() {}));
|
|
}
|
|
LocalForage.prototype.config = function config(options) {
|
|
if ((typeof options === "undefined" ? "undefined" : _typeof(options)) === "object") {
|
|
if (this._ready) {
|
|
return new Error("Can't call config() after localforage " + "has been used.");
|
|
}
|
|
for (var i in options) {
|
|
if (i === "storeName") {
|
|
options[i] = options[i].replace(/\W/g, "_");
|
|
}
|
|
if (i === "version" && typeof options[i] !== "number") {
|
|
return new Error("Database version must be a number.");
|
|
}
|
|
this._config[i] = options[i];
|
|
}
|
|
if ("driver" in options && options.driver) {
|
|
return this.setDriver(this._config.driver);
|
|
}
|
|
return true;
|
|
} else if (typeof options === "string") {
|
|
return this._config[options];
|
|
} else {
|
|
return this._config;
|
|
}
|
|
};
|
|
LocalForage.prototype.defineDriver = function defineDriver(driverObject, callback, errorCallback) {
|
|
var promise = new Promise$1((function(resolve, reject) {
|
|
try {
|
|
var driverName = driverObject._driver;
|
|
var complianceError = new Error("Custom driver not compliant; see " + "https://mozilla.github.io/localForage/#definedriver");
|
|
if (!driverObject._driver) {
|
|
reject(complianceError);
|
|
return;
|
|
}
|
|
var driverMethods = LibraryMethods.concat("_initStorage");
|
|
for (var i = 0, len = driverMethods.length; i < len; i++) {
|
|
var driverMethodName = driverMethods[i];
|
|
var isRequired = !includes(OptionalDriverMethods, driverMethodName);
|
|
if ((isRequired || driverObject[driverMethodName]) && typeof driverObject[driverMethodName] !== "function") {
|
|
reject(complianceError);
|
|
return;
|
|
}
|
|
}
|
|
var configureMissingMethods = function configureMissingMethods() {
|
|
var methodNotImplementedFactory = function methodNotImplementedFactory(methodName) {
|
|
return function() {
|
|
var error = new Error("Method " + methodName + " is not implemented by the current driver");
|
|
var promise = Promise$1.reject(error);
|
|
executeCallback(promise, arguments[arguments.length - 1]);
|
|
return promise;
|
|
};
|
|
};
|
|
for (var _i = 0, _len = OptionalDriverMethods.length; _i < _len; _i++) {
|
|
var optionalDriverMethod = OptionalDriverMethods[_i];
|
|
if (!driverObject[optionalDriverMethod]) {
|
|
driverObject[optionalDriverMethod] = methodNotImplementedFactory(optionalDriverMethod);
|
|
}
|
|
}
|
|
};
|
|
configureMissingMethods();
|
|
var setDriverSupport = function setDriverSupport(support) {
|
|
if (DefinedDrivers[driverName]) {
|
|
console.info("Redefining LocalForage driver: " + driverName);
|
|
}
|
|
DefinedDrivers[driverName] = driverObject;
|
|
DriverSupport[driverName] = support;
|
|
resolve();
|
|
};
|
|
if ("_support" in driverObject) {
|
|
if (driverObject._support && typeof driverObject._support === "function") {
|
|
driverObject._support().then(setDriverSupport, reject);
|
|
} else {
|
|
setDriverSupport(!!driverObject._support);
|
|
}
|
|
} else {
|
|
setDriverSupport(true);
|
|
}
|
|
} catch (e) {
|
|
reject(e);
|
|
}
|
|
}));
|
|
executeTwoCallbacks(promise, callback, errorCallback);
|
|
return promise;
|
|
};
|
|
LocalForage.prototype.driver = function driver() {
|
|
return this._driver || null;
|
|
};
|
|
LocalForage.prototype.getDriver = function getDriver(driverName, callback, errorCallback) {
|
|
var getDriverPromise = DefinedDrivers[driverName] ? Promise$1.resolve(DefinedDrivers[driverName]) : Promise$1.reject(new Error("Driver not found."));
|
|
executeTwoCallbacks(getDriverPromise, callback, errorCallback);
|
|
return getDriverPromise;
|
|
};
|
|
LocalForage.prototype.getSerializer = function getSerializer(callback) {
|
|
var serializerPromise = Promise$1.resolve(localforageSerializer);
|
|
executeTwoCallbacks(serializerPromise, callback);
|
|
return serializerPromise;
|
|
};
|
|
LocalForage.prototype.ready = function ready(callback) {
|
|
var self = this;
|
|
var promise = self._driverSet.then((function() {
|
|
if (self._ready === null) {
|
|
self._ready = self._initDriver();
|
|
}
|
|
return self._ready;
|
|
}));
|
|
executeTwoCallbacks(promise, callback, callback);
|
|
return promise;
|
|
};
|
|
LocalForage.prototype.setDriver = function setDriver(drivers, callback, errorCallback) {
|
|
var self = this;
|
|
if (!isArray(drivers)) {
|
|
drivers = [ drivers ];
|
|
}
|
|
var supportedDrivers = this._getSupportedDrivers(drivers);
|
|
function setDriverToConfig() {
|
|
self._config.driver = self.driver();
|
|
}
|
|
function extendSelfWithDriver(driver) {
|
|
self._extend(driver);
|
|
setDriverToConfig();
|
|
self._ready = self._initStorage(self._config);
|
|
return self._ready;
|
|
}
|
|
function initDriver(supportedDrivers) {
|
|
return function() {
|
|
var currentDriverIndex = 0;
|
|
function driverPromiseLoop() {
|
|
while (currentDriverIndex < supportedDrivers.length) {
|
|
var driverName = supportedDrivers[currentDriverIndex];
|
|
currentDriverIndex++;
|
|
self._dbInfo = null;
|
|
self._ready = null;
|
|
return self.getDriver(driverName).then(extendSelfWithDriver)["catch"](driverPromiseLoop);
|
|
}
|
|
setDriverToConfig();
|
|
var error = new Error("No available storage method found.");
|
|
self._driverSet = Promise$1.reject(error);
|
|
return self._driverSet;
|
|
}
|
|
return driverPromiseLoop();
|
|
};
|
|
}
|
|
var oldDriverSetDone = this._driverSet !== null ? this._driverSet["catch"]((function() {
|
|
return Promise$1.resolve();
|
|
})) : Promise$1.resolve();
|
|
this._driverSet = oldDriverSetDone.then((function() {
|
|
var driverName = supportedDrivers[0];
|
|
self._dbInfo = null;
|
|
self._ready = null;
|
|
return self.getDriver(driverName).then((function(driver) {
|
|
self._driver = driver._driver;
|
|
setDriverToConfig();
|
|
self._wrapLibraryMethodsWithReady();
|
|
self._initDriver = initDriver(supportedDrivers);
|
|
}));
|
|
}))["catch"]((function() {
|
|
setDriverToConfig();
|
|
var error = new Error("No available storage method found.");
|
|
self._driverSet = Promise$1.reject(error);
|
|
return self._driverSet;
|
|
}));
|
|
executeTwoCallbacks(this._driverSet, callback, errorCallback);
|
|
return this._driverSet;
|
|
};
|
|
LocalForage.prototype.supports = function supports(driverName) {
|
|
return !!DriverSupport[driverName];
|
|
};
|
|
LocalForage.prototype._extend = function _extend(libraryMethodsAndProperties) {
|
|
extend(this, libraryMethodsAndProperties);
|
|
};
|
|
LocalForage.prototype._getSupportedDrivers = function _getSupportedDrivers(drivers) {
|
|
var supportedDrivers = [];
|
|
for (var i = 0, len = drivers.length; i < len; i++) {
|
|
var driverName = drivers[i];
|
|
if (this.supports(driverName)) {
|
|
supportedDrivers.push(driverName);
|
|
}
|
|
}
|
|
return supportedDrivers;
|
|
};
|
|
LocalForage.prototype._wrapLibraryMethodsWithReady = function _wrapLibraryMethodsWithReady() {
|
|
for (var i = 0, len = LibraryMethods.length; i < len; i++) {
|
|
callWhenReady(this, LibraryMethods[i]);
|
|
}
|
|
};
|
|
LocalForage.prototype.createInstance = function createInstance(options) {
|
|
return new LocalForage(options);
|
|
};
|
|
return LocalForage;
|
|
}();
|
|
var localforage_js = new LocalForage;
|
|
module.exports = localforage_js;
|
|
}, {
|
|
3: 3
|
|
} ]
|
|
}, {}, [ 4 ])(4);
|
|
}));
|
|
})(localforage);
|
|
return localforage.exports;
|
|
}
|
|
var hasRequiredCacheStore;
|
|
function requireCacheStore() {
|
|
if (hasRequiredCacheStore) return cacheStore$1.exports;
|
|
hasRequiredCacheStore = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, localForage) => {
|
|
let window = globalThis;
|
|
let self = globalThis;
|
|
var S = window.CryptPad_Cache = {};
|
|
var onReady = Util.mkEvent(true);
|
|
var allowed = false;
|
|
var disabled = false;
|
|
var supported = false;
|
|
try {
|
|
var request = window.indexedDB.open("test_db", 1);
|
|
request.onsuccess = function() {
|
|
supported = true;
|
|
allowed = supported && !disabled;
|
|
onReady.fire();
|
|
};
|
|
request.onerror = function() {
|
|
onReady.fire();
|
|
};
|
|
} catch (e) {
|
|
onReady.fire();
|
|
}
|
|
S.enable = function() {
|
|
disabled = false;
|
|
allowed = supported && !disabled;
|
|
};
|
|
S.disable = function() {
|
|
disabled = true;
|
|
allowed = supported && !disabled;
|
|
};
|
|
S.isEnabled = () => allowed;
|
|
var cache = localForage.createInstance({
|
|
driver: localForage.INDEXEDDB,
|
|
name: "cp_cache"
|
|
});
|
|
S.getBlobCache = function(id, cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.getItem(id, (function(err, obj) {
|
|
if (err || !obj || !obj.c) {
|
|
return void cb(Util.serializeError(err || "EINVAL"));
|
|
}
|
|
cb(null, obj.c);
|
|
obj.t = +new Date;
|
|
cache.setItem(id, obj, (function(err) {
|
|
if (!err) {
|
|
return;
|
|
}
|
|
console.error(err);
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
S.setBlobCache = function(id, u8, cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
if (!u8) {
|
|
return void cb("EINVAL");
|
|
}
|
|
cache.setItem(id, {
|
|
c: u8,
|
|
t: +new Date
|
|
}, (function(err) {
|
|
cb(Util.serializeError(err));
|
|
}));
|
|
}));
|
|
};
|
|
S.getChannelCache = function(id, cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.getItem(id, (function(err, obj) {
|
|
if (err || !obj || !Array.isArray(obj.c)) {
|
|
return void cb(Util.serializeError(err || "EINVAL"));
|
|
}
|
|
cb(null, obj);
|
|
obj.t = +new Date;
|
|
cache.setItem(id, obj, (function(err) {
|
|
if (!err) {
|
|
return;
|
|
}
|
|
console.error(err);
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
var checkCheckpoints = function(array) {
|
|
if (!Array.isArray(array)) {
|
|
return;
|
|
}
|
|
if (array.length > 100) {
|
|
array.splice(0, array.length - 100);
|
|
}
|
|
var firstCpIdx;
|
|
array.some((function(el, i) {
|
|
if (!el.isCheckpoint) {
|
|
return;
|
|
}
|
|
firstCpIdx = i;
|
|
return true;
|
|
}));
|
|
array.splice(0, firstCpIdx);
|
|
};
|
|
var t = {};
|
|
S.storeCache = function(id, validateKey, val, onError) {
|
|
onError = Util.once(Util.mkAsync(onError || function() {}));
|
|
onReady.reg((function() {
|
|
t[id] = t[id] || Util.throttle((function(validateKey, val, onError) {
|
|
if (!allowed) {
|
|
return void onError("NOCACHE");
|
|
}
|
|
if (!Array.isArray(val) || !validateKey) {
|
|
return void onError("EINVAL");
|
|
}
|
|
checkCheckpoints(val);
|
|
cache.setItem(id, {
|
|
k: validateKey,
|
|
c: val,
|
|
t: +new Date
|
|
}, (function(err) {
|
|
if (err) {
|
|
onError(Util.serializeError(err));
|
|
}
|
|
}));
|
|
}), 50);
|
|
t[id](validateKey, val, onError);
|
|
}));
|
|
};
|
|
S.leaveChannel = function(id) {
|
|
delete t[id];
|
|
};
|
|
S.clearChannel = function(id, cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.removeItem(id, (function() {
|
|
cb();
|
|
}));
|
|
}));
|
|
};
|
|
S.clear = function(cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.clear(cb);
|
|
}));
|
|
};
|
|
S.getKeys = function(cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.keys().then((function(keys) {
|
|
cb(null, keys);
|
|
})).catch((function(err) {
|
|
cb(err);
|
|
}));
|
|
}));
|
|
};
|
|
S.getTime = function(id, cb) {
|
|
cb = Util.once(Util.mkAsync(cb || function() {}));
|
|
onReady.reg((function() {
|
|
if (!allowed) {
|
|
return void cb("NOCACHE");
|
|
}
|
|
cache.getItem(id, (function(err, obj) {
|
|
if (err || !obj || !obj.c) {
|
|
return void cb(Util.serializeError(err || "EINVAL"));
|
|
}
|
|
cb(null, obj.t);
|
|
}));
|
|
}));
|
|
};
|
|
self.CryptPad_clearIndexedDB = S.clear;
|
|
return S;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireLocalforage());
|
|
}
|
|
})();
|
|
})(cacheStore$1);
|
|
return cacheStore$1.exports;
|
|
}
|
|
var cacheStoreExports = requireCacheStore();
|
|
var cacheStore = getDefaultExportFromCjs(cacheStoreExports);
|
|
var Cache = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: cacheStore
|
|
}, [ cacheStoreExports ]);
|
|
const onCacheReadyEvt$1 = commonUtilExports.mkEvent(true);
|
|
const onReadyEvt$1 = commonUtilExports.mkEvent(true);
|
|
const onDisconnectEvt$1 = commonUtilExports.mkEvent();
|
|
const onReconnectEvt$1 = commonUtilExports.mkEvent();
|
|
let ApiConfig = {};
|
|
const init$1 = config => {
|
|
var _a, _b;
|
|
const {broadcast, userHash, anonHash} = config;
|
|
const hash = userHash || anonHash || commonHashExports.createRandomHash("drive");
|
|
const store = config.store;
|
|
const updateProgress = function(data) {
|
|
data.type = "drive";
|
|
broadcast([], "LOADING_DRIVE", data);
|
|
};
|
|
const secret = commonHashExports.getSecrets("drive", hash);
|
|
const listmapConfig = {
|
|
data: {},
|
|
websocketURL: networkConfigExports.getWebsocketURL(),
|
|
network: (_a = config.store) === null || _a === void 0 ? void 0 : _a.network,
|
|
channel: secret.channel,
|
|
readOnly: false,
|
|
validateKey: ((_b = secret.keys) === null || _b === void 0 ? void 0 : _b.validateKey) || undefined,
|
|
crypto: cryptoExports.createEncryptor(secret.keys),
|
|
Cache,
|
|
userName: "fs",
|
|
logLevel: 1,
|
|
ChainPad,
|
|
updateProgress,
|
|
classic: true
|
|
};
|
|
const rt = globalThis.CP_account_rt = chainpadListmapExports.create(listmapConfig);
|
|
store.driveSecret = secret;
|
|
store.proxy = rt.proxy;
|
|
store.onRpcReadyEvt = commonUtilExports.mkEvent(true);
|
|
store.loggedIn = typeof config.userHash !== "undefined";
|
|
const returned = {
|
|
loggedIn: store.loggedIn
|
|
};
|
|
rt.proxy.on("create", (function(info) {
|
|
store.realtime = info.realtime;
|
|
store.network = info.network;
|
|
if (!store.loggedIn) {
|
|
returned.anonHash = commonHashExports.getEditHashFromKeys(secret);
|
|
}
|
|
})).on("cacheready", (function(info) {
|
|
store.realtime = info.realtime;
|
|
store.offline = true;
|
|
store.networkPromise = info.networkPromise;
|
|
store.cacheReturned = returned;
|
|
if (store.networkPromise && store.networkPromise.then) {
|
|
const to = setTimeout((function() {
|
|
store.networkTimeout = true;
|
|
broadcast([], "LOADING_DRIVE", {
|
|
type: "offline"
|
|
});
|
|
}), 5e3);
|
|
store.networkPromise.then((function(network) {
|
|
store.network || (store.network = network);
|
|
clearTimeout(to);
|
|
}), (function(err) {
|
|
console.error(err);
|
|
clearTimeout(to);
|
|
}));
|
|
}
|
|
if (!config.cache) {
|
|
return;
|
|
}
|
|
returned.edPublic = rt.proxy.edPublic;
|
|
onCacheReadyEvt$1.fire(returned);
|
|
})).on("ready", (function(info) {
|
|
delete store.networkTimeout;
|
|
if (store.ready) {
|
|
return;
|
|
}
|
|
store.driveMetadata = info.metadata;
|
|
if (!rt.proxy.drive) {
|
|
rt.proxy.drive = {};
|
|
}
|
|
if (!rt.proxy[commonConstantsExports.displayNameKey] && store.noDriveName) {
|
|
rt.proxy[commonConstantsExports.displayNameKey] = store.noDriveName;
|
|
}
|
|
if (!rt.proxy.uid && store.noDriveUid) {
|
|
rt.proxy.uid = store.noDriveUid;
|
|
}
|
|
if (!rt.proxy.form_seed && config.form_seed) {
|
|
rt.proxy.form_seed = config.form_seed;
|
|
}
|
|
if (rt.proxy.edPublic && Array.isArray(ApiConfig.adminKeys) && ApiConfig.adminKeys.indexOf(rt.proxy.edPublic) !== -1) {
|
|
store.isAdmin = true;
|
|
}
|
|
returned.edPublic = rt.proxy.edPublic;
|
|
onReadyEvt$1.fire(returned);
|
|
})).on("error", (function(info) {
|
|
if (info.error !== "EDELETED") {
|
|
return;
|
|
}
|
|
if (store.ownDeletion) {
|
|
return;
|
|
}
|
|
store.isDeleted = true;
|
|
broadcast([], "DRIVE_DELETED", info.message);
|
|
})).on("disconnect", (function() {
|
|
store.offline = true;
|
|
onDisconnectEvt$1.fire();
|
|
broadcast([], "UPDATE_METADATA");
|
|
})).on("reconnect", (function() {
|
|
store.offline = false;
|
|
onReconnectEvt$1.fire();
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
return {
|
|
channel: secret.channel,
|
|
onAccountCacheReady: onCacheReadyEvt$1.reg,
|
|
onAccountReady: onReadyEvt$1.reg,
|
|
onDisconnect: onDisconnectEvt$1.reg,
|
|
onReconnect: onReconnectEvt$1.reg
|
|
};
|
|
};
|
|
const Account = {
|
|
setCustomize: data => {
|
|
ApiConfig = data.ApiConfig;
|
|
},
|
|
init: init$1
|
|
};
|
|
var account = Object.freeze({
|
|
__proto__: null,
|
|
Account
|
|
});
|
|
var commonFeedback$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCommonFeedback;
|
|
function requireCommonFeedback() {
|
|
if (hasRequiredCommonFeedback) return commonFeedback$1.exports;
|
|
hasRequiredCommonFeedback = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (AppConfig = {}, Messages = {}) => {
|
|
var Feedback = {};
|
|
Feedback.setCustomize = data => {
|
|
Messages = data.Messages;
|
|
AppConfig = data.AppConfig;
|
|
};
|
|
Feedback.init = function(state) {
|
|
Feedback.state = state;
|
|
};
|
|
var randomToken = function() {
|
|
return Math.random().toString(16).replace(/0./, "");
|
|
};
|
|
var ajax = function(url, cb) {
|
|
var http = new XMLHttpRequest;
|
|
http.open("HEAD", url);
|
|
http.onreadystatechange = function() {
|
|
if (this.readyState === this.DONE) {
|
|
if (cb) {
|
|
cb();
|
|
}
|
|
}
|
|
};
|
|
http.send();
|
|
};
|
|
Feedback.send = function(action, force, cb) {
|
|
if (typeof cb !== "function") {
|
|
cb = function() {};
|
|
}
|
|
if (AppConfig.disableFeedback) {
|
|
return void cb();
|
|
}
|
|
if (!action) {
|
|
return void cb();
|
|
}
|
|
if (force !== true) {
|
|
if (!Feedback.state) {
|
|
return void cb();
|
|
}
|
|
}
|
|
var href = "/common/feedback.html?" + action + "=" + randomToken();
|
|
ajax(href, cb);
|
|
};
|
|
Feedback.reportAppUsage = function() {
|
|
var pattern = window.location.pathname.split("/").filter((function(x) {
|
|
return x;
|
|
})).join(".");
|
|
if (/^#\/1\/view\//.test(window.location.hash)) {
|
|
Feedback.send(pattern + "_VIEW");
|
|
} else {
|
|
Feedback.send(pattern);
|
|
}
|
|
};
|
|
Feedback.reportScreenDimensions = function() {
|
|
var h = window.innerHeight;
|
|
var w = window.innerWidth;
|
|
Feedback.send("DIMENSIONS:" + h + "x" + w);
|
|
};
|
|
Feedback.reportLanguage = function() {
|
|
if (!Messages) {
|
|
return;
|
|
}
|
|
Feedback.send("LANG_" + Messages._languageUsed);
|
|
};
|
|
return Feedback;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, undefined);
|
|
}
|
|
})();
|
|
})(commonFeedback$1);
|
|
return commonFeedback$1.exports;
|
|
}
|
|
var commonFeedbackExports = requireCommonFeedback();
|
|
var commonFeedback = getDefaultExportFromCjs(commonFeedbackExports);
|
|
var Feedback = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: commonFeedback
|
|
}, [ commonFeedbackExports ]);
|
|
var commonCredential$1 = {
|
|
exports: {}
|
|
};
|
|
var scryptAsync = {
|
|
exports: {}
|
|
};
|
|
/*!
|
|
* Fast "async" scrypt implementation in JavaScript.
|
|
* Copyright (c) 2013-2015 Dmitry Chestnykh | BSD License
|
|
* https://github.com/dchest/scrypt-async-js
|
|
*/ var hasRequiredScryptAsync;
|
|
function requireScryptAsync() {
|
|
if (hasRequiredScryptAsync) return scryptAsync.exports;
|
|
hasRequiredScryptAsync = 1;
|
|
(function(module) {
|
|
function scrypt(password, salt, logN, r, dkLen, interruptStep, callback, encoding) {
|
|
function SHA256(m) {
|
|
var K = [ 1116352408, 1899447441, 3049323471, 3921009573, 961987163, 1508970993, 2453635748, 2870763221, 3624381080, 310598401, 607225278, 1426881987, 1925078388, 2162078206, 2614888103, 3248222580, 3835390401, 4022224774, 264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986, 2554220882, 2821834349, 2952996808, 3210313671, 3336571891, 3584528711, 113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291, 1695183700, 1986661051, 2177026350, 2456956037, 2730485921, 2820302411, 3259730800, 3345764771, 3516065817, 3600352804, 4094571909, 275423344, 430227734, 506948616, 659060556, 883997877, 958139571, 1322822218, 1537002063, 1747873779, 1955562222, 2024104815, 2227730452, 2361852424, 2428436474, 2756734187, 3204031479, 3329325298 ];
|
|
var h0 = 1779033703, h1 = 3144134277, h2 = 1013904242, h3 = 2773480762, h4 = 1359893119, h5 = 2600822924, h6 = 528734635, h7 = 1541459225, w = new Array(64);
|
|
function blocks(p) {
|
|
var off = 0, len = p.length;
|
|
while (len >= 64) {
|
|
var a = h0, b = h1, c = h2, d = h3, e = h4, f = h5, g = h6, h = h7, u, i, j, t1, t2;
|
|
for (i = 0; i < 16; i++) {
|
|
j = off + i * 4;
|
|
w[i] = (p[j] & 255) << 24 | (p[j + 1] & 255) << 16 | (p[j + 2] & 255) << 8 | p[j + 3] & 255;
|
|
}
|
|
for (i = 16; i < 64; i++) {
|
|
u = w[i - 2];
|
|
t1 = (u >>> 17 | u << 32 - 17) ^ (u >>> 19 | u << 32 - 19) ^ u >>> 10;
|
|
u = w[i - 15];
|
|
t2 = (u >>> 7 | u << 32 - 7) ^ (u >>> 18 | u << 32 - 18) ^ u >>> 3;
|
|
w[i] = (t1 + w[i - 7] | 0) + (t2 + w[i - 16] | 0) | 0;
|
|
}
|
|
for (i = 0; i < 64; i++) {
|
|
t1 = (((e >>> 6 | e << 32 - 6) ^ (e >>> 11 | e << 32 - 11) ^ (e >>> 25 | e << 32 - 25)) + (e & f ^ ~e & g) | 0) + (h + (K[i] + w[i] | 0) | 0) | 0;
|
|
t2 = ((a >>> 2 | a << 32 - 2) ^ (a >>> 13 | a << 32 - 13) ^ (a >>> 22 | a << 32 - 22)) + (a & b ^ a & c ^ b & c) | 0;
|
|
h = g;
|
|
g = f;
|
|
f = e;
|
|
e = d + t1 | 0;
|
|
d = c;
|
|
c = b;
|
|
b = a;
|
|
a = t1 + t2 | 0;
|
|
}
|
|
h0 = h0 + a | 0;
|
|
h1 = h1 + b | 0;
|
|
h2 = h2 + c | 0;
|
|
h3 = h3 + d | 0;
|
|
h4 = h4 + e | 0;
|
|
h5 = h5 + f | 0;
|
|
h6 = h6 + g | 0;
|
|
h7 = h7 + h | 0;
|
|
off += 64;
|
|
len -= 64;
|
|
}
|
|
}
|
|
blocks(m);
|
|
var i, bytesLeft = m.length % 64, bitLenHi = m.length / 536870912 | 0, bitLenLo = m.length << 3, numZeros = bytesLeft < 56 ? 56 : 120, p = m.slice(m.length - bytesLeft, m.length);
|
|
p.push(128);
|
|
for (i = bytesLeft + 1; i < numZeros; i++) p.push(0);
|
|
p.push(bitLenHi >>> 24 & 255);
|
|
p.push(bitLenHi >>> 16 & 255);
|
|
p.push(bitLenHi >>> 8 & 255);
|
|
p.push(bitLenHi >>> 0 & 255);
|
|
p.push(bitLenLo >>> 24 & 255);
|
|
p.push(bitLenLo >>> 16 & 255);
|
|
p.push(bitLenLo >>> 8 & 255);
|
|
p.push(bitLenLo >>> 0 & 255);
|
|
blocks(p);
|
|
return [ h0 >>> 24 & 255, h0 >>> 16 & 255, h0 >>> 8 & 255, h0 >>> 0 & 255, h1 >>> 24 & 255, h1 >>> 16 & 255, h1 >>> 8 & 255, h1 >>> 0 & 255, h2 >>> 24 & 255, h2 >>> 16 & 255, h2 >>> 8 & 255, h2 >>> 0 & 255, h3 >>> 24 & 255, h3 >>> 16 & 255, h3 >>> 8 & 255, h3 >>> 0 & 255, h4 >>> 24 & 255, h4 >>> 16 & 255, h4 >>> 8 & 255, h4 >>> 0 & 255, h5 >>> 24 & 255, h5 >>> 16 & 255, h5 >>> 8 & 255, h5 >>> 0 & 255, h6 >>> 24 & 255, h6 >>> 16 & 255, h6 >>> 8 & 255, h6 >>> 0 & 255, h7 >>> 24 & 255, h7 >>> 16 & 255, h7 >>> 8 & 255, h7 >>> 0 & 255 ];
|
|
}
|
|
function PBKDF2_HMAC_SHA256_OneIter(password, salt, dkLen) {
|
|
password = password.length <= 64 ? password : SHA256(password);
|
|
var i, innerLen = 64 + salt.length + 4, inner = new Array(innerLen), outerKey = new Array(64), dk = [];
|
|
for (i = 0; i < 64; i++) inner[i] = 54;
|
|
for (i = 0; i < password.length; i++) inner[i] ^= password[i];
|
|
for (i = 0; i < salt.length; i++) inner[64 + i] = salt[i];
|
|
for (i = innerLen - 4; i < innerLen; i++) inner[i] = 0;
|
|
for (i = 0; i < 64; i++) outerKey[i] = 92;
|
|
for (i = 0; i < password.length; i++) outerKey[i] ^= password[i];
|
|
function incrementCounter() {
|
|
for (var i = innerLen - 1; i >= innerLen - 4; i--) {
|
|
inner[i]++;
|
|
if (inner[i] <= 255) return;
|
|
inner[i] = 0;
|
|
}
|
|
}
|
|
while (dkLen >= 32) {
|
|
incrementCounter();
|
|
dk = dk.concat(SHA256(outerKey.concat(SHA256(inner))));
|
|
dkLen -= 32;
|
|
}
|
|
if (dkLen > 0) {
|
|
incrementCounter();
|
|
dk = dk.concat(SHA256(outerKey.concat(SHA256(inner))).slice(0, dkLen));
|
|
}
|
|
return dk;
|
|
}
|
|
function salsaXOR(tmp, B, bin, bout) {
|
|
var j0 = tmp[0] ^ B[bin++], j1 = tmp[1] ^ B[bin++], j2 = tmp[2] ^ B[bin++], j3 = tmp[3] ^ B[bin++], j4 = tmp[4] ^ B[bin++], j5 = tmp[5] ^ B[bin++], j6 = tmp[6] ^ B[bin++], j7 = tmp[7] ^ B[bin++], j8 = tmp[8] ^ B[bin++], j9 = tmp[9] ^ B[bin++], j10 = tmp[10] ^ B[bin++], j11 = tmp[11] ^ B[bin++], j12 = tmp[12] ^ B[bin++], j13 = tmp[13] ^ B[bin++], j14 = tmp[14] ^ B[bin++], j15 = tmp[15] ^ B[bin++], u, i;
|
|
var x0 = j0, x1 = j1, x2 = j2, x3 = j3, x4 = j4, x5 = j5, x6 = j6, x7 = j7, x8 = j8, x9 = j9, x10 = j10, x11 = j11, x12 = j12, x13 = j13, x14 = j14, x15 = j15;
|
|
for (i = 0; i < 8; i += 2) {
|
|
u = x0 + x12;
|
|
x4 ^= u << 7 | u >>> 32 - 7;
|
|
u = x4 + x0;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x4;
|
|
x12 ^= u << 13 | u >>> 32 - 13;
|
|
u = x12 + x8;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x1;
|
|
x9 ^= u << 7 | u >>> 32 - 7;
|
|
u = x9 + x5;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x9;
|
|
x1 ^= u << 13 | u >>> 32 - 13;
|
|
u = x1 + x13;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x6;
|
|
x14 ^= u << 7 | u >>> 32 - 7;
|
|
u = x14 + x10;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x14;
|
|
x6 ^= u << 13 | u >>> 32 - 13;
|
|
u = x6 + x2;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x11;
|
|
x3 ^= u << 7 | u >>> 32 - 7;
|
|
u = x3 + x15;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x3;
|
|
x11 ^= u << 13 | u >>> 32 - 13;
|
|
u = x11 + x7;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
u = x0 + x3;
|
|
x1 ^= u << 7 | u >>> 32 - 7;
|
|
u = x1 + x0;
|
|
x2 ^= u << 9 | u >>> 32 - 9;
|
|
u = x2 + x1;
|
|
x3 ^= u << 13 | u >>> 32 - 13;
|
|
u = x3 + x2;
|
|
x0 ^= u << 18 | u >>> 32 - 18;
|
|
u = x5 + x4;
|
|
x6 ^= u << 7 | u >>> 32 - 7;
|
|
u = x6 + x5;
|
|
x7 ^= u << 9 | u >>> 32 - 9;
|
|
u = x7 + x6;
|
|
x4 ^= u << 13 | u >>> 32 - 13;
|
|
u = x4 + x7;
|
|
x5 ^= u << 18 | u >>> 32 - 18;
|
|
u = x10 + x9;
|
|
x11 ^= u << 7 | u >>> 32 - 7;
|
|
u = x11 + x10;
|
|
x8 ^= u << 9 | u >>> 32 - 9;
|
|
u = x8 + x11;
|
|
x9 ^= u << 13 | u >>> 32 - 13;
|
|
u = x9 + x8;
|
|
x10 ^= u << 18 | u >>> 32 - 18;
|
|
u = x15 + x14;
|
|
x12 ^= u << 7 | u >>> 32 - 7;
|
|
u = x12 + x15;
|
|
x13 ^= u << 9 | u >>> 32 - 9;
|
|
u = x13 + x12;
|
|
x14 ^= u << 13 | u >>> 32 - 13;
|
|
u = x14 + x13;
|
|
x15 ^= u << 18 | u >>> 32 - 18;
|
|
}
|
|
B[bout++] = tmp[0] = x0 + j0 | 0;
|
|
B[bout++] = tmp[1] = x1 + j1 | 0;
|
|
B[bout++] = tmp[2] = x2 + j2 | 0;
|
|
B[bout++] = tmp[3] = x3 + j3 | 0;
|
|
B[bout++] = tmp[4] = x4 + j4 | 0;
|
|
B[bout++] = tmp[5] = x5 + j5 | 0;
|
|
B[bout++] = tmp[6] = x6 + j6 | 0;
|
|
B[bout++] = tmp[7] = x7 + j7 | 0;
|
|
B[bout++] = tmp[8] = x8 + j8 | 0;
|
|
B[bout++] = tmp[9] = x9 + j9 | 0;
|
|
B[bout++] = tmp[10] = x10 + j10 | 0;
|
|
B[bout++] = tmp[11] = x11 + j11 | 0;
|
|
B[bout++] = tmp[12] = x12 + j12 | 0;
|
|
B[bout++] = tmp[13] = x13 + j13 | 0;
|
|
B[bout++] = tmp[14] = x14 + j14 | 0;
|
|
B[bout++] = tmp[15] = x15 + j15 | 0;
|
|
}
|
|
function blockCopy(dst, di, src, si, len) {
|
|
while (len--) dst[di++] = src[si++];
|
|
}
|
|
function blockXOR(dst, di, src, si, len) {
|
|
while (len--) dst[di++] ^= src[si++];
|
|
}
|
|
function blockMix(tmp, B, bin, bout, r) {
|
|
blockCopy(tmp, 0, B, bin + (2 * r - 1) * 16, 16);
|
|
for (var i = 0; i < 2 * r; i += 2) {
|
|
salsaXOR(tmp, B, bin + i * 16, bout + i * 8);
|
|
salsaXOR(tmp, B, bin + i * 16 + 16, bout + i * 8 + r * 16);
|
|
}
|
|
}
|
|
function integerify(B, bi, r) {
|
|
return B[bi + (2 * r - 1) * 16];
|
|
}
|
|
function stringToUTF8Bytes(s) {
|
|
var arr = [];
|
|
for (var i = 0; i < s.length; i++) {
|
|
var c = s.charCodeAt(i);
|
|
if (c < 128) {
|
|
arr.push(c);
|
|
} else if (c > 127 && c < 2048) {
|
|
arr.push(c >> 6 | 192);
|
|
arr.push(c & 63 | 128);
|
|
} else {
|
|
arr.push(c >> 12 | 224);
|
|
arr.push(c >> 6 & 63 | 128);
|
|
arr.push(c & 63 | 128);
|
|
}
|
|
}
|
|
return arr;
|
|
}
|
|
function bytesToHex(p) {
|
|
var enc = "0123456789abcdef".split("");
|
|
var len = p.length, arr = [], i = 0;
|
|
for (;i < len; i++) {
|
|
arr.push(enc[p[i] >>> 4 & 15]);
|
|
arr.push(enc[p[i] >>> 0 & 15]);
|
|
}
|
|
return arr.join("");
|
|
}
|
|
function bytesToBase64(p) {
|
|
var enc = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + "0123456789+/").split("");
|
|
var len = p.length, arr = [], i = 0, a, b, c, t;
|
|
while (i < len) {
|
|
a = i < len ? p[i++] : 0;
|
|
b = i < len ? p[i++] : 0;
|
|
c = i < len ? p[i++] : 0;
|
|
t = (a << 16) + (b << 8) + c;
|
|
arr.push(enc[t >>> 3 * 6 & 63]);
|
|
arr.push(enc[t >>> 2 * 6 & 63]);
|
|
arr.push(enc[t >>> 1 * 6 & 63]);
|
|
arr.push(enc[t >>> 0 * 6 & 63]);
|
|
}
|
|
if (len % 3 > 0) {
|
|
arr[arr.length - 1] = "=";
|
|
if (len % 3 === 1) arr[arr.length - 2] = "=";
|
|
}
|
|
return arr.join("");
|
|
}
|
|
var p = 1;
|
|
if (logN < 1 || logN > 31) throw new Error("scrypt: logN not be between 1 and 31");
|
|
var MAX_INT = 1 << 31 >>> 0, N = 1 << logN >>> 0, XY, V, B, tmp;
|
|
if (r * p >= 1 << 30 || r > MAX_INT / 128 / p || r > MAX_INT / 256 || N > MAX_INT / 128 / r) throw new Error("scrypt: parameters are too large");
|
|
if (typeof password === "string") password = stringToUTF8Bytes(password);
|
|
if (typeof salt === "string") salt = stringToUTF8Bytes(salt);
|
|
if (typeof Int32Array !== "undefined") {
|
|
XY = new Int32Array(64 * r);
|
|
V = new Int32Array(32 * N * r);
|
|
tmp = new Int32Array(16);
|
|
} else {
|
|
XY = [];
|
|
V = [];
|
|
tmp = new Array(16);
|
|
}
|
|
B = PBKDF2_HMAC_SHA256_OneIter(password, salt, p * 128 * r);
|
|
var xi = 0, yi = 32 * r;
|
|
function smixStart() {
|
|
for (var i = 0; i < 32 * r; i++) {
|
|
var j = i * 4;
|
|
XY[xi + i] = (B[j + 3] & 255) << 24 | (B[j + 2] & 255) << 16 | (B[j + 1] & 255) << 8 | (B[j + 0] & 255) << 0;
|
|
}
|
|
}
|
|
function smixStep1(start, end) {
|
|
for (var i = start; i < end; i += 2) {
|
|
blockCopy(V, i * (32 * r), XY, xi, 32 * r);
|
|
blockMix(tmp, XY, xi, yi, r);
|
|
blockCopy(V, (i + 1) * (32 * r), XY, yi, 32 * r);
|
|
blockMix(tmp, XY, yi, xi, r);
|
|
}
|
|
}
|
|
function smixStep2(start, end) {
|
|
for (var i = start; i < end; i += 2) {
|
|
var j = integerify(XY, xi, r) & N - 1;
|
|
blockXOR(XY, xi, V, j * (32 * r), 32 * r);
|
|
blockMix(tmp, XY, xi, yi, r);
|
|
j = integerify(XY, yi, r) & N - 1;
|
|
blockXOR(XY, yi, V, j * (32 * r), 32 * r);
|
|
blockMix(tmp, XY, yi, xi, r);
|
|
}
|
|
}
|
|
function smixFinish() {
|
|
for (var i = 0; i < 32 * r; i++) {
|
|
var j = XY[xi + i];
|
|
B[i * 4 + 0] = j >>> 0 & 255;
|
|
B[i * 4 + 1] = j >>> 8 & 255;
|
|
B[i * 4 + 2] = j >>> 16 & 255;
|
|
B[i * 4 + 3] = j >>> 24 & 255;
|
|
}
|
|
}
|
|
var nextTick = typeof setImmediate !== "undefined" ? setImmediate : setTimeout;
|
|
function interruptedFor(start, end, step, fn, donefn) {
|
|
(function performStep() {
|
|
nextTick((function() {
|
|
fn(start, start + step < end ? start + step : end);
|
|
start += step;
|
|
if (start < end) performStep(); else donefn();
|
|
}));
|
|
})();
|
|
}
|
|
function getResult(enc) {
|
|
var result = PBKDF2_HMAC_SHA256_OneIter(password, B, dkLen);
|
|
if (enc === "base64") return bytesToBase64(result); else if (enc === "hex") return bytesToHex(result); else return result;
|
|
}
|
|
if (typeof interruptStep === "function") {
|
|
encoding = callback;
|
|
callback = interruptStep;
|
|
interruptStep = 1e3;
|
|
}
|
|
if (interruptStep <= 0) {
|
|
smixStart();
|
|
smixStep1(0, N);
|
|
smixStep2(0, N);
|
|
smixFinish();
|
|
callback(getResult(encoding));
|
|
} else {
|
|
smixStart();
|
|
interruptedFor(0, N, interruptStep * 2, smixStep1, (function() {
|
|
interruptedFor(0, N, interruptStep * 2, smixStep2, (function() {
|
|
smixFinish();
|
|
callback(getResult(encoding));
|
|
}));
|
|
}));
|
|
}
|
|
}
|
|
module.exports = scrypt;
|
|
})(scryptAsync);
|
|
return scryptAsync.exports;
|
|
}
|
|
var hasRequiredCommonCredential;
|
|
function requireCommonCredential() {
|
|
if (hasRequiredCommonCredential) return commonCredential$1.exports;
|
|
hasRequiredCommonCredential = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(AppConfig = {}, Scrypt) {
|
|
var Cred = {};
|
|
Cred.setCustomize = data => {
|
|
AppConfig = data.AppConfig;
|
|
};
|
|
Cred.MINIMUM_PASSWORD_LENGTH = typeof AppConfig.minimumPasswordLength === "number" ? AppConfig.minimumPasswordLength : 8;
|
|
Cred.MINIMUM_NAME_LENGTH = 1;
|
|
Cred.MAXIMUM_NAME_LENGTH = 64;
|
|
Cred.isEmail = function(email) {
|
|
var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
return re.test(String(email).toLowerCase());
|
|
};
|
|
Cred.isLongEnoughPassword = function(passwd) {
|
|
return passwd.length >= Cred.MINIMUM_PASSWORD_LENGTH;
|
|
};
|
|
var isString = Cred.isString = function(x) {
|
|
return typeof x === "string";
|
|
};
|
|
Cred.isValidUsername = function(name) {
|
|
return !!(isString(name) && name.length >= Cred.MINIMUM_NAME_LENGTH);
|
|
};
|
|
Cred.isValidPassword = function(passwd) {
|
|
return !!(passwd && isString(passwd));
|
|
};
|
|
Cred.passwordsMatch = function(a, b) {
|
|
return isString(a) && isString(b) && a === b;
|
|
};
|
|
Cred.customSalt = function() {
|
|
return typeof AppConfig.loginSalt === "string" ? AppConfig.loginSalt : "";
|
|
};
|
|
Cred.deriveFromPassphrase = function(username, password, len, cb) {
|
|
Scrypt(password, username + Cred.customSalt(), 8, 1024, len || 128, 200, cb, undefined);
|
|
};
|
|
Cred.dispenser = function(bytes) {
|
|
var entropy = {
|
|
used: 0
|
|
};
|
|
var consume = function(n) {
|
|
if (entropy.used + n > bytes.length) {
|
|
throw new Error("exceeded available entropy");
|
|
}
|
|
if (typeof n !== "number") {
|
|
throw new Error("expected a number");
|
|
}
|
|
if (n <= 0) {
|
|
throw new Error("expected to consume a positive number of bytes");
|
|
}
|
|
var A;
|
|
if (bytes.slice) {
|
|
A = bytes.slice(entropy.used, entropy.used + n);
|
|
} else {
|
|
A = bytes.subarray(entropy.used, entropy.used + n);
|
|
}
|
|
entropy.used += n;
|
|
return A;
|
|
};
|
|
return consume;
|
|
};
|
|
return Cred;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, requireScryptAsync());
|
|
}
|
|
})();
|
|
})(commonCredential$1);
|
|
return commonCredential$1.exports;
|
|
}
|
|
var commonCredentialExports = requireCommonCredential();
|
|
var commonCredential = getDefaultExportFromCjs(commonCredentialExports);
|
|
var Credential = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: commonCredential
|
|
}, [ commonCredentialExports ]);
|
|
var proxyManager$1 = {
|
|
exports: {}
|
|
};
|
|
var userObject$1 = {
|
|
exports: {}
|
|
};
|
|
var userObjectSetter = {
|
|
exports: {}
|
|
};
|
|
var commonRealtime = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCommonRealtime;
|
|
function requireCommonRealtime() {
|
|
if (hasRequiredCommonRealtime) return commonRealtime.exports;
|
|
hasRequiredCommonRealtime = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = () => {
|
|
var common = {};
|
|
common.whenRealtimeSyncs = function(realtime, cb) {
|
|
if (typeof realtime.getAuthDoc !== "function") {
|
|
return void console.error("improper use of this function");
|
|
}
|
|
setTimeout((function() {
|
|
if (realtime.getAuthDoc() === realtime.getUserDoc()) {
|
|
return void cb();
|
|
} else {
|
|
realtime.onSettle(cb);
|
|
}
|
|
}), 0);
|
|
};
|
|
return common;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
}
|
|
})();
|
|
})(commonRealtime);
|
|
return commonRealtime.exports;
|
|
}
|
|
var hasRequiredUserObjectSetter;
|
|
function requireUserObjectSetter() {
|
|
if (hasRequiredUserObjectSetter) return userObjectSetter.exports;
|
|
hasRequiredUserObjectSetter = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, Realtime) => {
|
|
let window = globalThis;
|
|
var module = {};
|
|
module.setCustomize = () => {};
|
|
var clone = function(o) {
|
|
try {
|
|
return JSON.parse(JSON.stringify(o));
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
};
|
|
module.init = function(config, exp, files) {
|
|
var loggedIn = config.loggedIn;
|
|
var sharedFolder = config.sharedFolder;
|
|
var readOnly = config.readOnly;
|
|
var Messages = config.Messages || {};
|
|
var ROOT = exp.ROOT;
|
|
var FILES_DATA = exp.FILES_DATA;
|
|
var STATIC_DATA = exp.STATIC_DATA;
|
|
var OLD_FILES_DATA = exp.OLD_FILES_DATA;
|
|
var UNSORTED = exp.UNSORTED;
|
|
var TRASH = exp.TRASH;
|
|
var TEMPLATE = exp.TEMPLATE;
|
|
var SHARED_FOLDERS = exp.SHARED_FOLDERS;
|
|
var SHARED_FOLDERS_TEMP = exp.SHARED_FOLDERS_TEMP;
|
|
var debug = exp.debug;
|
|
exp._setReadOnly = function(state) {
|
|
readOnly = state;
|
|
if (!readOnly) {
|
|
exp.fixFiles();
|
|
}
|
|
};
|
|
exp.setHref = function(channel, id, href) {
|
|
if (!id && !channel) {
|
|
return;
|
|
}
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var ids = id ? [ id ] : exp.findChannels([ channel ]);
|
|
ids.forEach((function(i) {
|
|
var data = exp.getFileData(i, true);
|
|
var oldHref = exp.getHref(data);
|
|
if (oldHref === href) {
|
|
return;
|
|
}
|
|
data.href = exp.cryptor.encrypt(href);
|
|
}));
|
|
};
|
|
exp.setPadAttribute = function(href, attr, value, cb) {
|
|
cb = cb || function() {};
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var id = exp.getIdFromHref(href);
|
|
if (!id) {
|
|
return void cb("E_INVAL_HREF");
|
|
}
|
|
if (!attr || !attr.trim()) {
|
|
return void cb("E_INVAL_ATTR");
|
|
}
|
|
var data = exp.getFileData(id, true);
|
|
if (attr === "href") {
|
|
exp.setHref(null, id, value);
|
|
} else {
|
|
data[attr] = clone(value);
|
|
}
|
|
cb(null);
|
|
};
|
|
exp.getPadAttribute = function(href, attr, cb) {
|
|
cb = cb || function() {};
|
|
var id = exp.getIdFromHref(href);
|
|
if (!id) {
|
|
return void cb(null, undefined);
|
|
}
|
|
var data = exp.getFileData(id);
|
|
cb(null, clone(data[attr]));
|
|
};
|
|
exp.pushData = function(_data, cb) {
|
|
if (typeof cb !== "function") {
|
|
cb = function() {};
|
|
}
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var id = Util.createRandomInteger();
|
|
var data = clone(_data);
|
|
if (data.href && data.href.indexOf("#") !== -1) {
|
|
data.href = exp.cryptor.encrypt(data.href);
|
|
}
|
|
files[FILES_DATA][id] = data;
|
|
cb(null, id);
|
|
};
|
|
exp.pushLink = function(_data, cb) {
|
|
if (typeof cb !== "function") {
|
|
cb = function() {};
|
|
}
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var id = Util.createRandomInteger();
|
|
var data = clone(_data);
|
|
files[STATIC_DATA][id] = data;
|
|
cb(null, id);
|
|
};
|
|
exp.pushSharedFolder = function(_data, cb) {
|
|
if (typeof cb !== "function") {
|
|
cb = function() {};
|
|
}
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var data = clone(_data);
|
|
var exists;
|
|
if (Object.keys(files[SHARED_FOLDERS]).some((function(k) {
|
|
if (files[SHARED_FOLDERS][k].channel === data.channel) {
|
|
if (data.href && !files[SHARED_FOLDERS][k].href) {
|
|
files[SHARED_FOLDERS][k].href = data.href;
|
|
}
|
|
exists = k;
|
|
return true;
|
|
}
|
|
}))) {
|
|
return void cb("EEXISTS", exists);
|
|
}
|
|
if (!loggedIn || config.testMode) {
|
|
return void cb("EAUTH");
|
|
}
|
|
var id = Util.createRandomInteger();
|
|
if (data.href && data.href.indexOf("#") !== -1) {
|
|
data.href = exp.cryptor.encrypt(data.href);
|
|
}
|
|
files[SHARED_FOLDERS][id] = data;
|
|
cb(null, id);
|
|
};
|
|
exp.deprecateSharedFolder = function(id, reason) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var data = files[SHARED_FOLDERS][id];
|
|
if (!data) {
|
|
return;
|
|
}
|
|
var ro = !data.href || exp.cryptor.decrypt(data.href).indexOf("#") === -1;
|
|
if (!ro) {
|
|
var obj = files[SHARED_FOLDERS_TEMP][id] = JSON.parse(JSON.stringify(data));
|
|
obj.legacy = reason !== "PASSWORD_CHANGE";
|
|
}
|
|
var paths = exp.findFile(Number(id));
|
|
exp.delete(paths, null, true);
|
|
delete files[SHARED_FOLDERS][id];
|
|
};
|
|
var spliceFileData = function(id) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
delete files[FILES_DATA][id];
|
|
};
|
|
exp.checkDeletedFiles = function(cb) {
|
|
if (!loggedIn && !config.testMode) {
|
|
return void cb();
|
|
}
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var filesList = exp.getFiles([ ROOT, "hrefArray", TRASH ]);
|
|
var toClean = [];
|
|
exp.getFiles([ FILES_DATA, SHARED_FOLDERS, STATIC_DATA ]).forEach((function(id) {
|
|
if (filesList.indexOf(id) === -1) {
|
|
var fd = exp.isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
|
|
var channelId = fd.channel;
|
|
if (fd.lastVersion) {
|
|
toClean.push(Hash.hrefToHexChannelId(fd.lastVersion));
|
|
}
|
|
if (fd.rtChannel) {
|
|
toClean.push(fd.rtChannel);
|
|
}
|
|
if (channelId) {
|
|
toClean.push(channelId);
|
|
}
|
|
if (exp.isSharedFolder(id)) {
|
|
delete files[SHARED_FOLDERS][id];
|
|
if (config.removeProxy) {
|
|
config.removeProxy(id);
|
|
}
|
|
} else if (files[STATIC_DATA][id]) {
|
|
delete files[STATIC_DATA][id];
|
|
} else {
|
|
spliceFileData(id);
|
|
}
|
|
}
|
|
}));
|
|
if (!toClean.length) {
|
|
return void cb();
|
|
}
|
|
cb(null, toClean);
|
|
};
|
|
var deleteHrefs = function(ids) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
ids.forEach((function(obj) {
|
|
var idx = files[obj.root].indexOf(obj.id);
|
|
files[obj.root].splice(idx, 1);
|
|
}));
|
|
};
|
|
var deleteMultipleTrashRoot = function(roots) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
roots.forEach((function(obj) {
|
|
var idx = files[TRASH][obj.name].indexOf(obj.el);
|
|
files[TRASH][obj.name].splice(idx, 1);
|
|
}));
|
|
};
|
|
exp.deleteMultiplePermanently = function(paths, nocheck, cb) {
|
|
if (readOnly) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
var allFilesPaths = paths.filter((function(x) {
|
|
return exp.isPathIn(x, [ FILES_DATA ]);
|
|
}));
|
|
if (!loggedIn && !config.testMode) {
|
|
allFilesPaths.forEach((function(path) {
|
|
var id = path[1];
|
|
if (!id) {
|
|
return;
|
|
}
|
|
spliceFileData(id);
|
|
}));
|
|
return void cb();
|
|
}
|
|
var hrefPaths = paths.filter((function(x) {
|
|
return exp.isPathIn(x, [ "hrefArray" ]);
|
|
}));
|
|
var rootPaths = paths.filter((function(x) {
|
|
return exp.isPathIn(x, [ ROOT ]);
|
|
}));
|
|
var trashPaths = paths.filter((function(x) {
|
|
return exp.isPathIn(x, [ TRASH ]);
|
|
}));
|
|
var ids = [];
|
|
hrefPaths.forEach((function(path) {
|
|
var id = exp.find(path);
|
|
ids.push({
|
|
root: path[0],
|
|
id
|
|
});
|
|
}));
|
|
deleteHrefs(ids);
|
|
rootPaths.forEach((function(path) {
|
|
var parentPath = path.slice();
|
|
var key = parentPath.pop();
|
|
var parentEl = exp.find(parentPath);
|
|
delete parentEl[key];
|
|
}));
|
|
var trashRoot = [];
|
|
trashPaths.forEach((function(path) {
|
|
var parentPath = path.slice();
|
|
var key = parentPath.pop();
|
|
var parentEl = exp.find(parentPath);
|
|
if (path.length === 4) {
|
|
trashRoot.push({
|
|
name: path[1],
|
|
el: parentEl
|
|
});
|
|
return;
|
|
}
|
|
delete parentEl[key];
|
|
}));
|
|
deleteMultipleTrashRoot(trashRoot);
|
|
if (!nocheck) {
|
|
exp.checkDeletedFiles(cb);
|
|
} else {
|
|
cb();
|
|
}
|
|
};
|
|
exp.copyFromOtherDrive = function(path, element, data, key) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var toRemove = [];
|
|
Object.keys(data).forEach((function(id) {
|
|
id = Number(id);
|
|
var d = data[id];
|
|
if (d.static) {
|
|
delete d.static;
|
|
files[STATIC_DATA][id] = d;
|
|
return;
|
|
}
|
|
if (d.href) {
|
|
d.href = exp.cryptor.encrypt(d.href);
|
|
}
|
|
var found = false;
|
|
for (var i in files[FILES_DATA]) {
|
|
if (files[FILES_DATA][i].channel === d.channel) {
|
|
if (!files[FILES_DATA][i].href) {
|
|
files[FILES_DATA][i].href = d.href;
|
|
}
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
toRemove.push(id);
|
|
return;
|
|
}
|
|
files[FILES_DATA][id] = d;
|
|
}));
|
|
if (exp.isFile(element) && toRemove.indexOf(element) !== -1) {
|
|
exp.log(Messages.sharedFolders_duplicate);
|
|
return;
|
|
} else if (exp.isFolder(element)) {
|
|
var _removeExisting = function(root) {
|
|
for (var k in root) {
|
|
if (exp.isFile(root[k])) {
|
|
if (toRemove.indexOf(root[k]) !== -1) {
|
|
exp.log(Messages.sharedFolders_duplicate);
|
|
delete root[k];
|
|
}
|
|
} else if (exp.isFolder(root[k])) {
|
|
_removeExisting(root[k]);
|
|
}
|
|
}
|
|
};
|
|
_removeExisting(element);
|
|
}
|
|
var newParent = exp.find(path);
|
|
var tempName = exp.isFile(element) ? Hash.createChannelId() : key;
|
|
var newName = exp.getAvailableName(newParent, tempName);
|
|
if (Array.isArray(newParent)) {
|
|
newParent.push(element);
|
|
return;
|
|
}
|
|
newParent[newName] = element;
|
|
};
|
|
var pushToTrash = function(name, element, path) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var trash = files[TRASH];
|
|
if (typeof trash[name] === "undefined") {
|
|
trash[name] = [];
|
|
}
|
|
var trashArray = trash[name];
|
|
var trashElement = {
|
|
element,
|
|
path
|
|
};
|
|
trashArray.push(trashElement);
|
|
};
|
|
exp.copyElement = function(elementPath, newParentPath) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
if (exp.comparePath(elementPath, newParentPath)) {
|
|
return;
|
|
}
|
|
var element = exp.find(elementPath);
|
|
var newParent = exp.find(newParentPath);
|
|
if (exp.isPathIn(newParentPath, [ TRASH ])) {
|
|
if (!elementPath || elementPath.length < 2 || elementPath[0] === TRASH) {
|
|
debug("Can't move an element from the trash to the trash: ", elementPath);
|
|
return;
|
|
}
|
|
var key = elementPath[elementPath.length - 1];
|
|
var elName = exp.isPathIn(elementPath, [ "hrefArray" ]) ? exp.getTitle(element) : key;
|
|
var parentPath = elementPath.slice();
|
|
parentPath.pop();
|
|
pushToTrash(elName, element, parentPath);
|
|
return true;
|
|
}
|
|
if (exp.isPathIn(newParentPath, [ "hrefArray" ])) {
|
|
if (exp.isFolder(element)) {
|
|
exp.log(Messages.fo_moveUnsortedError);
|
|
return;
|
|
} else {
|
|
if (elementPath[0] === newParentPath[0]) {
|
|
return;
|
|
}
|
|
var fileRoot = newParentPath[0];
|
|
if (files[fileRoot].indexOf(element) === -1) {
|
|
files[fileRoot].push(element);
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
var newName = exp.isFile(element) ? exp.getAvailableName(newParent, Hash.createChannelId()) : exp.isInTrashRoot(elementPath) ? elementPath[1] : elementPath.pop();
|
|
if (typeof newParent[newName] !== "undefined") {
|
|
exp.log(Messages.fo_unavailableName);
|
|
return;
|
|
}
|
|
newParent[newName] = element;
|
|
return true;
|
|
};
|
|
exp.forget = function(href) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var id = exp.getIdFromHref(href);
|
|
if (!id) {
|
|
return;
|
|
}
|
|
if (!loggedIn && !config.testMode) {
|
|
spliceFileData(id);
|
|
return true;
|
|
}
|
|
var paths = exp.findFile(id);
|
|
exp.move(paths, [ TRASH ]);
|
|
return true;
|
|
};
|
|
exp.restoreHref = function(href) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var idO = exp.getIdFromHref(href);
|
|
if (!idO || !exp.isFile(idO)) {
|
|
return;
|
|
}
|
|
var paths = exp.findFile(idO);
|
|
var allInTrash = true;
|
|
paths.forEach((function(p) {
|
|
if (p[0] === TRASH) {
|
|
exp.delete(p, null, true);
|
|
return;
|
|
}
|
|
allInTrash = false;
|
|
}));
|
|
if (allInTrash) {
|
|
exp.add(idO);
|
|
}
|
|
};
|
|
exp.add = function(id, path) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
if (!loggedIn && !config.testMode) {
|
|
return;
|
|
}
|
|
id = Number(id);
|
|
var data = files[FILES_DATA][id] || files[STATIC_DATA][id] || files[SHARED_FOLDERS][id];
|
|
if (!data || typeof data !== "object") {
|
|
return;
|
|
}
|
|
var newPath = path, parentEl;
|
|
if (path && !Array.isArray(path)) {
|
|
newPath = decodeURIComponent(path).split(",");
|
|
}
|
|
if (path && exp.isPathIn(newPath, [ "hrefArray" ])) {
|
|
parentEl = exp.find(newPath);
|
|
parentEl.push(id);
|
|
return;
|
|
}
|
|
var filesList = exp.getFiles([ ROOT, TRASH, "hrefArray" ]);
|
|
if (filesList.indexOf(id) === -1 && !newPath) {
|
|
newPath = [ ROOT ];
|
|
}
|
|
if (path && exp.isPathIn(newPath, [ ROOT ])) {
|
|
parentEl = exp.find(newPath);
|
|
if (parentEl) {
|
|
var newName = exp.getAvailableName(parentEl, Hash.createChannelId());
|
|
parentEl[newName] = id;
|
|
return;
|
|
} else {
|
|
parentEl = exp.find([ ROOT ]);
|
|
newPath.slice(1).forEach((function(folderName) {
|
|
parentEl = parentEl[folderName] = parentEl[folderName] || {};
|
|
}));
|
|
parentEl[Hash.createChannelId()] = id;
|
|
}
|
|
}
|
|
};
|
|
exp.setFolderData = function(path, key, value, cb) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
var folder = exp.find(path);
|
|
if (!exp.isFolder(folder) || exp.isSharedFolder(folder)) {
|
|
return;
|
|
}
|
|
if (!exp.hasFolderData(folder)) {
|
|
var hashKey = "000" + Hash.createChannelId().slice(0, -3);
|
|
folder[hashKey] = {
|
|
metadata: true
|
|
};
|
|
}
|
|
exp.getFolderData(folder)[key] = value;
|
|
cb();
|
|
};
|
|
var onSync = function(next) {
|
|
if (exp.rt) {
|
|
exp.rt.sync();
|
|
Realtime.whenRealtimeSyncs(exp.rt, next);
|
|
} else {
|
|
window.setTimeout(next, 1e3);
|
|
}
|
|
};
|
|
exp.migrateReadOnly = function(cb) {
|
|
if (readOnly || !config.editKey) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (files.version >= 2) {
|
|
return void cb();
|
|
}
|
|
files.migrateRo = 1;
|
|
var next = function() {
|
|
var copy = JSON.parse(JSON.stringify(files));
|
|
exp.reencrypt(config.editKey, config.editKey, copy);
|
|
setTimeout((function() {
|
|
if (files.version >= 2) {
|
|
return void cb();
|
|
}
|
|
Object.keys(copy).forEach((function(k) {
|
|
files[k] = copy[k];
|
|
}));
|
|
files.version = 2;
|
|
delete files.migrateRo;
|
|
onSync(cb);
|
|
}), 1e3);
|
|
};
|
|
onSync(next);
|
|
};
|
|
exp.migrate = function(cb) {
|
|
if (readOnly) {
|
|
return void cb();
|
|
}
|
|
var fixUnsorted = function() {
|
|
if (!files[UNSORTED] || !files[OLD_FILES_DATA]) {
|
|
return;
|
|
}
|
|
debug("UNSORTED still exists in the object, removing it...");
|
|
var us = files[UNSORTED];
|
|
if (us.length === 0) {
|
|
delete files[UNSORTED];
|
|
return;
|
|
}
|
|
us.forEach((function(el) {
|
|
if (typeof el !== "string") {
|
|
return;
|
|
}
|
|
var data = files[OLD_FILES_DATA].filter((function(x) {
|
|
return x.href === el;
|
|
}));
|
|
if (data.length === 0) {
|
|
files[OLD_FILES_DATA].push({
|
|
href: el
|
|
});
|
|
}
|
|
return;
|
|
}));
|
|
delete files[UNSORTED];
|
|
};
|
|
var migrateToNewFormat = function(todo) {
|
|
if (!files[OLD_FILES_DATA]) {
|
|
return void todo();
|
|
}
|
|
try {
|
|
debug("Migrating file system...");
|
|
files.migrate = 1;
|
|
var next = function() {
|
|
var oldData = files[OLD_FILES_DATA].slice();
|
|
if (!files[FILES_DATA]) {
|
|
files[FILES_DATA] = {};
|
|
}
|
|
var newData = files[FILES_DATA];
|
|
oldData.forEach((function(obj) {
|
|
if (!obj || !obj.href) {
|
|
return;
|
|
}
|
|
var href = obj.href;
|
|
var id = Util.createRandomInteger();
|
|
var paths = exp.findFile(href);
|
|
var data = obj;
|
|
var key = Hash.createChannelId();
|
|
if (data) {
|
|
newData[id] = data;
|
|
} else {
|
|
newData[id] = {
|
|
href
|
|
};
|
|
}
|
|
paths.forEach((function(p) {
|
|
var parentPath = p.slice();
|
|
var okey = parentPath.pop();
|
|
var parent = exp.find(parentPath);
|
|
if (exp.isInTrashRoot(p)) {
|
|
parent.element = id;
|
|
newData[id].filename = p[1];
|
|
return;
|
|
}
|
|
if (exp.isPathIn(p, [ "hrefArray" ])) {
|
|
parent[okey] = id;
|
|
return;
|
|
}
|
|
parent[key] = id;
|
|
newData[id].filename = okey;
|
|
delete parent[okey];
|
|
}));
|
|
}));
|
|
delete files[OLD_FILES_DATA];
|
|
delete files.migrate;
|
|
todo();
|
|
};
|
|
onSync(next);
|
|
} catch (e) {
|
|
console.error(e);
|
|
todo();
|
|
}
|
|
};
|
|
fixUnsorted();
|
|
migrateToNewFormat(cb);
|
|
};
|
|
exp.fixFiles = function(silent) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
if (silent) {
|
|
debug = function() {};
|
|
}
|
|
var t0 = +new Date;
|
|
debug("Cleaning file system...");
|
|
var before = JSON.stringify(files);
|
|
var fixRoot = function(elem) {
|
|
if (typeof files[ROOT] !== "object") {
|
|
debug("ROOT was not an object");
|
|
files[ROOT] = {};
|
|
}
|
|
var element = elem || files[ROOT];
|
|
if (!element) {
|
|
return console.error("Invalid element in root");
|
|
}
|
|
var nbMetadataFolders = 0;
|
|
var static_data = files[STATIC_DATA];
|
|
var files_data = files[FILES_DATA];
|
|
var element_el;
|
|
for (var el in element) {
|
|
element_el = element[el];
|
|
if (element_el === null) {
|
|
console.error("element[%s] is null", el);
|
|
delete element[el];
|
|
continue;
|
|
}
|
|
if (exp.isFolderData(element_el)) {
|
|
if (nbMetadataFolders !== 0) {
|
|
debug("Multiple metadata files in folder");
|
|
delete element[el];
|
|
}
|
|
nbMetadataFolders++;
|
|
continue;
|
|
}
|
|
if (!exp.isFile(element_el, true) && !exp.isFolder(element_el)) {
|
|
debug("An element in ROOT was not a folder nor a file. ", element_el);
|
|
delete element[el];
|
|
continue;
|
|
}
|
|
if (exp.isFolder(element_el)) {
|
|
fixRoot(element_el);
|
|
continue;
|
|
}
|
|
if (typeof element_el === "string") {
|
|
var id = Util.createRandomInteger();
|
|
var key = Hash.createChannelId();
|
|
files_data[id] = {
|
|
href: exp.cryptor.encrypt(element_el),
|
|
filename: el
|
|
};
|
|
element[key] = id;
|
|
delete element[el];
|
|
}
|
|
if (typeof element_el === "number") {
|
|
var data = files_data[element_el] || static_data[element_el];
|
|
if (!data) {
|
|
debug("An element in ROOT doesn't have associated data", element_el, el);
|
|
delete element[el];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var fixTrashRoot = function() {
|
|
if (sharedFolder) {
|
|
return;
|
|
}
|
|
if (typeof files[TRASH] !== "object") {
|
|
debug("TRASH was not an object");
|
|
files[TRASH] = {};
|
|
}
|
|
var tr = files[TRASH];
|
|
var toClean;
|
|
var addToClean = function(obj, idx, el) {
|
|
if (typeof obj !== "object") {
|
|
toClean.push(idx);
|
|
return;
|
|
}
|
|
if (exp.isSharedFolder(obj.element)) {
|
|
return;
|
|
}
|
|
if (!exp.isFile(obj.element, true) && !exp.isFolder(obj.element)) {
|
|
toClean.push(idx);
|
|
return;
|
|
}
|
|
if (!Array.isArray(obj.path)) {
|
|
toClean.push(idx);
|
|
return;
|
|
}
|
|
if (typeof obj.element === "string") {
|
|
var id = Util.createRandomInteger();
|
|
files[FILES_DATA][id] = {
|
|
href: exp.cryptor.encrypt(obj.element),
|
|
filename: el
|
|
};
|
|
obj.element = id;
|
|
}
|
|
if (exp.isFolder(obj.element)) {
|
|
fixRoot(obj.element);
|
|
}
|
|
if (typeof obj.element === "number") {
|
|
var data = files[FILES_DATA][obj.element] || files[STATIC_DATA][obj.element];
|
|
if (!data) {
|
|
debug("An element in TRASH doesn't have associated data", obj.element, el);
|
|
toClean.push(idx);
|
|
}
|
|
}
|
|
};
|
|
for (var el in tr) {
|
|
if (!Array.isArray(tr[el])) {
|
|
debug("An element in TRASH root is not an array. ", tr[el]);
|
|
delete tr[el];
|
|
} else if (tr[el].length === 0) {
|
|
debug("Empty array in TRASH root. ", tr[el]);
|
|
delete tr[el];
|
|
} else {
|
|
toClean = [];
|
|
for (var j = 0; j < tr[el].length; j++) {
|
|
addToClean(tr[el][j], j, el);
|
|
}
|
|
for (var i = toClean.length - 1; i >= 0; i--) {
|
|
tr[el].splice(toClean[i], 1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
var fixTemplate = function() {
|
|
if (sharedFolder) {
|
|
return;
|
|
}
|
|
if (!Array.isArray(files[TEMPLATE])) {
|
|
debug("TEMPLATE was not an array");
|
|
files[TEMPLATE] = [];
|
|
}
|
|
var dedup = Util.deduplicateString(files[TEMPLATE]);
|
|
if (dedup.length !== files[TEMPLATE].length) {
|
|
files[TEMPLATE] = dedup;
|
|
}
|
|
var us = files[TEMPLATE];
|
|
var rootFiles = exp.getFiles([ ROOT ]);
|
|
var toClean = [];
|
|
us.forEach((function(el, idx) {
|
|
if (!exp.isFile(el, true) || rootFiles.indexOf(el) !== -1) {
|
|
toClean.push(el);
|
|
return;
|
|
}
|
|
if (typeof el === "string") {
|
|
var id = Util.createRandomInteger();
|
|
files[FILES_DATA][id] = {
|
|
href: exp.cryptor.encrypt(el)
|
|
};
|
|
us[idx] = id;
|
|
return;
|
|
}
|
|
if (typeof el === "number") {
|
|
var data = files[FILES_DATA][el];
|
|
if (!data) {
|
|
debug("An element in TEMPLATE doesn't have associated data", el);
|
|
toClean.push(el);
|
|
}
|
|
}
|
|
}));
|
|
toClean.forEach((function(el) {
|
|
var idx = us.indexOf(el);
|
|
if (idx !== -1) {
|
|
us.splice(idx, 1);
|
|
}
|
|
}));
|
|
};
|
|
var fixFilesData = function() {
|
|
if (typeof files[FILES_DATA] !== "object") {
|
|
debug("FILES_DATA was not an object");
|
|
files[FILES_DATA] = {};
|
|
}
|
|
var fd = files[FILES_DATA];
|
|
var rootFiles = exp.getFiles([ ROOT, TRASH, "hrefArray" ]);
|
|
var root = exp.find([ ROOT ]);
|
|
var toClean = [];
|
|
for (var id in fd) {
|
|
if (String(id) !== String(Number(id))) {
|
|
debug("Invalid file ID in filesData.", id);
|
|
toClean.push(id);
|
|
continue;
|
|
}
|
|
id = Number(id);
|
|
var el = fd[id];
|
|
if (!el || typeof el !== "object") {
|
|
debug("An element in filesData was not an object.", el);
|
|
toClean.push(id);
|
|
continue;
|
|
}
|
|
if (!el.href && !el.roHref) {
|
|
debug("Removing an element in filesData with a missing href.", el);
|
|
toClean.push(id);
|
|
continue;
|
|
}
|
|
var decryptedHref;
|
|
try {
|
|
decryptedHref = el.href && (el.href.indexOf("#") !== -1 ? el.href : exp.cryptor.decrypt(el.href));
|
|
} catch (e) {}
|
|
if (decryptedHref && decryptedHref.indexOf("#") === -1) {
|
|
continue;
|
|
}
|
|
var parsed = Hash.parsePadUrl(decryptedHref || el.roHref);
|
|
var secret;
|
|
if (!parsed.hash) {
|
|
debug("Removing an element in filesData with a invalid href.", el);
|
|
toClean.push(id);
|
|
continue;
|
|
}
|
|
if (!parsed.type) {
|
|
debug("Removing an element in filesData with a invalid type.", el);
|
|
toClean.push(id);
|
|
continue;
|
|
}
|
|
if (decryptedHref && parsed.hashData.type === "pad" && parsed.hashData.version) {
|
|
if (parsed.hashData.mode === "view") {
|
|
el.roHref = decryptedHref;
|
|
delete el.href;
|
|
} else if (!el.roHref) {
|
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
|
el.roHref = "/" + parsed.type + "/#" + Hash.getViewHashFromKeys(secret);
|
|
} else {
|
|
var parsed2 = Hash.parsePadUrl(el.roHref);
|
|
if (!parsed2.hash || !parsed2.type) {
|
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
|
el.roHref = "/" + parsed.type + "/#" + Hash.getViewHashFromKeys(secret);
|
|
}
|
|
}
|
|
}
|
|
if (parsed.hashData.version === 0) {
|
|
delete el.roHref;
|
|
}
|
|
if (decryptedHref && decryptedHref.slice(0, 1) !== "/") {
|
|
el.href = exp.cryptor.encrypt(Hash.getRelativeHref(decryptedHref));
|
|
}
|
|
if (!el.ctime) {
|
|
el.ctime = el.atime;
|
|
}
|
|
if (!el.title) {
|
|
el.title = exp.getDefaultName(parsed);
|
|
}
|
|
if (!el.channel) {
|
|
try {
|
|
if (!secret) {
|
|
secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
|
}
|
|
el.channel = secret.channel;
|
|
console.log(el);
|
|
debug("Adding missing channel in filesData ", el.channel);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
if (!Hash.isValidChannel(el.channel)) {
|
|
console.error("Remove invalid channel", el.channel, el);
|
|
}
|
|
if ((loggedIn || config.testMode) && rootFiles.indexOf(id) === -1) {
|
|
debug("An element in filesData was not in ROOT, TEMPLATE or TRASH.", id, el);
|
|
var newName = Hash.createChannelId();
|
|
root[newName] = id;
|
|
continue;
|
|
}
|
|
}
|
|
toClean.forEach((function(id) {
|
|
spliceFileData(id);
|
|
}));
|
|
var sd = files[STATIC_DATA];
|
|
var toCleanSD = [];
|
|
for (var id2 in sd) {
|
|
id2 = Number(id2);
|
|
var el2 = sd[id2];
|
|
if (!el2 || typeof el2 !== "object" || !el2.href) {
|
|
toCleanSD.push(id2);
|
|
continue;
|
|
}
|
|
if ((loggedIn || config.testMode) && rootFiles.indexOf(id2) === -1) {
|
|
toCleanSD.push(id2);
|
|
continue;
|
|
}
|
|
}
|
|
var spliceSD = function(id) {
|
|
if (readOnly) {
|
|
return;
|
|
}
|
|
delete files[STATIC_DATA][id];
|
|
};
|
|
toCleanSD.forEach(spliceSD);
|
|
};
|
|
var fixSharedFolders = function() {
|
|
if (sharedFolder) {
|
|
return;
|
|
}
|
|
if (typeof files[SHARED_FOLDERS] !== "object") {
|
|
debug("SHARED_FOLDER was not an object");
|
|
files[SHARED_FOLDERS] = {};
|
|
}
|
|
var sf = files[SHARED_FOLDERS];
|
|
var rootFiles = exp.getFiles([ ROOT, TRASH ]);
|
|
var root = exp.find([ ROOT ]);
|
|
var parsed, el;
|
|
for (var id in sf) {
|
|
el = sf[id];
|
|
id = Number(id);
|
|
var href;
|
|
try {
|
|
href = el.href && (el.href.indexOf("#") !== -1 ? el.href : exp.cryptor.decrypt(el.href));
|
|
} catch (e) {}
|
|
parsed = Hash.parsePadUrl(href || el.roHref);
|
|
if (!parsed || !parsed.hash || parsed.hash === "undefined") {
|
|
delete sf[id];
|
|
continue;
|
|
}
|
|
if (rootFiles.indexOf(id) === -1) {
|
|
console.log("missing" + id);
|
|
var newName = Hash.createChannelId();
|
|
root[newName] = id;
|
|
}
|
|
}
|
|
};
|
|
var fixSharedFoldersTemp = function() {
|
|
if (sharedFolder) {
|
|
return;
|
|
}
|
|
if (typeof files[SHARED_FOLDERS_TEMP] !== "object") {
|
|
debug("SHARED_FOLDER_TEMP was not an object");
|
|
files[SHARED_FOLDERS_TEMP] = {};
|
|
}
|
|
var sft = files[SHARED_FOLDERS_TEMP];
|
|
var sf = files[SHARED_FOLDERS];
|
|
for (var id in sft) {
|
|
if (sf[id]) {
|
|
delete sft[id];
|
|
}
|
|
}
|
|
};
|
|
var fixStaticData = function() {
|
|
if (!Util.isObject(files[STATIC_DATA])) {
|
|
debug("STATIC_DATA was not an object");
|
|
files[STATIC_DATA] = {};
|
|
}
|
|
};
|
|
var fixDrive = function() {
|
|
Object.keys(files).forEach((function(key) {
|
|
if (key.slice(0, 1) === "/") {
|
|
delete files[key];
|
|
}
|
|
}));
|
|
};
|
|
fixStaticData();
|
|
fixRoot();
|
|
fixTrashRoot();
|
|
fixTemplate();
|
|
fixFilesData();
|
|
fixDrive();
|
|
fixSharedFolders();
|
|
fixSharedFoldersTemp();
|
|
var ms = +new Date - t0 + "ms";
|
|
if (JSON.stringify(files) !== before) {
|
|
debug("Your file system was corrupted. It has been cleaned so that the pads you visit can be stored safely.", ms);
|
|
return;
|
|
}
|
|
debug("File system was clean.", ms);
|
|
};
|
|
return exp;
|
|
};
|
|
return module;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireCommonRealtime());
|
|
}
|
|
})();
|
|
})(userObjectSetter);
|
|
return userObjectSetter.exports;
|
|
}
|
|
var hasRequiredUserObject;
|
|
function requireUserObject() {
|
|
if (hasRequiredUserObject) return userObject$1.exports;
|
|
hasRequiredUserObject = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, Constants, UOSetter, Crypto, Messages = {}) => {
|
|
let window = globalThis;
|
|
var module = {};
|
|
module.setCustomize = data => {
|
|
Messages = data.Messages;
|
|
UOSetter.setCustomize(data);
|
|
};
|
|
var ROOT = module.ROOT = "root";
|
|
var UNSORTED = module.UNSORTED = "unsorted";
|
|
var TRASH = module.TRASH = "trash";
|
|
var TEMPLATE = module.TEMPLATE = "template";
|
|
var SHARED_FOLDERS = module.SHARED_FOLDERS = "sharedFolders";
|
|
var SHARED_FOLDERS_TEMP = module.SHARED_FOLDERS_TEMP = "sharedFoldersTemp";
|
|
var FILES_DATA = module.FILES_DATA = Constants.storageKey;
|
|
var OLD_FILES_DATA = module.OLD_FILES_DATA = Constants.oldStorageKey;
|
|
var STATIC_DATA = module.STATIC_DATA = "static";
|
|
var getLocaleDate = function() {
|
|
if (window.Intl && window.Intl.DateTimeFormat) {
|
|
var options = {
|
|
weekday: "short",
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric"
|
|
};
|
|
return new window.Intl.DateTimeFormat(undefined, options).format(new Date);
|
|
}
|
|
return (new Date).toString().split(" ").slice(0, 4).join(" ");
|
|
};
|
|
module.getDefaultName = function(parsed) {
|
|
var type = parsed.type;
|
|
var name = Messages.type[type] + " - " + getLocaleDate();
|
|
return name;
|
|
};
|
|
var createCryptor = module.createCryptor = function(key) {
|
|
var cryptor = {};
|
|
if (!key) {
|
|
cryptor.encrypt = function(x) {
|
|
return x;
|
|
};
|
|
cryptor.decrypt = function(x) {
|
|
return x;
|
|
};
|
|
return cryptor;
|
|
}
|
|
try {
|
|
var c = Crypto.createEncryptor(key);
|
|
cryptor.encrypt = function(href) {
|
|
try {
|
|
if (href.slice(0, 7) === "/file/#") {
|
|
return href;
|
|
}
|
|
return c.encrypt(href);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
};
|
|
cryptor.decrypt = function(msg) {
|
|
try {
|
|
return c.decrypt(msg);
|
|
} catch (e) {
|
|
return;
|
|
}
|
|
};
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
return cryptor;
|
|
};
|
|
module.getHref = function(pad, cryptor) {
|
|
if (pad.href && pad.href.indexOf("#") !== -1) {
|
|
return pad.href;
|
|
}
|
|
if (pad.href && cryptor) {
|
|
var d = cryptor.decrypt(pad.href);
|
|
if (d && d.indexOf("#") !== -1) {
|
|
return d;
|
|
}
|
|
}
|
|
return pad.roHref;
|
|
};
|
|
module.reencrypt = function(oldKey, newKey, obj) {
|
|
if (!obj) {
|
|
return void console.error("Nothing to reencrypt");
|
|
}
|
|
var oldCryptor = createCryptor(oldKey);
|
|
var newCryptor = createCryptor(newKey);
|
|
Object.keys(obj[FILES_DATA]).forEach((function(id) {
|
|
var data = obj[FILES_DATA][id] || {};
|
|
if (data.href && data.roHref && !data.fileType) {
|
|
var _href = data.href && data.href.indexOf("#") === -1 ? oldCryptor.decrypt(data.href) : data.href;
|
|
if (!_href) {
|
|
return;
|
|
}
|
|
data.href = newCryptor.encrypt(_href);
|
|
}
|
|
}));
|
|
Object.keys(obj[SHARED_FOLDERS] || {}).forEach((function(id) {
|
|
var data = obj[SHARED_FOLDERS][id] || {};
|
|
if (data.href) {
|
|
var _href = data.href && data.href.indexOf("#") === -1 ? oldCryptor.decrypt(data.href) : data.href;
|
|
if (!_href) {
|
|
return;
|
|
}
|
|
data.href = newCryptor.encrypt(_href);
|
|
}
|
|
}));
|
|
Object.keys(obj[SHARED_FOLDERS_TEMP] || {}).forEach((function(id) {
|
|
var data = obj[SHARED_FOLDERS_TEMP][id] || {};
|
|
if (data.href) {
|
|
var _href = data.href && data.href.indexOf("#") === -1 ? oldCryptor.decrypt(data.href) : data.href;
|
|
if (!_href) {
|
|
return;
|
|
}
|
|
data.href = newCryptor.encrypt(_href);
|
|
}
|
|
}));
|
|
};
|
|
module.init = function(files, config) {
|
|
var exp = {};
|
|
exp.cryptor = createCryptor(config.editKey);
|
|
exp.setReadOnly = function(state, key) {
|
|
config.editKey = key;
|
|
exp.cryptor = createCryptor(key);
|
|
exp.cryptor.k = Math.random();
|
|
exp.readOnly = state;
|
|
if (exp._setReadOnly) {
|
|
exp._setReadOnly(state);
|
|
}
|
|
};
|
|
exp.readOnly = config.readOnly;
|
|
exp.reencrypt = module.reencrypt;
|
|
exp.getDefaultName = module.getDefaultName;
|
|
var sframeChan = config.sframeChan;
|
|
var NEW_FOLDER_NAME = Messages.fm_newFolder || "New folder";
|
|
var NEW_FILE_NAME = Messages.fm_newFile || "New file";
|
|
exp.ROOT = ROOT;
|
|
exp.STATIC_DATA = STATIC_DATA;
|
|
exp.UNSORTED = UNSORTED;
|
|
exp.TRASH = TRASH;
|
|
exp.TEMPLATE = TEMPLATE;
|
|
exp.SHARED_FOLDERS = SHARED_FOLDERS;
|
|
exp.SHARED_FOLDERS_TEMP = SHARED_FOLDERS_TEMP;
|
|
exp.FILES_DATA = FILES_DATA;
|
|
exp.OLD_FILES_DATA = OLD_FILES_DATA;
|
|
var sharedFolder = exp.sharedFolder = config.sharedFolder;
|
|
exp.id = config.id;
|
|
var logging = function() {
|
|
console.debug.apply(console, arguments);
|
|
};
|
|
var log = exp.log = config.log || logging;
|
|
var logError = config.logError || logging;
|
|
var debug = exp.debug = config.debug || logging;
|
|
exp.fixFiles = function() {};
|
|
var error = exp.error = function() {
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "fixFiles",
|
|
data: {}
|
|
}, (function() {}));
|
|
} else if (typeof exp.fixFiles === "function") {
|
|
exp.fixFiles();
|
|
}
|
|
console.error.apply(console, arguments);
|
|
exp.fixFiles();
|
|
};
|
|
if (config.outer) {
|
|
UOSetter.init(config, exp, files);
|
|
}
|
|
exp.getStructure = function() {
|
|
var a = {};
|
|
a[ROOT] = {};
|
|
a[TRASH] = {};
|
|
a[FILES_DATA] = {};
|
|
a[TEMPLATE] = [];
|
|
a[SHARED_FOLDERS] = {};
|
|
return a;
|
|
};
|
|
var getHref = exp.getHref = function(pad) {
|
|
return module.getHref(pad, exp.cryptor);
|
|
};
|
|
var type = function(dat) {
|
|
return dat === null ? "null" : Array.isArray(dat) ? "array" : typeof dat;
|
|
};
|
|
exp.isValidDrive = function(obj) {
|
|
var base = exp.getStructure();
|
|
return typeof obj === "object" && Object.keys(base).every((function(key) {
|
|
return obj[key] && type(base[key]) === type(obj[key]);
|
|
}));
|
|
};
|
|
var getHrefArray = function() {
|
|
return [ TEMPLATE ];
|
|
};
|
|
var compareFiles = function(fileA, fileB) {
|
|
return fileA === fileB;
|
|
};
|
|
var isSharedFolder = exp.isSharedFolder = function(element) {
|
|
if (sharedFolder) {
|
|
return false;
|
|
}
|
|
return Boolean(files[SHARED_FOLDERS] && files[SHARED_FOLDERS][element]);
|
|
};
|
|
var isFile = exp.isFile = function(element, allowStr) {
|
|
if (isSharedFolder(element)) {
|
|
return false;
|
|
}
|
|
return typeof element === "number" || (typeof files[OLD_FILES_DATA] !== "undefined" || allowStr) && typeof element === "string";
|
|
};
|
|
var isFolderData = exp.isFolderData = function(element) {
|
|
return typeof element === "object" && element.metadata === true;
|
|
};
|
|
exp.isReadOnlyFile = function(element) {
|
|
if (!isFile(element)) {
|
|
return false;
|
|
}
|
|
var data = exp.getFileData(element);
|
|
if (!data.roHref) {
|
|
return;
|
|
}
|
|
return Boolean(data.roHref && !data.href);
|
|
};
|
|
exp.isStaticFile = function(element) {
|
|
return Boolean(files[STATIC_DATA] && files[STATIC_DATA][element]);
|
|
};
|
|
var isFolder = exp.isFolder = function(element) {
|
|
if (isFolderData(element)) {
|
|
return false;
|
|
}
|
|
return typeof element === "object" && !element.channel || isSharedFolder(element);
|
|
};
|
|
exp.isFolderEmpty = function(element) {
|
|
if (!isFolder(element)) {
|
|
return false;
|
|
}
|
|
if (Object.keys(element).length === 0) {
|
|
return true;
|
|
}
|
|
if (Object.keys(element).length === 1 && isFolderData(element[Object.keys(element)[0]])) {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
exp.hasSubfolder = function(element, trashRoot) {
|
|
if (!isFolder(element)) {
|
|
return false;
|
|
}
|
|
var subfolder = 0;
|
|
var addSubfolder = function(el) {
|
|
subfolder += isFolder(el.element) ? 1 : 0;
|
|
};
|
|
for (var f in element) {
|
|
if (trashRoot) {
|
|
if (Array.isArray(element[f])) {
|
|
element[f].forEach(addSubfolder);
|
|
}
|
|
} else {
|
|
subfolder += isFolder(element[f]) ? 1 : 0;
|
|
}
|
|
}
|
|
return subfolder;
|
|
};
|
|
exp.hasFile = function(element, trashRoot) {
|
|
if (!isFolder(element)) {
|
|
return false;
|
|
}
|
|
var file = 0;
|
|
var addFile = function(el) {
|
|
file += isFile(el.element) ? 1 : 0;
|
|
};
|
|
for (var f in element) {
|
|
if (trashRoot) {
|
|
if (Array.isArray(element[f])) {
|
|
element[f].forEach(addFile);
|
|
}
|
|
} else {
|
|
file += isFile(element[f]) ? 1 : 0;
|
|
}
|
|
}
|
|
return file;
|
|
};
|
|
exp.hasFolderData = function(folder) {
|
|
for (var el in folder) {
|
|
if (isFolderData(folder[el])) {
|
|
return true;
|
|
}
|
|
}
|
|
};
|
|
var hasSubSharedFolder = exp.hasSubSharedFolder = function(folder) {
|
|
for (var el in folder) {
|
|
if (isSharedFolder(folder[el])) {
|
|
return true;
|
|
} else if (isFolder(folder[el])) {
|
|
if (hasSubSharedFolder(folder[el])) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
var getFileData = exp.getFileData = function(file, editable) {
|
|
if (!file) {
|
|
return;
|
|
}
|
|
var link;
|
|
try {
|
|
link = (files[STATIC_DATA] || {})[file];
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
if (link) {
|
|
var _link = editable ? link : Util.clone(link);
|
|
if (!editable) {
|
|
_link.static = true;
|
|
}
|
|
return _link;
|
|
}
|
|
var data;
|
|
try {
|
|
data = files[FILES_DATA][file] || {};
|
|
} catch (err) {
|
|
console.error(err);
|
|
data = {};
|
|
}
|
|
if (!editable) {
|
|
data = JSON.parse(JSON.stringify(data));
|
|
if (data.href && data.href.indexOf("#") === -1) {
|
|
if (config.editKey) {
|
|
try {
|
|
data.href = exp.cryptor.decrypt(data.href);
|
|
} catch (e) {
|
|
delete data.href;
|
|
}
|
|
} else {
|
|
delete data.href;
|
|
}
|
|
}
|
|
}
|
|
return data;
|
|
};
|
|
exp.getFolderData = function(folder) {
|
|
for (var el in folder) {
|
|
if (isFolderData(folder[el])) {
|
|
return folder[el];
|
|
}
|
|
}
|
|
return {};
|
|
};
|
|
var getTitle = exp.getTitle = function(file, type) {
|
|
if (isSharedFolder(file)) {
|
|
return "??";
|
|
}
|
|
var data = getFileData(file);
|
|
if (!data) {
|
|
error("unable to retrieve data about the requested file: ", file, data);
|
|
return;
|
|
}
|
|
if (data.static) {
|
|
return data.name;
|
|
}
|
|
if (!file || !(data.href || data.roHref)) {
|
|
error("getTitle called with a non-existing file id: ", file, data);
|
|
return;
|
|
}
|
|
if (type === "title") {
|
|
return data.title;
|
|
}
|
|
if (type === "name") {
|
|
return data.filename;
|
|
}
|
|
return data.filename || data.title || NEW_FILE_NAME;
|
|
};
|
|
var comparePath = exp.comparePath = function(a, b) {
|
|
if (!a || !b || !Array.isArray(a) || !Array.isArray(b)) {
|
|
return false;
|
|
}
|
|
if (a.length !== b.length) {
|
|
return false;
|
|
}
|
|
var result = true;
|
|
var i = a.length - 1;
|
|
while (result && i >= 0) {
|
|
result = a[i] === b[i];
|
|
i--;
|
|
}
|
|
return result;
|
|
};
|
|
var isSubpath = exp.isSubpath = function(path, parentPath) {
|
|
var pathA = parentPath.slice();
|
|
var pathB = path.slice(0, pathA.length);
|
|
return comparePath(pathA, pathB);
|
|
};
|
|
var isPathIn = exp.isPathIn = function(path, categories) {
|
|
if (!categories) {
|
|
return;
|
|
}
|
|
var idx = categories.indexOf("hrefArray");
|
|
if (idx !== -1) {
|
|
categories.splice(idx, 1);
|
|
categories = categories.concat(getHrefArray());
|
|
}
|
|
return categories.some((function(c) {
|
|
return Array.isArray(path) && path[0] === c;
|
|
}));
|
|
};
|
|
var isInTrashRoot = exp.isInTrashRoot = function(path) {
|
|
return path[0] === TRASH && path.length === 4;
|
|
};
|
|
var findElement = function(root, pathInput) {
|
|
if (!pathInput) {
|
|
error("Invalid path:\n", pathInput, "\nin root\n", root);
|
|
return;
|
|
}
|
|
if (pathInput.length === 0) {
|
|
return root;
|
|
}
|
|
var path = pathInput.slice();
|
|
var key = path.shift();
|
|
if (typeof root[key] === "undefined") {
|
|
debug("Unable to find the key '" + key + "' in the root object provided:", root);
|
|
return;
|
|
}
|
|
return findElement(root[key], path);
|
|
};
|
|
var find = exp.find = function(path) {
|
|
return findElement(files, path);
|
|
};
|
|
var getFilesRecursively = exp.getFilesRecursively = function(root, arr) {
|
|
arr = arr || [];
|
|
for (var e in root) {
|
|
if (isFile(root[e]) || isSharedFolder(root[e])) {
|
|
if (arr.indexOf(root[e]) === -1) {
|
|
arr.push(root[e]);
|
|
}
|
|
} else if (!isFolderData(root[e])) {
|
|
getFilesRecursively(root[e], arr);
|
|
}
|
|
}
|
|
return arr;
|
|
};
|
|
var _getFiles = {};
|
|
_getFiles["array"] = function(cat) {
|
|
if (!files[cat]) {
|
|
files[cat] = [];
|
|
}
|
|
return files[cat].slice();
|
|
};
|
|
getHrefArray().forEach((function(c) {
|
|
_getFiles[c] = function() {
|
|
return _getFiles["array"](c);
|
|
};
|
|
}));
|
|
_getFiles["hrefArray"] = function() {
|
|
var ret = [];
|
|
if (sharedFolder) {
|
|
return ret;
|
|
}
|
|
getHrefArray().forEach((function(c) {
|
|
ret = ret.concat(_getFiles[c]());
|
|
}));
|
|
return Util.deduplicateString(ret);
|
|
};
|
|
_getFiles[ROOT] = function() {
|
|
var ret = [];
|
|
getFilesRecursively(files[ROOT], ret);
|
|
return ret;
|
|
};
|
|
_getFiles[TRASH] = function() {
|
|
var root = files[TRASH];
|
|
var ret = [];
|
|
var addFiles = function(el) {
|
|
if (isFile(el.element) || isSharedFolder(el.element)) {
|
|
if (ret.indexOf(el.element) === -1) {
|
|
ret.push(el.element);
|
|
}
|
|
} else {
|
|
getFilesRecursively(el.element, ret);
|
|
}
|
|
};
|
|
for (var e in root) {
|
|
if (!Array.isArray(root[e])) {
|
|
error("Trash contains a non-array element");
|
|
return;
|
|
}
|
|
root[e].forEach(addFiles);
|
|
}
|
|
return ret;
|
|
};
|
|
_getFiles[OLD_FILES_DATA] = function() {
|
|
var ret = [];
|
|
if (!files[OLD_FILES_DATA]) {
|
|
return ret;
|
|
}
|
|
files[OLD_FILES_DATA].forEach((function(el) {
|
|
if (el.href && ret.indexOf(el.href) === -1) {
|
|
ret.push(el.href);
|
|
}
|
|
}));
|
|
return ret;
|
|
};
|
|
_getFiles[STATIC_DATA] = function() {
|
|
var ret = [];
|
|
if (!files[STATIC_DATA]) {
|
|
return ret;
|
|
}
|
|
return Object.keys(files[STATIC_DATA]).map(Number).filter(Boolean);
|
|
};
|
|
_getFiles[FILES_DATA] = function() {
|
|
var ret = [];
|
|
if (!files[FILES_DATA]) {
|
|
return ret;
|
|
}
|
|
return Object.keys(files[FILES_DATA]).map(Number).filter(Boolean);
|
|
};
|
|
_getFiles[SHARED_FOLDERS] = function() {
|
|
var ret = [];
|
|
if (!files[SHARED_FOLDERS]) {
|
|
return ret;
|
|
}
|
|
return Object.keys(files[SHARED_FOLDERS]).map(Number).filter(Boolean);
|
|
};
|
|
var getFiles = exp.getFiles = function(categories) {
|
|
var ret = [];
|
|
if (!categories || !categories.length) {
|
|
categories = [ ROOT, "hrefArray", TRASH, OLD_FILES_DATA, FILES_DATA, SHARED_FOLDERS ];
|
|
}
|
|
categories.forEach((function(c) {
|
|
if (typeof _getFiles[c] === "function") {
|
|
ret = ret.concat(_getFiles[c]());
|
|
}
|
|
}));
|
|
return Util.deduplicateString(ret);
|
|
};
|
|
var getIdFromHref = exp.getIdFromHref = function(_href) {
|
|
var result;
|
|
var noPassword = function(str) {
|
|
if (!str) {
|
|
return;
|
|
}
|
|
var parsed = Hash.parsePadUrl(str);
|
|
return parsed.getUrl().replace(/\/p\/?/, "/");
|
|
};
|
|
var href = noPassword(_href);
|
|
getFiles([ FILES_DATA ]).some((function(id) {
|
|
if (noPassword(getHref(files[FILES_DATA][id])) === href || noPassword(files[FILES_DATA][id].roHref) === href) {
|
|
result = id;
|
|
return true;
|
|
}
|
|
}));
|
|
return result;
|
|
};
|
|
exp.getSFIdFromHref = function(_href) {
|
|
var result;
|
|
var noPassword = function(str) {
|
|
if (!str) {
|
|
return;
|
|
}
|
|
var parsed = Hash.parsePadUrl(str);
|
|
return parsed.getUrl().replace(/\/p\/?/, "/");
|
|
};
|
|
var href = noPassword(_href);
|
|
getFiles([ SHARED_FOLDERS ]).some((function(id) {
|
|
if (noPassword(getHref(files[SHARED_FOLDERS][id])) === href || noPassword(files[SHARED_FOLDERS][id].roHref) === href) {
|
|
result = id;
|
|
return true;
|
|
}
|
|
}));
|
|
return result;
|
|
};
|
|
var _findFileInRoot = function(path, files) {
|
|
if (!isPathIn(path, [ ROOT, TRASH ])) {
|
|
return [];
|
|
}
|
|
files = Array.isArray(files) ? files : [ files ];
|
|
var paths = {};
|
|
var root = find(path);
|
|
var addPaths = function(p) {
|
|
Object.keys(p).forEach((file => {
|
|
paths[file] ||= [];
|
|
Array.prototype.push.apply(paths[file], p[file]);
|
|
}));
|
|
};
|
|
if (isFile(root) || isSharedFolder(root)) {
|
|
files.some((file => {
|
|
if (compareFiles(file, root)) {
|
|
paths[file] ||= [];
|
|
paths[file].push(path);
|
|
}
|
|
}));
|
|
return paths;
|
|
}
|
|
if (isFolder(root)) {
|
|
for (var e in root) {
|
|
let nPath = path.slice();
|
|
nPath.push(e);
|
|
addPaths(_findFileInRoot(nPath, files));
|
|
}
|
|
}
|
|
return paths;
|
|
};
|
|
var _findFileInHrefArray = function(rootName, toFind) {
|
|
if (sharedFolder) {
|
|
return {};
|
|
}
|
|
if (!files[rootName]) {
|
|
return {};
|
|
}
|
|
var unsorted = files[rootName].slice();
|
|
var ret = {};
|
|
var i = -1;
|
|
toFind.forEach((file => {
|
|
while ((i = unsorted.indexOf(file, i + 1)) !== -1) {
|
|
ret[file] ||= [];
|
|
ret[file].push([ rootName, i ]);
|
|
}
|
|
}));
|
|
return ret;
|
|
};
|
|
var _findFileInTrash = function(path, files) {
|
|
if (sharedFolder) {
|
|
return [];
|
|
}
|
|
var root = find(path);
|
|
var paths = {};
|
|
var addPaths = function(p) {
|
|
Object.keys(p).forEach((file => {
|
|
paths[file] ||= [];
|
|
Array.prototype.push.apply(paths[file], p[file]);
|
|
}));
|
|
};
|
|
if (path.length === 1 && typeof root === "object") {
|
|
Object.keys(root).forEach((function(key) {
|
|
var arr = root[key];
|
|
if (!Array.isArray(arr)) {
|
|
return;
|
|
}
|
|
var nPath = path.slice();
|
|
nPath.push(key);
|
|
addPaths(_findFileInTrash(nPath, files));
|
|
}));
|
|
}
|
|
if (path.length === 2) {
|
|
if (!Array.isArray(root)) {
|
|
return [];
|
|
}
|
|
root.forEach((function(el, i) {
|
|
var nPath = path.slice();
|
|
nPath.push(i);
|
|
nPath.push("element");
|
|
if (isFile(el.element)) {
|
|
files.some((file => {
|
|
if (compareFiles(file, el.element)) {
|
|
paths[file] ||= [];
|
|
paths[file].push(nPath);
|
|
}
|
|
}));
|
|
return;
|
|
}
|
|
addPaths(_findFileInTrash(nPath, files));
|
|
}));
|
|
}
|
|
if (path.length >= 4) {
|
|
addPaths(_findFileInRoot(path, files));
|
|
}
|
|
return paths;
|
|
};
|
|
var findFiles = exp.findFiles = function(files) {
|
|
var rootpaths = _findFileInRoot([ ROOT ], files);
|
|
var templatepaths = _findFileInHrefArray(TEMPLATE, files);
|
|
var trashpaths = _findFileInTrash([ TRASH ], files);
|
|
let res = {};
|
|
files.forEach((file => {
|
|
res[file] = [];
|
|
}));
|
|
[ rootpaths, templatepaths, trashpaths ].forEach((r => {
|
|
Object.keys(r).forEach((file => {
|
|
res[file] ||= [];
|
|
Array.prototype.push.apply(res[file], r[file]);
|
|
}));
|
|
}));
|
|
return res;
|
|
};
|
|
var findFile = exp.findFile = function(file) {
|
|
let res = findFiles([ file ]);
|
|
return res[file] || [];
|
|
};
|
|
exp.findChannels = function(channels, includeSharedFolders) {
|
|
var allFilesList = files[FILES_DATA];
|
|
var sfList = files[SHARED_FOLDERS];
|
|
var paths = [ FILES_DATA ];
|
|
if (includeSharedFolders) {
|
|
paths.push(SHARED_FOLDERS);
|
|
}
|
|
return getFiles(paths).filter((function(k) {
|
|
var data = allFilesList[k] || sfList[k] || {};
|
|
return channels.indexOf(data.channel) !== -1;
|
|
}));
|
|
};
|
|
exp.search = function(value) {
|
|
if (typeof value !== "string") {
|
|
return [];
|
|
}
|
|
value = value.trim();
|
|
var res = [];
|
|
var allFilesList = files[FILES_DATA];
|
|
var allSFList = files[SHARED_FOLDERS];
|
|
var lValue = value.toLowerCase();
|
|
var tags;
|
|
if (/^#/.test(lValue)) {
|
|
tags = [ lValue.slice(1).trim() ];
|
|
}
|
|
var containsSearchedTag = function(T) {
|
|
if (!tags) {
|
|
return false;
|
|
}
|
|
if (!T.length) {
|
|
return false;
|
|
}
|
|
T = T.map((function(t) {
|
|
return t.toLowerCase();
|
|
}));
|
|
return tags.some((function(tag) {
|
|
return T.some((function(t) {
|
|
return t === tag;
|
|
}));
|
|
}));
|
|
};
|
|
getFiles([ FILES_DATA, SHARED_FOLDERS ]).forEach((function(id) {
|
|
var data = allFilesList[id] || allSFList[id];
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (Array.isArray(data.tags) && containsSearchedTag(data.tags)) {
|
|
return void res.push(id);
|
|
}
|
|
var title = data.title || data.lastTitle;
|
|
if (title && title.toLowerCase().indexOf(lValue) !== -1 || data.filename && data.filename.toLowerCase().indexOf(lValue) !== -1) {
|
|
res.push(id);
|
|
}
|
|
}));
|
|
var href = Hash.getRelativeHref(value);
|
|
if (href) {
|
|
var id = getIdFromHref(href);
|
|
if (id) {
|
|
res.push(id);
|
|
}
|
|
}
|
|
res = Util.deduplicateString(res);
|
|
var ret = [];
|
|
res.forEach((function(l) {
|
|
ret.push({
|
|
id: l,
|
|
paths: findFile(l),
|
|
data: exp.getFileData(l)
|
|
});
|
|
}));
|
|
var resFolders = [];
|
|
var findFoldersRec = function(folder, path) {
|
|
for (var key in folder) {
|
|
if (isFolder(folder[key]) && !isSharedFolder(folder[key])) {
|
|
if (key.toLowerCase().indexOf(lValue) !== -1) {
|
|
resFolders.push({
|
|
id: null,
|
|
paths: [ path.concat(key) ],
|
|
data: {
|
|
title: key
|
|
}
|
|
});
|
|
}
|
|
findFoldersRec(folder[key], path.concat(key));
|
|
}
|
|
}
|
|
};
|
|
findFoldersRec(files[ROOT], [ ROOT ]);
|
|
resFolders = resFolders.sort((function(a, b) {
|
|
return a.data.title.toLowerCase() > b.data.title.toLowerCase();
|
|
}));
|
|
ret = resFolders.concat(ret);
|
|
return ret;
|
|
};
|
|
exp.getRecentPads = function() {
|
|
var allFiles = files[FILES_DATA];
|
|
var sorted = Object.keys(allFiles).filter((function(a) {
|
|
return allFiles[a];
|
|
})).sort((function(a, b) {
|
|
return allFiles[b].atime - allFiles[a].atime;
|
|
})).map((function(str) {
|
|
return Number(str);
|
|
}));
|
|
return sorted;
|
|
};
|
|
exp.getOwnedPads = function(edPub) {
|
|
var allFiles = files[FILES_DATA];
|
|
return Object.keys(allFiles).filter((function(id) {
|
|
return allFiles[id].owners && allFiles[id].owners.indexOf(edPub) !== -1;
|
|
})).map((function(k) {
|
|
return Number(k);
|
|
}));
|
|
};
|
|
var getAvailableName = exp.getAvailableName = function(parentEl, name) {
|
|
if (typeof parentEl[name] === "undefined") {
|
|
return name;
|
|
}
|
|
var newName = name;
|
|
var i = 1;
|
|
while (typeof parentEl[newName] !== "undefined") {
|
|
newName = name + "_" + i;
|
|
i++;
|
|
}
|
|
return newName;
|
|
};
|
|
var move = exp.move = function(paths, newPath, cb) {
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "move",
|
|
data: {
|
|
paths,
|
|
newPath
|
|
}
|
|
}, cb);
|
|
}
|
|
var toRemove = [];
|
|
paths.forEach((function(p) {
|
|
var parentPath = p.slice();
|
|
parentPath.pop();
|
|
if (comparePath(parentPath, newPath)) {
|
|
return;
|
|
}
|
|
if (isSubpath(newPath, p)) {
|
|
log(Messages.fo_moveFolderToChildError);
|
|
return;
|
|
}
|
|
if (exp.copyElement(p.slice(), newPath)) {
|
|
toRemove.push(p);
|
|
}
|
|
}));
|
|
exp.delete(toRemove, cb);
|
|
};
|
|
exp.restore = function(path, cb) {
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "restore",
|
|
data: {
|
|
path
|
|
}
|
|
}, cb);
|
|
}
|
|
if (!isInTrashRoot(path)) {
|
|
return;
|
|
}
|
|
var parentPath = path.slice();
|
|
parentPath.pop();
|
|
var oldPath = find(parentPath).path;
|
|
move([ path ], oldPath, cb);
|
|
};
|
|
exp.addFolder = function(folderPath, name, cb) {
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "addFolder",
|
|
data: {
|
|
path: folderPath,
|
|
name
|
|
}
|
|
}, cb);
|
|
}
|
|
var parentEl = find(folderPath);
|
|
var folderName = getAvailableName(parentEl, name || NEW_FOLDER_NAME);
|
|
parentEl[folderName] = {};
|
|
var newPath = folderPath.slice();
|
|
newPath.push(folderName);
|
|
cb({
|
|
newPath
|
|
});
|
|
};
|
|
exp.delete = function(paths, cb, nocheck) {
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "delete",
|
|
data: {
|
|
paths,
|
|
nocheck
|
|
}
|
|
}, cb);
|
|
}
|
|
cb = cb || function() {};
|
|
exp.deleteMultiplePermanently(paths, nocheck, cb);
|
|
};
|
|
exp.emptyTrash = function(cb) {
|
|
cb = cb || function() {};
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "emptyTrash"
|
|
}, cb);
|
|
}
|
|
files[TRASH] = {};
|
|
exp.checkDeletedFiles(cb);
|
|
};
|
|
exp.ownedInTrash = function(isOwned) {
|
|
return getFiles([ TRASH ]).map((function(id) {
|
|
var data = isSharedFolder(id) ? files[SHARED_FOLDERS][id] : exp.getFileData(id);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
return isOwned(data.owners) ? data.channel : undefined;
|
|
})).filter(Boolean);
|
|
};
|
|
exp.rename = function(path, newName, cb) {
|
|
cb = cb || function() {};
|
|
if (sframeChan) {
|
|
return void sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "rename",
|
|
data: {
|
|
path,
|
|
newName
|
|
}
|
|
}, cb);
|
|
}
|
|
if (path.length <= 1) {
|
|
logError("Renaming `root` is forbidden");
|
|
return;
|
|
}
|
|
var element = find(path);
|
|
if (isFolder(element) && !isSharedFolder(element)) {
|
|
var parentPath = path.slice();
|
|
var oldName = parentPath.pop();
|
|
if (!newName || !newName.trim() || oldName === newName) {
|
|
return;
|
|
}
|
|
var parentEl = find(parentPath);
|
|
if (typeof parentEl[newName] !== "undefined") {
|
|
log(Messages.fo_existingNameError);
|
|
return;
|
|
}
|
|
parentEl[newName] = element;
|
|
delete parentEl[oldName];
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
return;
|
|
}
|
|
var data;
|
|
if (isSharedFolder(element)) {
|
|
data = files[SHARED_FOLDERS][element];
|
|
} else {
|
|
data = files[FILES_DATA][element] || files[STATIC_DATA][element];
|
|
}
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (files[STATIC_DATA][element]) {
|
|
if (!newName || !newName.trim()) {
|
|
return void cb();
|
|
}
|
|
data.name = newName;
|
|
cb();
|
|
return;
|
|
}
|
|
if (!newName || newName.trim() === "") {
|
|
delete data.filename;
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
return;
|
|
}
|
|
if (getTitle(element, "name") === newName) {
|
|
return;
|
|
}
|
|
data.filename = newName;
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
};
|
|
exp.getTagsList = function() {
|
|
var tags = {};
|
|
var data;
|
|
var pushTag = function(tag) {
|
|
tags[tag] = tags[tag] ? ++tags[tag] : 1;
|
|
};
|
|
for (var id in files[FILES_DATA]) {
|
|
data = files[FILES_DATA][id];
|
|
if (!data.tags || !Array.isArray(data.tags)) {
|
|
continue;
|
|
}
|
|
data.tags.forEach(pushTag);
|
|
}
|
|
return tags;
|
|
};
|
|
return exp;
|
|
};
|
|
return module;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireCommonConstants(), requireUserObjectSetter(), requireCrypto(), undefined);
|
|
}
|
|
})();
|
|
})(userObject$1);
|
|
return userObject$1.exports;
|
|
}
|
|
var sharedfolder = {
|
|
exports: {}
|
|
};
|
|
var nthen = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredNthen;
|
|
function requireNthen() {
|
|
if (hasRequiredNthen) return nthen.exports;
|
|
hasRequiredNthen = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var nThen = function(next) {
|
|
var funcs = [];
|
|
var timeouts = [];
|
|
var calls = 0;
|
|
var abort;
|
|
var waitFor = function(func) {
|
|
calls++;
|
|
return function() {
|
|
if (func) {
|
|
func.apply(null, arguments);
|
|
}
|
|
calls = (calls || 1) - 1;
|
|
while (!calls && funcs.length && !abort) {
|
|
funcs.shift()(waitFor);
|
|
}
|
|
};
|
|
};
|
|
waitFor.abort = function() {
|
|
timeouts.forEach(clearTimeout);
|
|
abort = 1;
|
|
};
|
|
var ret = {
|
|
nThen: function(next) {
|
|
if (!abort) {
|
|
if (!calls) {
|
|
next(waitFor);
|
|
} else {
|
|
funcs.push(next);
|
|
}
|
|
}
|
|
return ret;
|
|
},
|
|
orTimeout: function(func, milliseconds) {
|
|
if (abort) {
|
|
return ret;
|
|
}
|
|
if (!milliseconds) {
|
|
throw Error("Must specify milliseconds to orTimeout()");
|
|
}
|
|
var cto;
|
|
var timeout = setTimeout((function() {
|
|
while (funcs.shift() !== cto) {}
|
|
func(waitFor);
|
|
calls = (calls || 1) - 1;
|
|
while (!calls && funcs.length) {
|
|
funcs.shift()(waitFor);
|
|
}
|
|
}), milliseconds);
|
|
funcs.push(cto = function() {
|
|
var idx = timeouts.indexOf(timeout);
|
|
if (idx > -1) {
|
|
timeouts.splice(idx, 1);
|
|
clearTimeout(timeout);
|
|
return;
|
|
}
|
|
throw new Error("timeout not listed in array");
|
|
});
|
|
timeouts.push(timeout);
|
|
return ret;
|
|
}
|
|
};
|
|
return ret.nThen(next);
|
|
};
|
|
{
|
|
module.exports = nThen;
|
|
}
|
|
})();
|
|
})(nthen);
|
|
return nthen.exports;
|
|
}
|
|
var hasRequiredSharedfolder;
|
|
function requireSharedfolder() {
|
|
if (hasRequiredSharedfolder) return sharedfolder.exports;
|
|
hasRequiredSharedfolder = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Hash, Util, UserObject, Cache, nThen, Crypto, Listmap, ChainPad) => {
|
|
var SF = {};
|
|
var allSharedFolders = {};
|
|
SF.checkMigration = function(secondaryKey, proxy, uo, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!proxy) {
|
|
return void cb();
|
|
}
|
|
if (!secondaryKey) {
|
|
return void cb();
|
|
}
|
|
if (proxy.version >= 2) {
|
|
return void cb();
|
|
}
|
|
if (!proxy.migrateRo) {
|
|
return void uo.migrateReadOnly(cb);
|
|
}
|
|
var done = false;
|
|
var to;
|
|
var it = setInterval((function() {
|
|
if (proxy.version >= 2) {
|
|
done = true;
|
|
clearTimeout(to);
|
|
clearInterval(it);
|
|
return void cb();
|
|
}
|
|
}), 100);
|
|
to = setTimeout((function() {
|
|
clearInterval(it);
|
|
uo.migrateReadOnly((function() {
|
|
done = true;
|
|
cb();
|
|
}));
|
|
}), 2e4);
|
|
var path = [ "version" ];
|
|
proxy.on("change", path, (function() {
|
|
if (done) {
|
|
return;
|
|
}
|
|
if (proxy.version >= 2) {
|
|
done = true;
|
|
clearTimeout(to);
|
|
clearInterval(it);
|
|
cb();
|
|
}
|
|
}));
|
|
};
|
|
SF.migrate = function(channel) {
|
|
var sf = allSharedFolders[channel];
|
|
if (!sf) {
|
|
return;
|
|
}
|
|
var clients = sf.teams;
|
|
if (!Array.isArray(clients) || !clients.length) {
|
|
return;
|
|
}
|
|
var c = clients[0];
|
|
if (!c.secondaryKey) {
|
|
return;
|
|
}
|
|
var f = Util.find(c, [ "store", "manager", "folders", c.id ]);
|
|
if (!f) {
|
|
return;
|
|
}
|
|
if (!f.proxy || f.proxy.version) {
|
|
return;
|
|
}
|
|
f.userObject.migrateReadOnly((function() {
|
|
clients.forEach((function(obj) {
|
|
var uo = Util.find(obj, [ "store", "manager", "folders", obj.id, "userObject" ]);
|
|
uo.setReadOnly(false, obj.secondarykey);
|
|
}));
|
|
}));
|
|
};
|
|
SF.load = function(config, id, data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var network = config.network;
|
|
var store = config.store;
|
|
var isNew = config.isNew;
|
|
var isNewChannel = config.isNewChannel;
|
|
var teamId = store.id;
|
|
var handler = store.handleSharedFolder;
|
|
var href = store.manager.user.userObject.getHref(data);
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets("drive", parsed.hash, data.password);
|
|
if (!secret.keys) {
|
|
store.manager.deprecateProxy(id);
|
|
return void cb(null);
|
|
}
|
|
var secondaryKey = secret.keys.secondaryKey;
|
|
nThen((function(waitFor) {
|
|
if (config.cache) {
|
|
Cache.getChannelCache(secret.channel, waitFor((function(err) {
|
|
if (err === "EINVAL") {
|
|
waitFor.abort();
|
|
store.manager.restrictedProxy(id, secret.channel);
|
|
return void cb(null);
|
|
}
|
|
})));
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
isNewChannel(null, {
|
|
channel: secret.channel
|
|
}, waitFor((function(obj) {
|
|
if (obj.isNew && !isNew) {
|
|
store.manager.deprecateProxy(id, secret.channel, obj.reason);
|
|
waitFor.abort();
|
|
return void cb(null);
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
var sf = allSharedFolders[secret.channel];
|
|
if (sf && sf.readOnly && secondaryKey) {
|
|
SF.upgrade(secret.channel, secret);
|
|
}
|
|
if (sf && sf.ready && sf.rt) {
|
|
setTimeout((function() {
|
|
var leave = function() {
|
|
SF.leave(secret.channel, teamId);
|
|
};
|
|
store.manager.addProxy(id, sf.rt, leave, secondaryKey);
|
|
cb(sf.rt);
|
|
}));
|
|
sf.teams.push({
|
|
cb,
|
|
store,
|
|
id
|
|
});
|
|
if (handler) {
|
|
handler(id, sf.rt);
|
|
}
|
|
return;
|
|
}
|
|
if (sf && !sf.ready && sf.rt) {
|
|
sf.teams.push({
|
|
cb,
|
|
store,
|
|
secondaryKey,
|
|
id
|
|
});
|
|
if (handler) {
|
|
handler(id, sf.rt);
|
|
}
|
|
return;
|
|
}
|
|
sf = allSharedFolders[secret.channel] = {
|
|
teams: [ {
|
|
cb,
|
|
store,
|
|
secondaryKey,
|
|
id
|
|
} ],
|
|
readOnly: !Boolean(secondaryKey)
|
|
};
|
|
var owners = data.owners;
|
|
var listmapConfig = {
|
|
data: {},
|
|
channel: secret.channel,
|
|
readOnly: !Boolean(secondaryKey),
|
|
crypto: Crypto.createEncryptor(secret.keys),
|
|
userName: "sharedFolder",
|
|
logLevel: 1,
|
|
ChainPad,
|
|
classic: true,
|
|
network,
|
|
Cache,
|
|
metadata: {
|
|
validateKey: secret.keys.validateKey || undefined,
|
|
owners
|
|
},
|
|
onRejected: config.Store && config.Store.onRejected
|
|
};
|
|
var rt = sf.rt = Listmap.create(listmapConfig);
|
|
rt.proxy.on("cacheready", (function() {
|
|
if (!sf.teams) {
|
|
return;
|
|
}
|
|
sf.teams.forEach((function(obj) {
|
|
var leave = function() {
|
|
SF.leave(secret.channel, obj.store.id);
|
|
};
|
|
rt.cache = true;
|
|
obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey, config.updatePassword);
|
|
config.updatePassword = false;
|
|
obj.cb(sf.rt);
|
|
}));
|
|
sf.ready = true;
|
|
}));
|
|
rt.proxy.on("ready", (function() {
|
|
if (isNew && !Object.keys(rt.proxy).length) {
|
|
rt.proxy.version = 2;
|
|
}
|
|
if (!sf.teams) {
|
|
return;
|
|
}
|
|
sf.teams.forEach((function(obj) {
|
|
var leave = function() {
|
|
SF.leave(secret.channel, obj.store.id);
|
|
};
|
|
rt.cache = false;
|
|
obj.store.manager.addProxy(obj.id, rt, leave, obj.secondaryKey, config.updatePassword);
|
|
obj.cb(sf.rt);
|
|
}));
|
|
sf.ready = true;
|
|
}));
|
|
rt.proxy.on("error", (function(info) {
|
|
if (info && info.error) {
|
|
if (info.error === "EDELETED") {
|
|
try {
|
|
sf.teams.forEach((function(obj) {
|
|
obj.store.manager.deprecateProxy(obj.id, secret.channel, info.message);
|
|
if (obj.store.handleSharedFolder) {
|
|
obj.store.handleSharedFolder(obj.id, null);
|
|
}
|
|
obj.cb();
|
|
}));
|
|
} catch (e) {}
|
|
delete allSharedFolders[secret.channel];
|
|
return void cb();
|
|
}
|
|
if (info.error === "ERESTRICTED") {
|
|
sf.teams.forEach((function(obj) {
|
|
obj.store.manager.restrictedProxy(obj.id, secret.channel);
|
|
obj.cb();
|
|
}));
|
|
delete allSharedFolders[secret.channel];
|
|
return void cb();
|
|
}
|
|
}
|
|
}));
|
|
if (handler) {
|
|
handler(id, rt);
|
|
}
|
|
}));
|
|
};
|
|
SF.upgrade = function(channel, secret) {
|
|
var sf = allSharedFolders[channel];
|
|
if (!sf || !sf.readOnly) {
|
|
return;
|
|
}
|
|
if (!sf.rt.setReadOnly) {
|
|
return;
|
|
}
|
|
if (!secret.keys || !secret.keys.editKeyStr) {
|
|
return;
|
|
}
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
sf.readOnly = false;
|
|
sf.rt.setReadOnly(false, crypto);
|
|
};
|
|
SF.leave = function(channel, teamId) {
|
|
var sf = allSharedFolders[channel];
|
|
if (!sf) {
|
|
return;
|
|
}
|
|
var clients = sf.teams;
|
|
if (!Array.isArray(clients)) {
|
|
return;
|
|
}
|
|
var idx;
|
|
clients.some((function(obj, i) {
|
|
if (obj.store.id === teamId) {
|
|
if (obj.store.handleSharedFolder) {
|
|
obj.store.handleSharedFolder(obj.id, null);
|
|
}
|
|
idx = i;
|
|
return true;
|
|
}
|
|
}));
|
|
if (typeof idx === "undefined") {
|
|
return;
|
|
}
|
|
clients.splice(idx, 1);
|
|
if (clients.length) {
|
|
return;
|
|
}
|
|
if (sf.rt && sf.rt.stop) {
|
|
sf.rt.stop();
|
|
}
|
|
};
|
|
SF.updatePassword = function(Store, data, network, cb) {
|
|
var oldChannel = data.oldChannel;
|
|
var href = data.href;
|
|
var password = data.password;
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, password);
|
|
var sf = allSharedFolders[oldChannel];
|
|
if (!sf) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (sf.rt && sf.rt.stop) {
|
|
try {
|
|
sf.rt.stop();
|
|
} catch (e) {}
|
|
}
|
|
var nt = nThen;
|
|
sf.teams.forEach((function(obj) {
|
|
nt = nt((function(waitFor) {
|
|
var s = obj.store;
|
|
var sfId = obj.id;
|
|
var shared = Util.find(s.proxy, [ "drive", UserObject.SHARED_FOLDERS ]) || {};
|
|
if (!sfId || !shared[sfId]) {
|
|
return;
|
|
}
|
|
var sf = JSON.parse(JSON.stringify(shared[sfId]));
|
|
sf.password = password;
|
|
SF.load({
|
|
network,
|
|
store: s,
|
|
updatePassword: true,
|
|
Store,
|
|
isNewChannel: Store.isNewChannel
|
|
}, sfId, sf, waitFor());
|
|
if (!s.rpc) {
|
|
return;
|
|
}
|
|
s.rpc.unpin([ oldChannel ], waitFor());
|
|
s.rpc.pin([ secret.channel ], waitFor());
|
|
})).nThen;
|
|
}));
|
|
nt((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
SF.loadSharedFolders = function(Store, network, store, drive, userObject, waitFor, progress, cache) {
|
|
var shared = drive[UserObject.SHARED_FOLDERS] || {};
|
|
var steps = Object.keys(shared).length;
|
|
var i = 1;
|
|
var w = waitFor();
|
|
progress = progress || function() {};
|
|
nThen((function(waitFor) {
|
|
Object.keys(shared).forEach((function(id) {
|
|
var sf = shared[id];
|
|
SF.load({
|
|
network,
|
|
store,
|
|
Store,
|
|
cache,
|
|
isNewChannel: Store.isNewChannel
|
|
}, id, sf, waitFor((function() {
|
|
progress({
|
|
progress: i,
|
|
max: steps
|
|
});
|
|
i++;
|
|
})));
|
|
}));
|
|
})).nThen((function() {
|
|
setTimeout(w);
|
|
}));
|
|
};
|
|
SF.isSharedFolderChannel = function(chanId) {
|
|
return Object.keys(allSharedFolders).includes(chanId);
|
|
};
|
|
return SF;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonHash(), requireCommonUtil(), requireUserObject(), requireCacheStore(), requireNthen(), requireCrypto(), requireChainpadListmap(), requireChainpad_dist());
|
|
}
|
|
})();
|
|
})(sharedfolder);
|
|
return sharedfolder.exports;
|
|
}
|
|
var hasRequiredProxyManager;
|
|
function requireProxyManager() {
|
|
if (hasRequiredProxyManager) return proxyManager$1.exports;
|
|
hasRequiredProxyManager = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (UserObject, Util, Hash, SF, Messages = {}, Feedback, nThen) => {
|
|
let setCustomize = data => {
|
|
Messages = data.Messages;
|
|
UserObject.setCustomize(data);
|
|
};
|
|
var getConfig = function(Env) {
|
|
var cfg = {};
|
|
for (var k in Env.cfg) {
|
|
cfg[k] = Env.cfg[k];
|
|
}
|
|
return cfg;
|
|
};
|
|
var addProxy = function(Env, id, lm, leave, editKey, force) {
|
|
if (Env.folders[id] && !force && !Env.folders[id].restricted) {
|
|
if (Env.folders[id].offline && !lm.cache && Env.Store) {
|
|
Env.folders[id].offline = false;
|
|
if (Env.folders[id].userObject.fixFiles) {
|
|
Env.folders[id].userObject.fixFiles();
|
|
}
|
|
Env.Store.refreshDriveUI();
|
|
}
|
|
return;
|
|
}
|
|
var cfg = getConfig(Env);
|
|
cfg.sharedFolder = true;
|
|
cfg.id = id;
|
|
cfg.editKey = editKey;
|
|
cfg.rt = lm.realtime;
|
|
cfg.readOnly = Boolean(!editKey);
|
|
var userObject = UserObject.init(lm.proxy, cfg);
|
|
if (userObject.fixFiles) {
|
|
userObject.fixFiles();
|
|
}
|
|
var proxy = lm.proxy;
|
|
if (proxy.metadata && proxy.metadata.title) {
|
|
var sf = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
|
if (sf) {
|
|
sf.lastTitle = proxy.metadata.title;
|
|
}
|
|
}
|
|
Env.folders[id] = {
|
|
proxy: lm.proxy,
|
|
userObject,
|
|
leave,
|
|
restricted: proxy.restricted,
|
|
offline: Boolean(lm.cache)
|
|
};
|
|
if (proxy.on) {
|
|
proxy.on("disconnect", (function() {
|
|
Env.folders[id].offline = true;
|
|
}));
|
|
proxy.on("reconnect", (function() {
|
|
Env.folders[id].offline = false;
|
|
}));
|
|
}
|
|
return userObject;
|
|
};
|
|
var removeProxy = function(Env, id) {
|
|
var f = Env.folders[id];
|
|
if (!f) {
|
|
return;
|
|
}
|
|
f.leave();
|
|
delete Env.folders[id];
|
|
};
|
|
var sendNotification = (Env, sfId, title) => {
|
|
var mailbox = Env.store.mailbox;
|
|
if (!mailbox) {
|
|
return;
|
|
}
|
|
var team = Env.cfg.teamId;
|
|
var box;
|
|
if (team) {
|
|
let teams = Env.store.modules["team"].getTeamsData();
|
|
box = teams[team];
|
|
} else {
|
|
let md = Env.Store.getMetadata(null, null, (() => {}));
|
|
box = md.user;
|
|
}
|
|
mailbox.sendTo("SF_DELETED", {
|
|
sfId,
|
|
team,
|
|
title
|
|
}, {
|
|
curvePublic: box.curvePublic,
|
|
channel: box.notifications
|
|
}, (err => {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
var deprecateProxy = function(Env, id, channel, reason) {
|
|
if (Env.folders[id] && Env.folders[id].deleting) {
|
|
return;
|
|
}
|
|
if (Env.user.userObject.readOnly) {
|
|
var lm = {
|
|
proxy: {
|
|
deprecated: true
|
|
}
|
|
};
|
|
removeProxy(Env, id);
|
|
addProxy(Env, id, lm, (function() {}));
|
|
return void Env.Store.refreshDriveUI();
|
|
}
|
|
if (channel) {
|
|
Env.unpinPads([ channel ], (function() {}));
|
|
}
|
|
if (reason && reason !== "PASSWORD_CHANGE") {
|
|
let temp = Util.find(Env, [ "user", "proxy", UserObject.SHARED_FOLDERS ]);
|
|
let title = temp[id] && temp[id].lastTitle;
|
|
if (title) {
|
|
sendNotification(Env, id, title);
|
|
}
|
|
delete temp[id];
|
|
if (Env.Store && Env.Store.refreshDriveUI) {
|
|
Env.Store.refreshDriveUI();
|
|
}
|
|
return;
|
|
}
|
|
Env.user.userObject.deprecateSharedFolder(id, reason);
|
|
removeProxy(Env, id);
|
|
if (Env.Store && Env.Store.refreshDriveUI) {
|
|
Env.Store.refreshDriveUI();
|
|
}
|
|
};
|
|
var restrictedProxy = function(Env, id) {
|
|
var lm = {
|
|
proxy: {
|
|
restricted: true,
|
|
root: {},
|
|
filesData: {}
|
|
}
|
|
};
|
|
removeProxy(Env, id);
|
|
addProxy(Env, id, lm, (function() {}));
|
|
return void Env.Store.refreshDriveUI();
|
|
};
|
|
var _ownedByMe = function(Env, owners) {
|
|
return Array.isArray(owners) && owners.indexOf(Env.edPublic) !== -1;
|
|
};
|
|
var _ownedByOther = function(Env, owners) {
|
|
return Array.isArray(owners) && owners.length && (!Env.edPublic || owners.indexOf(Env.edPublic) === -1);
|
|
};
|
|
var _getUserObjects = function(Env) {
|
|
var userObjects = [ Env.user.userObject ];
|
|
var foldersUO = Object.keys(Env.folders).map((function(k) {
|
|
return Env.folders[k].userObject;
|
|
}));
|
|
Array.prototype.push.apply(userObjects, foldersUO);
|
|
return userObjects;
|
|
};
|
|
var _getUserObjectFromId = function(Env, id) {
|
|
var userObjects = _getUserObjects(Env);
|
|
var userObject = Env.user.userObject;
|
|
userObjects.some((function(uo) {
|
|
if (Object.keys(uo.getFileData(id)).length) {
|
|
userObject = uo;
|
|
return true;
|
|
}
|
|
}));
|
|
return userObject;
|
|
};
|
|
var _getUserObjectPath = function(Env, uo) {
|
|
var fId = Number(uo.id);
|
|
if (!fId) {
|
|
return;
|
|
}
|
|
var fPath = Env.user.userObject.findFile(fId)[0];
|
|
return fPath;
|
|
};
|
|
var findChannel = function(Env, channel, editable) {
|
|
var ret = [];
|
|
Env.user.userObject.findChannels([ channel ], true).forEach((function(id) {
|
|
var data = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
|
if (data && !editable) {
|
|
data = JSON.parse(JSON.stringify(data));
|
|
}
|
|
if (!data) {
|
|
data = Env.user.userObject.getFileData(id, editable);
|
|
}
|
|
ret.push({
|
|
id,
|
|
data,
|
|
userObject: Env.user.userObject
|
|
});
|
|
}));
|
|
Object.keys(Env.folders).forEach((function(fId) {
|
|
Env.folders[fId].userObject.findChannels([ channel ]).forEach((function(id) {
|
|
ret.push({
|
|
id,
|
|
fId,
|
|
data: Env.folders[fId].userObject.getFileData(id, editable),
|
|
userObject: Env.folders[fId].userObject
|
|
});
|
|
}));
|
|
}));
|
|
return ret;
|
|
};
|
|
var findHref = function(Env, href) {
|
|
var ret = [];
|
|
var id = Env.user.userObject.getIdFromHref(href);
|
|
if (id) {
|
|
ret.push({
|
|
data: Env.user.userObject.getFileData(id),
|
|
userObject: Env.user.userObject
|
|
});
|
|
}
|
|
Object.keys(Env.folders).forEach((function(fId) {
|
|
var id = Env.folders[fId].userObject.getIdFromHref(href);
|
|
if (!id) {
|
|
return;
|
|
}
|
|
ret.push({
|
|
fId,
|
|
data: Env.folders[fId].userObject.getFileData(id),
|
|
userObject: Env.folders[fId].userObject
|
|
});
|
|
}));
|
|
return ret;
|
|
};
|
|
var findFile = function(Env, id) {
|
|
var ret = [];
|
|
var userObjects = _getUserObjects(Env);
|
|
let mainRes = {};
|
|
userObjects.forEach((function(uo) {
|
|
var fId = Number(uo.id);
|
|
let results;
|
|
if (!fId) {
|
|
let ids = [ id ];
|
|
let fIds = userObjects.map((uo => +uo.id)).filter(Boolean);
|
|
Array.prototype.push.apply(ids, fIds);
|
|
mainRes = uo.findFiles(ids);
|
|
results = mainRes[id];
|
|
} else {
|
|
results = uo.findFile(id);
|
|
let fPath = (mainRes[fId] || [])[0];
|
|
if (!fPath) {
|
|
return;
|
|
}
|
|
results.forEach((function(p) {
|
|
Array.prototype.unshift.apply(p, fPath);
|
|
}));
|
|
}
|
|
Array.prototype.push.apply(ret, results);
|
|
}));
|
|
return ret;
|
|
};
|
|
var findFiles = function(Env, ids) {
|
|
var ret = {};
|
|
var userObjects = _getUserObjects(Env);
|
|
let mainRes = {};
|
|
userObjects.forEach((function(uo) {
|
|
var fId = Number(uo.id);
|
|
if (!uo.id) {
|
|
let fIds = userObjects.map((uo => +uo.id)).filter(Boolean);
|
|
Array.prototype.push.apply(ids, fIds);
|
|
}
|
|
var results = uo.findFiles(ids);
|
|
if (!uo.id) {
|
|
mainRes = results;
|
|
}
|
|
if (fId) {
|
|
let fPath = (mainRes[fId] || [])[0];
|
|
if (!fPath) {
|
|
return;
|
|
}
|
|
Object.keys(results).forEach((file => {
|
|
results[file].forEach((p => {
|
|
Array.prototype.unshift.apply(p, fPath);
|
|
}));
|
|
}));
|
|
}
|
|
Object.keys(results).forEach((file => {
|
|
ret[file] ||= [];
|
|
Array.prototype.push.apply(ret[file], results[file]);
|
|
}));
|
|
}));
|
|
return ret;
|
|
};
|
|
var _findChannels = function(Env, channels, onlyMain) {
|
|
if (onlyMain) {
|
|
return Env.user.userObject.findChannels(channels);
|
|
}
|
|
var ret = [];
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
var results = uo.findChannels(channels);
|
|
Array.prototype.push.apply(ret, results);
|
|
}));
|
|
ret = Util.deduplicateString(ret);
|
|
return ret;
|
|
};
|
|
var _getFileData = function(Env, id, editable) {
|
|
var userObjects = _getUserObjects(Env);
|
|
var data = {};
|
|
userObjects.some((function(uo) {
|
|
data = uo.getFileData(id, editable);
|
|
if (data && Object.keys(data).length) {
|
|
return true;
|
|
}
|
|
}));
|
|
return data;
|
|
};
|
|
var getSharedFolderData = function(Env, id) {
|
|
var inHistory;
|
|
if (Env.isHistoryMode && !Env.folders[id]) {
|
|
inHistory = true;
|
|
} else if (!Env.folders[id]) {
|
|
return {};
|
|
}
|
|
var proxy = inHistory ? {} : Env.folders[id].proxy;
|
|
if (Object.keys(proxy.metadata || {}).length > 1) {
|
|
proxy.metadata = {
|
|
title: proxy.metadata.title
|
|
};
|
|
}
|
|
var obj = Util.clone(proxy.metadata || {});
|
|
for (var k in Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {}) {
|
|
if (typeof Env.user.proxy[UserObject.SHARED_FOLDERS][id][k] === "undefined") {
|
|
continue;
|
|
}
|
|
var data = Util.clone(Env.user.proxy[UserObject.SHARED_FOLDERS][id][k]);
|
|
if (k === "href" && data.indexOf("#") === -1) {
|
|
try {
|
|
data = Env.user.userObject.cryptor.decrypt(data);
|
|
} catch (e) {}
|
|
}
|
|
if (k === "href" && data.indexOf("#") === -1) {
|
|
data = undefined;
|
|
}
|
|
obj[k] = data;
|
|
}
|
|
return obj;
|
|
};
|
|
var _resolvePath = function(Env, path) {
|
|
var res = {
|
|
id: null,
|
|
userObject: Env.user.userObject,
|
|
path
|
|
};
|
|
if (!Array.isArray(path) || path.length <= 1) {
|
|
return res;
|
|
}
|
|
var current;
|
|
var uo = Env.user.userObject;
|
|
for (var i = 2; i < path.length; i++) {
|
|
current = uo.find(path.slice(0, i));
|
|
if (uo.isSharedFolder(current)) {
|
|
res = {
|
|
id: current,
|
|
userObject: Env.folders[current]?.userObject,
|
|
path: path.slice(i)
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
};
|
|
var _resolvePaths = function(Env, paths) {
|
|
var main = [];
|
|
var folders = {};
|
|
paths.forEach((function(path) {
|
|
var r = _resolvePath(Env, path);
|
|
if (r.id) {
|
|
if (!folders[r.id]) {
|
|
folders[r.id] = [ r.path ];
|
|
} else {
|
|
folders[r.id].push(r.path);
|
|
}
|
|
} else {
|
|
main.push(r.path);
|
|
}
|
|
}));
|
|
return {
|
|
main,
|
|
folders
|
|
};
|
|
};
|
|
var _isInSharedFolder = function(Env, path) {
|
|
var resolved = _resolvePath(Env, path);
|
|
return typeof resolved.id === "number" ? resolved.id : false;
|
|
};
|
|
var _isDuplicateOwned = function(Env, path, id) {
|
|
if (path && _isInSharedFolder(Env, path)) {
|
|
return;
|
|
}
|
|
var data = _getFileData(Env, id || Env.user.userObject.find(path));
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (!_ownedByMe(Env, data.owners)) {
|
|
return;
|
|
}
|
|
var channel = data.channel;
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
var foldersUO = Object.keys(Env.folders).map((function(k) {
|
|
return Env.folders[k].userObject;
|
|
}));
|
|
return foldersUO.some((function(uo) {
|
|
return uo.findChannels([ channel ]).length;
|
|
}));
|
|
};
|
|
var _getCopyFromPaths = function(Env, paths, userObject) {
|
|
var data = [];
|
|
var toNotRemove = [];
|
|
paths.forEach((function(path, idx) {
|
|
var el = userObject.find(path);
|
|
var files = [];
|
|
var key = path[path.length - 1];
|
|
if (userObject.isFile(el)) {
|
|
files.push(el);
|
|
} else if (userObject.isSharedFolder(el)) {
|
|
files.push(el);
|
|
var obj = Env.folders[el].proxy.metadata || {};
|
|
if (obj) {
|
|
key = obj.title;
|
|
}
|
|
} else {
|
|
try {
|
|
el = JSON.parse(JSON.stringify(el));
|
|
} catch (e) {
|
|
return undefined;
|
|
}
|
|
userObject.getFilesRecursively(el, files);
|
|
}
|
|
if (files.some((function(f) {
|
|
return userObject.isSharedFolder(f);
|
|
}))) {
|
|
if (Env.cfg && Env.cfg.log) {
|
|
Env.cfg.log(Messages._getKey("fm_moveNestedSF", [ key ]));
|
|
}
|
|
toNotRemove.unshift(idx);
|
|
return;
|
|
}
|
|
files = Util.deduplicateString(files);
|
|
var filesData = {};
|
|
files.forEach((function(f) {
|
|
filesData[f] = userObject.getFileData(f);
|
|
}));
|
|
data.push({
|
|
el,
|
|
data: filesData,
|
|
key
|
|
});
|
|
}));
|
|
toNotRemove.forEach((function(idx) {
|
|
paths.splice(idx, 1);
|
|
}));
|
|
return data;
|
|
};
|
|
var getEditHash = function(Env, channel) {
|
|
var res = findChannel(Env, channel);
|
|
var stronger;
|
|
res.some((function(obj) {
|
|
if (!obj || !obj.data || !obj.data.href) {
|
|
return;
|
|
}
|
|
var parsed = Hash.parsePadUrl(obj.data.href);
|
|
var parsedHash = parsed.hashData;
|
|
if (!parsedHash || parsedHash.mode === "view") {
|
|
return;
|
|
}
|
|
stronger = parsed.hash;
|
|
return true;
|
|
}));
|
|
return stronger;
|
|
};
|
|
var _move = function(Env, data, cb) {
|
|
var resolved = _resolvePaths(Env, data.paths);
|
|
var newResolved = _resolvePath(Env, data.newPath);
|
|
var copy = data.copy;
|
|
if (!newResolved.userObject.isFolder(newResolved.path)) {
|
|
return void cb();
|
|
}
|
|
nThen((function(waitFor) {
|
|
if (resolved.main.length) {
|
|
if (!newResolved.id) {
|
|
Env.user.userObject.move(resolved.main, newResolved.path, waitFor());
|
|
} else {
|
|
var toCopy = _getCopyFromPaths(Env, resolved.main, Env.user.userObject);
|
|
var newUserObject = newResolved.userObject;
|
|
toCopy.forEach((function(obj) {
|
|
newUserObject.copyFromOtherDrive(newResolved.path, obj.el, obj.data, obj.key);
|
|
}));
|
|
if (copy) {
|
|
return;
|
|
}
|
|
if (resolved.main.length) {
|
|
Env.user.userObject.delete(resolved.main, waitFor());
|
|
}
|
|
}
|
|
}
|
|
var folderIds = Object.keys(resolved.folders);
|
|
if (folderIds.length) {
|
|
folderIds.forEach((function(fIdStr) {
|
|
var fId = Number(fIdStr);
|
|
var paths = resolved.folders[fId];
|
|
if (newResolved.id === fId) {
|
|
newResolved.userObject.move(paths, newResolved.path, waitFor());
|
|
} else {
|
|
var uoFrom = Env.folders[fId].userObject;
|
|
var uoTo = newResolved.userObject;
|
|
var toCopy = _getCopyFromPaths(Env, paths, uoFrom);
|
|
toCopy.forEach((function(obj) {
|
|
uoTo.copyFromOtherDrive(newResolved.path, obj.el, obj.data, obj.key);
|
|
}));
|
|
if (copy) {
|
|
return;
|
|
}
|
|
uoFrom.delete(paths, waitFor());
|
|
}
|
|
}));
|
|
}
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var _restore = function(Env, data, cb) {
|
|
var userObject = Env.user.userObject;
|
|
data = data || {};
|
|
userObject.restore(data.path, cb);
|
|
};
|
|
var _addFolder = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePath(Env, data.path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
resolved.userObject.addFolder(resolved.path, data.name, (function(obj) {
|
|
if (obj.newPath && resolved.id) {
|
|
var fPath = _getUserObjectPath(Env, resolved.userObject);
|
|
if (fPath) {
|
|
Array.prototype.unshift.apply(obj.newPath, fPath);
|
|
}
|
|
}
|
|
cb(obj);
|
|
}));
|
|
};
|
|
var _addSharedFolder = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePath(Env, data.path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
if (resolved.id) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
if (!Env.pinPads) {
|
|
return void cb({
|
|
error: "EAUTH"
|
|
});
|
|
}
|
|
var folderData = data.folderData || {};
|
|
var id;
|
|
nThen((function() {
|
|
if (data.folderData) {
|
|
return;
|
|
}
|
|
var hash = Hash.createRandomHash("drive", data.password);
|
|
var secret = Hash.getSecrets("drive", hash, data.password);
|
|
var hashes = Hash.getHashes(secret);
|
|
folderData = {
|
|
href: "/drive/#" + hashes.editHash,
|
|
roHref: "/drive/#" + hashes.viewHash,
|
|
channel: secret.channel,
|
|
lastTitle: data.name,
|
|
ctime: +new Date
|
|
};
|
|
if (data.password) {
|
|
folderData.password = data.password;
|
|
}
|
|
if (data.owned) {
|
|
folderData.owners = [ Env.edPublic ];
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
Env.Store.getPadMetadata(null, {
|
|
channel: folderData.channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && (obj.error || obj.rejected)) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: obj.error || "ERESTRICTED"
|
|
});
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
Env.pinPads([ folderData.channel ], waitFor());
|
|
})).nThen((function(waitFor) {
|
|
Env.user.userObject.pushSharedFolder(folderData, waitFor((function(err, folderId) {
|
|
if (err === "EEXISTS" && folderData.href && folderId) {
|
|
var parsed = Hash.parsePadUrl(folderData.href);
|
|
var secret = Hash.getSecrets("drive", parsed.hash, folderData.password);
|
|
SF.upgrade(secret.channel, secret);
|
|
Env.folders[folderId].userObject.setReadOnly(false, secret.keys.secondaryKey);
|
|
waitFor.abort();
|
|
return void cb(folderId);
|
|
}
|
|
if (err === "EEXISTS" && folderId) {
|
|
waitFor.abort();
|
|
return void cb(folderId);
|
|
}
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb(err);
|
|
}
|
|
id = folderId;
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
Env.user.userObject.add(id, resolved.path);
|
|
Env.loadSharedFolder(id, folderData, waitFor((function(rt) {
|
|
if (!rt) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EDELETED"
|
|
});
|
|
}
|
|
if (!rt.proxy.metadata) {
|
|
rt.proxy.metadata = {
|
|
title: data.name || Messages.fm_newFolder
|
|
};
|
|
}
|
|
if (data.folderData) {
|
|
Env.Store.getPadMetadata(null, {
|
|
channel: folderData.channel
|
|
}, (function(md) {
|
|
var fData = Env.user.proxy[UserObject.SHARED_FOLDERS][id];
|
|
if (md.owners) {
|
|
fData.owners = md.owners;
|
|
}
|
|
if (md.expire) {
|
|
fData.expire = +md.expire;
|
|
}
|
|
}));
|
|
}
|
|
})), !Boolean(data.folderData));
|
|
})).nThen((function() {
|
|
Env.onSync((function() {
|
|
cb(id);
|
|
}));
|
|
}));
|
|
};
|
|
var _addLink = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePath(Env, data.path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
var uo = resolved.userObject;
|
|
var now = +new Date;
|
|
uo.pushLink({
|
|
name: data.name,
|
|
href: data.href,
|
|
atime: now,
|
|
ctime: now
|
|
}, (function(e, id) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
uo.add(id, resolved.path);
|
|
Env.onSync(cb);
|
|
}));
|
|
};
|
|
var _restoreSharedFolder = function(Env, _data, cb) {
|
|
var fId = _data.id;
|
|
var newPassword = _data.password;
|
|
var temp = Util.find(Env, [ "user", "proxy", UserObject.SHARED_FOLDERS_TEMP ]);
|
|
var data = temp && temp[fId];
|
|
if (!data) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
if (!Env.Store) {
|
|
return void cb({
|
|
error: "ESTORE"
|
|
});
|
|
}
|
|
var href = Env.user.userObject.getHref ? Env.user.userObject.getHref(data) : data.href;
|
|
var isNew = false;
|
|
nThen((function(waitFor) {
|
|
Env.Store.isNewChannel(null, {
|
|
href,
|
|
password: newPassword
|
|
}, waitFor((function(obj) {
|
|
if (!obj || obj.error) {
|
|
isNew = false;
|
|
return;
|
|
}
|
|
isNew = obj.isNew;
|
|
})));
|
|
})).nThen((function() {
|
|
if (isNew) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
var newData = Util.clone(data);
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, newPassword);
|
|
newData.password = newPassword;
|
|
newData.channel = secret.channel;
|
|
if (secret.keys.editKeyStr) {
|
|
newData.href = "/drive/#" + Hash.getEditHashFromKeys(secret);
|
|
}
|
|
newData.roHref = "/drive/#" + Hash.getViewHashFromKeys(secret);
|
|
delete newData.legacy;
|
|
_addSharedFolder(Env, {
|
|
path: [ "root" ],
|
|
folderData: newData
|
|
}, (function() {
|
|
delete temp[fId];
|
|
Env.onSync(cb);
|
|
}));
|
|
}));
|
|
};
|
|
var _convertFolderToSharedFolder = function(Env, data, cb) {
|
|
var path = data.path;
|
|
var folderElement = Env.user.userObject.find(path);
|
|
if (path.length <= 1 || path[0] !== UserObject.ROOT) {
|
|
return void cb({
|
|
error: "E_INVAL_PATH"
|
|
});
|
|
}
|
|
if (_isInSharedFolder(Env, path)) {
|
|
return void cb({
|
|
error: "E_INVAL_NESTING"
|
|
});
|
|
}
|
|
if (Env.user.userObject.hasSubSharedFolder(folderElement)) {
|
|
return void cb({
|
|
error: "E_INVAL_NESTING"
|
|
});
|
|
}
|
|
var parentPath = path.slice(0, -1);
|
|
var parentFolder = Env.user.userObject.find(parentPath);
|
|
var folderName = path[path.length - 1];
|
|
var SFId;
|
|
nThen((function(waitFor) {
|
|
_addSharedFolder(Env, {
|
|
path: parentPath,
|
|
name: folderName,
|
|
owned: data.owned,
|
|
password: data.password || ""
|
|
}, waitFor((function(id) {
|
|
if (typeof id === "object" && id && id.error) {
|
|
waitFor.abort();
|
|
return void cb(id);
|
|
} else {
|
|
SFId = id;
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
if (!SFId) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "E_NO_ID"
|
|
});
|
|
}
|
|
var paths = [];
|
|
for (var el in folderElement) {
|
|
if (Env.user.userObject.isFolder(folderElement[el]) || Env.user.userObject.isFile(folderElement[el])) {
|
|
paths.push(path.concat(el));
|
|
}
|
|
}
|
|
var SFKey;
|
|
Object.keys(parentFolder).some((function(el) {
|
|
if (parentFolder[el] === SFId) {
|
|
SFKey = el;
|
|
return true;
|
|
}
|
|
}));
|
|
if (!SFKey) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "E_NO_KEY"
|
|
});
|
|
}
|
|
var newPath = parentPath.concat(SFKey).concat(UserObject.ROOT);
|
|
_move(Env, {
|
|
paths,
|
|
newPath,
|
|
copy: false
|
|
}, waitFor());
|
|
})).nThen((function(waitFor) {
|
|
var paths = [];
|
|
Object.keys(folderElement).forEach((function(el) {
|
|
if (!Env.user.userObject.isFile(folderElement[el])) {
|
|
return;
|
|
}
|
|
var data = Env.user.userObject.getFileData(folderElement[el]);
|
|
if (!data || !_ownedByMe(Env, data.owners)) {
|
|
return;
|
|
}
|
|
paths.push(path.concat(el));
|
|
}));
|
|
_move(Env, {
|
|
paths,
|
|
newPath: [ UserObject.ROOT ],
|
|
copy: false
|
|
}, waitFor());
|
|
})).nThen((function() {
|
|
var sharedFolderElement = Env.user.proxy[UserObject.SHARED_FOLDERS][SFId];
|
|
var metadata = Env.user.userObject.getFolderData(folderElement);
|
|
for (var key in metadata) {
|
|
if (key === "metadata") {
|
|
continue;
|
|
}
|
|
sharedFolderElement[key] = metadata[key];
|
|
}
|
|
Env.user.userObject.delete([ path ], (function() {
|
|
cb({
|
|
fId: SFId
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var _delete = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = data.resolved || _resolvePaths(Env, data.paths);
|
|
if (!resolved.main.length && !Object.keys(resolved.folders).length) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
if (data.paths && data.paths.length === 1 && data.paths[0][0] === UserObject.SHARED_FOLDERS_TEMP) {
|
|
var temp = Util.find(Env, [ "user", "proxy", UserObject.SHARED_FOLDERS_TEMP ]);
|
|
delete temp[data.paths[0][1]];
|
|
return void Env.onSync(cb);
|
|
}
|
|
var toUnpin = [];
|
|
nThen((function(waitFor) {
|
|
if (resolved.main.length) {
|
|
var uo = Env.user.userObject;
|
|
if (Util.find(Env.settings, [ "drive", "hideDuplicate" ])) {
|
|
resolved.main.forEach((function(p) {
|
|
var el = uo.find(p);
|
|
if (p[0] === UserObject.FILES_DATA) {
|
|
return;
|
|
}
|
|
if (uo.isFile(el) || uo.isSharedFolder(el)) {
|
|
return;
|
|
}
|
|
var arr = [];
|
|
uo.getFilesRecursively(el, arr);
|
|
arr.forEach((function(id) {
|
|
if (_isDuplicateOwned(Env, null, id)) {
|
|
Env.user.userObject.add(Number(id), [ UserObject.ROOT ]);
|
|
}
|
|
}));
|
|
}));
|
|
}
|
|
uo.delete(resolved.main, waitFor((function(err, _toUnpin) {
|
|
if (!Env.unpinPads || !_toUnpin) {
|
|
return;
|
|
}
|
|
Array.prototype.push.apply(toUnpin, _toUnpin);
|
|
})));
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
Object.keys(resolved.folders).forEach((function(id) {
|
|
Env.folders[id].userObject.delete(resolved.folders[id], waitFor((function(err, _toUnpin) {
|
|
if (!Env.unpinPads || !_toUnpin) {
|
|
return;
|
|
}
|
|
Array.prototype.push.apply(toUnpin, _toUnpin);
|
|
})));
|
|
}));
|
|
})).nThen((function(waitFor) {
|
|
if (!Env.unpinPads) {
|
|
return;
|
|
}
|
|
toUnpin = Util.deduplicateString(toUnpin);
|
|
var toKeep = [];
|
|
_findChannels(Env, toUnpin).forEach((function(id) {
|
|
var data = _getFileData(Env, id);
|
|
var arr = [ data.channel ];
|
|
if (data.answersChannel) {
|
|
arr.push(data.answersChannel);
|
|
}
|
|
if (data.rtChannel) {
|
|
arr.push(data.rtChannel);
|
|
}
|
|
if (data.lastVersion) {
|
|
arr.push(Hash.hrefToHexChannelId(data.lastVersion));
|
|
}
|
|
Array.prototype.push.apply(toKeep, arr);
|
|
}));
|
|
var unpinList = [];
|
|
toUnpin.forEach((function(chan) {
|
|
if (toKeep.indexOf(chan) === -1) {
|
|
unpinList.push(chan);
|
|
Env.Store.checkDeletedPad(chan);
|
|
}
|
|
}));
|
|
Env.unpinPads(unpinList, waitFor((function(response) {
|
|
if (response && response.error) {
|
|
return console.error(response.error);
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var _deleteOwned = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePaths(Env, data.paths || []);
|
|
if (!data.channel && !resolved.main.length && !Object.keys(resolved.folders).length) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
var toDelete = {
|
|
main: [],
|
|
folders: {}
|
|
};
|
|
var todo = function(channel, uo, p, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var chan = channel;
|
|
if (!chan && uo) {
|
|
var el = uo.find(p);
|
|
if (!uo.isFile(el) && !uo.isSharedFolder(el)) {
|
|
return;
|
|
}
|
|
var data = uo.isFile(el) ? uo.getFileData(el) : getSharedFolderData(Env, el);
|
|
chan = data.channel;
|
|
}
|
|
var fId;
|
|
Object.keys(Env.user.proxy[UserObject.SHARED_FOLDERS] || {}).some((function(id) {
|
|
var sfData = Env.user.proxy[UserObject.SHARED_FOLDERS][id] || {};
|
|
if (sfData.channel === chan) {
|
|
fId = Number(id);
|
|
Env.folders[id].deleting = true;
|
|
return true;
|
|
}
|
|
}));
|
|
Env.removeOwnedChannel(chan, (function(obj) {
|
|
if (obj && obj.error && obj.error !== "ENOENT") {
|
|
if (fId && Env.folders[fId] && Env.folders[fId].deleting) {
|
|
delete Env.folders[fId].deleting;
|
|
}
|
|
Feedback.send("ERROR_DELETING_OWNED_PAD=" + chan + "|" + obj.error, true);
|
|
return void cb();
|
|
}
|
|
var ids = _findChannels(Env, [ chan ]);
|
|
if (fId) {
|
|
ids.push(fId);
|
|
}
|
|
if (!ids.length) {
|
|
toDelete = undefined;
|
|
return void cb();
|
|
}
|
|
ids.forEach((function(id) {
|
|
var paths = findFile(Env, id);
|
|
var _resolved = _resolvePaths(Env, paths);
|
|
Array.prototype.push.apply(toDelete.main, _resolved.main);
|
|
Object.keys(_resolved.folders).forEach((function(fId) {
|
|
if (toDelete.folders[fId]) {
|
|
Array.prototype.push.apply(toDelete.folders[fId], _resolved.folders[fId]);
|
|
} else {
|
|
toDelete.folders[fId] = _resolved.folders[fId];
|
|
}
|
|
}));
|
|
}));
|
|
cb();
|
|
}));
|
|
};
|
|
nThen((function(w) {
|
|
if (data.channel) {
|
|
todo(data.channel, null, null, w());
|
|
}
|
|
resolved.main.forEach((function(p) {
|
|
todo(null, Env.user.userObject, p, w());
|
|
}));
|
|
Object.keys(resolved.folders).forEach((function(id) {
|
|
var uo = Env.folders[id].userObject;
|
|
resolved.folders[id].forEach((function(p) {
|
|
todo(null, uo, p, w());
|
|
}));
|
|
}));
|
|
})).nThen((function() {
|
|
if (!toDelete) {
|
|
cb();
|
|
} else {
|
|
_delete(Env, {
|
|
resolved: toDelete
|
|
}, cb);
|
|
}
|
|
if (data.channel) {
|
|
Env.Store.refreshDriveUI();
|
|
}
|
|
}));
|
|
};
|
|
var _emptyTrash = function(Env, data, cb) {
|
|
nThen((function(waitFor) {
|
|
if (data && data.deleteOwned) {
|
|
var owned = Env.user.userObject.ownedInTrash((function(owners) {
|
|
return _ownedByMe(Env, owners);
|
|
}));
|
|
var n = nThen;
|
|
owned.forEach((function(chan) {
|
|
n = n((function(w) {
|
|
Env.removeOwnedChannel(chan, w((function(obj) {
|
|
setTimeout(w(), 50);
|
|
if (obj && obj.error && obj.error !== "ENOENT") {
|
|
console.error(obj.error, chan);
|
|
Feedback.send("ERROR_EMPTYTRASH_OWNED=" + chan + "|" + obj.error, true);
|
|
}
|
|
console.warn("DELETED", chan);
|
|
})));
|
|
})).nThen;
|
|
}));
|
|
n(waitFor());
|
|
}
|
|
Env.user.userObject.emptyTrash(waitFor((function(err, toClean) {
|
|
var nn = nThen;
|
|
setTimeout(waitFor((function() {
|
|
if (!Array.isArray(toClean)) {
|
|
return;
|
|
}
|
|
var done = waitFor();
|
|
var toCheck = Util.deduplicateString(toClean);
|
|
var toUnpin = [];
|
|
toCheck.forEach((function(channel) {
|
|
nn = nn((function(w) {
|
|
var data = findChannel(Env, channel, true);
|
|
if (!data.length) {
|
|
toUnpin.push(channel);
|
|
}
|
|
Env.Store.checkDeletedPad(channel, w());
|
|
})).nThen;
|
|
}));
|
|
nn((function() {
|
|
Env.unpinPads(toUnpin, (function() {
|
|
done();
|
|
}));
|
|
}));
|
|
})));
|
|
})));
|
|
})).nThen(cb);
|
|
};
|
|
var _rename = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePath(Env, data.path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
if (!resolved.id) {
|
|
var el = Env.user.userObject.find(resolved.path);
|
|
if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) {
|
|
Env.folders[el].proxy.metadata.title = data.newName || Messages.fm_folder;
|
|
Env.user.proxy[UserObject.SHARED_FOLDERS][el].lastTitle = data.newName || Messages.fm_folder;
|
|
return void cb();
|
|
}
|
|
}
|
|
resolved.userObject.rename(resolved.path, data.newName, cb);
|
|
};
|
|
var _setFolderData = function(Env, data, cb) {
|
|
data = data || {};
|
|
var resolved = _resolvePath(Env, data.path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return void cb({
|
|
error: "E_NOTFOUND"
|
|
});
|
|
}
|
|
if (!resolved.id) {
|
|
var el = Env.user.userObject.find(resolved.path);
|
|
if (Env.user.userObject.isSharedFolder(el) && Env.folders[el]) {
|
|
Env.user.proxy[UserObject.SHARED_FOLDERS][el][data.key] = data.value;
|
|
return void Env.onSync(cb);
|
|
}
|
|
}
|
|
resolved.userObject.setFolderData(resolved.path, data.key, data.value, (function() {
|
|
Env.onSync(cb);
|
|
}));
|
|
};
|
|
var _updateStaticAccess = function(Env, id, cb) {
|
|
var uo = _getUserObjectFromId(Env, id);
|
|
var sd = uo.getFileData(id, true);
|
|
sd.atime = +new Date;
|
|
Env.onSync(cb);
|
|
};
|
|
var COMMANDS = {
|
|
move: _move,
|
|
restore: _restore,
|
|
addFolder: _addFolder,
|
|
addSharedFolder: _addSharedFolder,
|
|
addLink: _addLink,
|
|
restoreSharedFolder: _restoreSharedFolder,
|
|
convertFolderToSharedFolder: _convertFolderToSharedFolder,
|
|
delete: _delete,
|
|
deleteOwned: _deleteOwned,
|
|
emptyTrash: _emptyTrash,
|
|
rename: _rename,
|
|
setFolderData: _setFolderData,
|
|
updateStaticAccess: _updateStaticAccess
|
|
};
|
|
var onCommand = function(Env, cmdData, cb) {
|
|
var cmd = cmdData.cmd;
|
|
var data = cmdData.data || {};
|
|
var method = COMMANDS[cmd];
|
|
if (typeof method === "function") {
|
|
return void method(Env, data, cb);
|
|
}
|
|
cb();
|
|
};
|
|
var setPadAttribute = function(Env, data, cb) {
|
|
cb = cb || function() {};
|
|
if (!data.attr || !data.attr.trim()) {
|
|
return void cb("E_INVAL_ATTR");
|
|
}
|
|
var sfId = Env.user.userObject.getSFIdFromHref(data.href);
|
|
if (sfId) {
|
|
if (data.attr === "href") {
|
|
data.value = Env.user.userObject.cryptor.encrypt(data.value);
|
|
}
|
|
Env.user.proxy[UserObject.SHARED_FOLDERS][sfId][data.attr] = data.value;
|
|
}
|
|
var datas = findHref(Env, data.href);
|
|
var nt = nThen;
|
|
datas.forEach((function(d) {
|
|
nt = nt((function(waitFor) {
|
|
d.userObject.setPadAttribute(data.href, data.attr, data.value, waitFor());
|
|
})).nThen;
|
|
}));
|
|
nt((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var getPadAttribute = function(Env, data, cb) {
|
|
cb = cb || function() {};
|
|
var sfId = Env.user.userObject.getSFIdFromHref(data.href);
|
|
if (sfId) {
|
|
var sfData = getSharedFolderData(Env, sfId);
|
|
var sfValue = data.attr ? sfData[data.attr] : JSON.parse(JSON.stringify(sfData));
|
|
setTimeout((function() {
|
|
cb(null, {
|
|
value: sfValue,
|
|
atime: 1
|
|
});
|
|
}));
|
|
return;
|
|
}
|
|
var datas = findHref(Env, data.href);
|
|
var res = {};
|
|
datas.forEach((function(d) {
|
|
var atime = d.data.atime;
|
|
var value = data.attr ? d.data[data.attr] : JSON.parse(JSON.stringify(d.data));
|
|
if (!res.value || res.atime < atime) {
|
|
res.atime = atime;
|
|
res.value = value;
|
|
}
|
|
}));
|
|
setTimeout((function() {
|
|
cb(null, res);
|
|
}));
|
|
};
|
|
var getTagsList = function(Env) {
|
|
var list = {};
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
var l = uo.getTagsList();
|
|
Object.keys(l).forEach((function(t) {
|
|
list[t] = list[t] ? list[t] + l[t] : l[t];
|
|
}));
|
|
}));
|
|
return list;
|
|
};
|
|
var getSecureFilesList = function(Env, where) {
|
|
var userObjects = _getUserObjects(Env);
|
|
var list = [];
|
|
var channels = [];
|
|
userObjects.forEach((function(uo) {
|
|
var toPush = uo.getFiles(where).map((function(id) {
|
|
return {
|
|
id,
|
|
data: uo.getFileData(id)
|
|
};
|
|
})).filter((function(d) {
|
|
if (channels.indexOf(d.data.channel || d.id) === -1) {
|
|
channels.push(d.data.channel || d.id);
|
|
return true;
|
|
}
|
|
}));
|
|
Array.prototype.push.apply(list, toPush);
|
|
}));
|
|
return list;
|
|
};
|
|
var excludeInvalidIdentifiers = function(result) {
|
|
return result.filter((function(channel) {
|
|
if (typeof channel !== "string") {
|
|
return;
|
|
}
|
|
return [ 32, 48 ].indexOf(channel.length) !== -1;
|
|
}));
|
|
};
|
|
var getChannelsList = function(Env, type) {
|
|
var result = [];
|
|
var addChannel = function(userObject) {
|
|
if (type === "expirable") {
|
|
return function(fileId) {
|
|
var data = userObject.getFileData(fileId);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (result.indexOf(data.channel) !== -1) {
|
|
return;
|
|
}
|
|
if (_ownedByOther(Env, data.owners) || data.expire && data.expire < +new Date) {
|
|
result.push(data.channel);
|
|
}
|
|
};
|
|
}
|
|
if (type === "owned") {
|
|
return function(fileId) {
|
|
var data = userObject.getFileData(fileId);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (result.indexOf(data.channel) !== -1) {
|
|
return;
|
|
}
|
|
if (_ownedByMe(Env, data.owners)) {
|
|
result.push(data.channel);
|
|
}
|
|
};
|
|
}
|
|
if (type === "pin") {
|
|
return function(fileId) {
|
|
var data = userObject.getFileData(fileId);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
if (data.lastVersion) {
|
|
var otherChan = Hash.hrefToHexChannelId(data.lastVersion);
|
|
if (result.indexOf(otherChan) === -1) {
|
|
result.push(otherChan);
|
|
}
|
|
}
|
|
if (data.answersChannel && result.indexOf(data.answersChannel) === -1) {
|
|
result.push(data.answersChannel);
|
|
}
|
|
if (data.rtChannel && result.indexOf(data.rtChannel) === -1) {
|
|
result.push(data.rtChannel);
|
|
}
|
|
if (data.ooImages && Array.isArray(data.ooImages)) {
|
|
Array.prototype.push.apply(result, data.ooImages);
|
|
}
|
|
if (result.indexOf(data.channel) === -1) {
|
|
result.push(data.channel);
|
|
}
|
|
};
|
|
}
|
|
};
|
|
if (type === "owned" && !Env.edPublic) {
|
|
return excludeInvalidIdentifiers(result);
|
|
}
|
|
if (type === "pin" && !Env.edPublic) {
|
|
return excludeInvalidIdentifiers(result);
|
|
}
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
var files = uo.getFiles([ UserObject.FILES_DATA ]);
|
|
files.forEach(addChannel(uo));
|
|
}));
|
|
if (type === "owned") {
|
|
var sfOwned = Object.keys(Env.user.proxy[UserObject.SHARED_FOLDERS]).filter((function(fId) {
|
|
var owners = Env.user.proxy[UserObject.SHARED_FOLDERS][fId].owners;
|
|
if (_ownedByMe(Env, owners)) {
|
|
return true;
|
|
}
|
|
})).map((function(fId) {
|
|
return Env.user.proxy[UserObject.SHARED_FOLDERS][fId].channel;
|
|
}));
|
|
Array.prototype.push.apply(result, sfOwned);
|
|
}
|
|
if (type === "pin") {
|
|
var sfChannels = Object.keys(Env.folders).map((function(fId) {
|
|
try {
|
|
return Env.user.proxy[UserObject.SHARED_FOLDERS][fId].channel;
|
|
} catch (err) {
|
|
console.error(err);
|
|
}
|
|
})).filter(Boolean);
|
|
Array.prototype.push.apply(result, sfChannels);
|
|
}
|
|
return excludeInvalidIdentifiers(result);
|
|
};
|
|
var addPad = function(Env, path, pad, cb) {
|
|
var uo = Env.user.userObject;
|
|
var p = [ "root" ];
|
|
if (path) {
|
|
var resolved = _resolvePath(Env, path);
|
|
uo = resolved.userObject;
|
|
p = resolved.path;
|
|
}
|
|
var todo = function() {
|
|
var error;
|
|
nThen((function(waitFor) {
|
|
uo.pushData(pad, waitFor((function(e, id) {
|
|
if (e) {
|
|
error = e;
|
|
return;
|
|
}
|
|
uo.add(id, p);
|
|
})));
|
|
})).nThen((function() {
|
|
cb(error);
|
|
}));
|
|
};
|
|
if (!Env.pinPads) {
|
|
return void todo();
|
|
}
|
|
Env.pinPads([ pad.channel ], (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb(obj.error);
|
|
}
|
|
todo();
|
|
}));
|
|
};
|
|
var create = function(proxy, data, uoConfig) {
|
|
var Env = {
|
|
pinPads: data.pin,
|
|
unpinPads: data.unpin,
|
|
onSync: data.onSync,
|
|
Store: data.Store,
|
|
store: data.store,
|
|
removeOwnedChannel: data.removeOwnedChannel,
|
|
loadSharedFolder: data.loadSharedFolder,
|
|
cfg: uoConfig,
|
|
edPublic: data.edPublic,
|
|
settings: data.settings,
|
|
user: {
|
|
proxy
|
|
},
|
|
folders: {}
|
|
};
|
|
uoConfig.removeProxy = function(id) {
|
|
removeProxy(Env, id);
|
|
};
|
|
Env.user.userObject = UserObject.init(proxy, uoConfig);
|
|
var callWithEnv = function(f) {
|
|
return function() {
|
|
[].unshift.call(arguments, Env);
|
|
return f.apply(null, arguments);
|
|
};
|
|
};
|
|
var addPin = function(pin, unpin) {
|
|
Env.pinPads = pin;
|
|
Env.unpinPads = unpin;
|
|
};
|
|
var removePin = function() {
|
|
delete Env.pinPads;
|
|
delete Env.unpinPads;
|
|
};
|
|
return {
|
|
addProxy: callWithEnv(addProxy),
|
|
removeProxy: callWithEnv(removeProxy),
|
|
deprecateProxy: callWithEnv(deprecateProxy),
|
|
restrictedProxy: callWithEnv(restrictedProxy),
|
|
addSharedFolder: callWithEnv(_addSharedFolder),
|
|
addPin,
|
|
removePin,
|
|
command: callWithEnv(onCommand),
|
|
getPadAttribute: callWithEnv(getPadAttribute),
|
|
setPadAttribute: callWithEnv(setPadAttribute),
|
|
getTagsList: callWithEnv(getTagsList),
|
|
getSecureFilesList: callWithEnv(getSecureFilesList),
|
|
getSharedFolderData: callWithEnv(getSharedFolderData),
|
|
getChannelsList: callWithEnv(getChannelsList),
|
|
addPad: callWithEnv(addPad),
|
|
delete: callWithEnv(_delete),
|
|
deleteOwned: callWithEnv(_deleteOwned),
|
|
findChannel: callWithEnv(findChannel),
|
|
findHref: callWithEnv(findHref),
|
|
findFile: callWithEnv(findFile),
|
|
getEditHash: callWithEnv(getEditHash),
|
|
user: Env.user,
|
|
folders: Env.folders
|
|
};
|
|
};
|
|
var renameInner = function(Env, path, newName, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "rename",
|
|
data: {
|
|
path,
|
|
newName
|
|
}
|
|
}, cb);
|
|
};
|
|
var moveInner = function(Env, paths, newPath, cb, copy) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "move",
|
|
data: {
|
|
paths,
|
|
newPath,
|
|
copy
|
|
}
|
|
}, cb);
|
|
};
|
|
var emptyTrashInner = function(Env, deleteOwned, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "emptyTrash",
|
|
data: {
|
|
deleteOwned
|
|
}
|
|
}, cb);
|
|
};
|
|
var addFolderInner = function(Env, path, name, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "addFolder",
|
|
data: {
|
|
path,
|
|
name
|
|
}
|
|
}, cb);
|
|
};
|
|
var addSharedFolderInner = function(Env, path, data, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "addSharedFolder",
|
|
data: {
|
|
path,
|
|
name: data.name,
|
|
owned: data.owned,
|
|
password: data.password
|
|
}
|
|
}, cb);
|
|
};
|
|
var addLinkInner = function(Env, path, data, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "addLink",
|
|
data: {
|
|
path,
|
|
name: data.name,
|
|
href: data.url
|
|
}
|
|
}, cb);
|
|
};
|
|
var restoreSharedFolderInner = function(Env, fId, password, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "restoreSharedFolder",
|
|
data: {
|
|
id: fId,
|
|
password
|
|
}
|
|
}, cb);
|
|
};
|
|
var convertFolderToSharedFolderInner = function(Env, path, owned, password, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "convertFolderToSharedFolder",
|
|
data: {
|
|
path,
|
|
owned,
|
|
password
|
|
}
|
|
}, cb);
|
|
};
|
|
var deleteInner = function(Env, paths, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "delete",
|
|
data: {
|
|
paths
|
|
}
|
|
}, cb);
|
|
};
|
|
var deleteOwnedInner = function(Env, paths, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "deleteOwned",
|
|
data: {
|
|
paths
|
|
}
|
|
}, cb);
|
|
};
|
|
var restoreInner = function(Env, path, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "restore",
|
|
data: {
|
|
path
|
|
}
|
|
}, cb);
|
|
};
|
|
var setFolderDataInner = function(Env, data, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "setFolderData",
|
|
data
|
|
}, cb);
|
|
};
|
|
var updateStaticAccessInner = function(Env, id, cb) {
|
|
return void Env.sframeChan.query("Q_DRIVE_USEROBJECT", {
|
|
cmd: "updateStaticAccess",
|
|
data: id
|
|
}, cb);
|
|
};
|
|
var findChannels = _findChannels;
|
|
var getFileData = _getFileData;
|
|
var getUserObjectPath = _getUserObjectPath;
|
|
var find = function(Env, path, fId) {
|
|
if (fId) {
|
|
return Env.folders[fId].userObject.find(path);
|
|
}
|
|
var resolved = _resolvePath(Env, path);
|
|
return resolved.userObject.find(resolved.path);
|
|
};
|
|
var getTitle = function(Env, id, type) {
|
|
var uo = _getUserObjectFromId(Env, id);
|
|
return String(uo.getTitle(id, type));
|
|
};
|
|
var isStaticFile = function(Env, id) {
|
|
var uo = _getUserObjectFromId(Env, id);
|
|
return uo.isStaticFile(id);
|
|
};
|
|
var isReadOnlyFile = function(Env, id) {
|
|
var uo = _getUserObjectFromId(Env, id);
|
|
return uo.isReadOnlyFile(id);
|
|
};
|
|
var getFiles = function(Env, categories) {
|
|
var files = [];
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
Array.prototype.push.apply(files, uo.getFiles(categories));
|
|
}));
|
|
files = Util.deduplicateString(files);
|
|
return files;
|
|
};
|
|
var search = function(Env, value) {
|
|
var ret = [];
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
var fPath = _getUserObjectPath(Env, uo);
|
|
var results = uo.search(value);
|
|
if (!results.length) {
|
|
return;
|
|
}
|
|
if (fPath) {
|
|
results.forEach((function(r) {
|
|
r.inSharedFolder = true;
|
|
r.paths.forEach((function(p) {
|
|
Array.prototype.unshift.apply(p, fPath);
|
|
}));
|
|
}));
|
|
}
|
|
Array.prototype.push.apply(ret, results);
|
|
}));
|
|
return ret;
|
|
};
|
|
var getRecentPads = function(Env) {
|
|
var files = [];
|
|
var userObjects = _getUserObjects(Env);
|
|
userObjects.forEach((function(uo) {
|
|
var data = uo.getFiles([ UserObject.FILES_DATA, UserObject.STATIC_DATA ]).map((function(id) {
|
|
return [ Number(id), uo.getFileData(id) ];
|
|
}));
|
|
Array.prototype.push.apply(files, data);
|
|
}));
|
|
var sorted = files.filter((function(a) {
|
|
return a[1].atime;
|
|
})).sort((function(a, b) {
|
|
return b[1].atime - a[1].atime;
|
|
}));
|
|
return sorted;
|
|
};
|
|
var getOwnedPads = function(Env) {
|
|
return Env.user.userObject.getOwnedPads(Env.edPublic);
|
|
};
|
|
var setHistoryMode = function(Env, flag) {
|
|
Env.isHistoryMode = Boolean(flag);
|
|
};
|
|
var getFolderData = function(Env, path) {
|
|
var resolved = _resolvePath(Env, path);
|
|
if (!resolved || !resolved.userObject) {
|
|
return {};
|
|
}
|
|
if (!resolved.id) {
|
|
var el = Env.user.userObject.find(resolved.path);
|
|
if (Env.user.userObject.isSharedFolder(el)) {
|
|
return getSharedFolderData(Env, el);
|
|
}
|
|
}
|
|
var folder = resolved.userObject.find(resolved.path);
|
|
return resolved.userObject.getFolderData(folder);
|
|
};
|
|
var isInSharedFolder = _isInSharedFolder;
|
|
var isValidDrive = function(Env, obj) {
|
|
return Env.user.userObject.isValidDrive(obj);
|
|
};
|
|
var isFile = function(Env, el, allowStr) {
|
|
return Env.user.userObject.isFile(el, allowStr);
|
|
};
|
|
var isFolder = function(Env, el) {
|
|
return Env.user.userObject.isFolder(el);
|
|
};
|
|
var isSharedFolder = function(Env, el) {
|
|
return Env.user.userObject.isSharedFolder(el);
|
|
};
|
|
var isFolderEmpty = function(Env, el) {
|
|
if (Env.folders[el]) {
|
|
var uo = Env.folders[el].userObject;
|
|
return uo.isFolderEmpty(uo.find[uo.ROOT]);
|
|
}
|
|
return Env.user.userObject.isFolderEmpty(el);
|
|
};
|
|
var isPathIn = function(Env, path, categories) {
|
|
return Env.user.userObject.isPathIn(path, categories);
|
|
};
|
|
var isSubpath = function(Env, path, parentPath) {
|
|
return Env.user.userObject.isSubpath(path, parentPath);
|
|
};
|
|
var isInTrashRoot = function(Env, path) {
|
|
return Env.user.userObject.isInTrashRoot(path);
|
|
};
|
|
var comparePath = function(Env, a, b) {
|
|
return Env.user.userObject.comparePath(a, b);
|
|
};
|
|
var hasSubfolder = function(Env, el, trashRoot) {
|
|
if (Env.folders[el]) {
|
|
var uo = Env.folders[el].userObject;
|
|
return uo.hasSubfolder(uo.find[uo.ROOT]);
|
|
}
|
|
return Env.user.userObject.hasSubfolder(el, trashRoot);
|
|
};
|
|
var hasSubSharedFolder = function(Env, el) {
|
|
return Env.user.userObject.hasSubSharedFolder(el);
|
|
};
|
|
var hasFile = function(Env, el, trashRoot) {
|
|
if (Env.folders[el]) {
|
|
var uo = Env.folders[el].userObject;
|
|
return uo.hasFile(uo.find[uo.ROOT]);
|
|
}
|
|
return Env.user.userObject.hasFile(el, trashRoot);
|
|
};
|
|
var ownedInTrash = function(Env) {
|
|
return Env.user.userObject.ownedInTrash((function(owners) {
|
|
return _ownedByMe(Env, owners);
|
|
}));
|
|
};
|
|
var isDuplicateOwned = _isDuplicateOwned;
|
|
var createInner = function(proxy, sframeChan, edPublic, uoConfig) {
|
|
var Env = {
|
|
cfg: uoConfig,
|
|
sframeChan,
|
|
edPublic,
|
|
user: {
|
|
proxy,
|
|
userObject: UserObject.init(proxy, uoConfig)
|
|
},
|
|
folders: {}
|
|
};
|
|
var callWithEnv = function(f) {
|
|
return function() {
|
|
[].unshift.call(arguments, Env);
|
|
return f.apply(null, arguments);
|
|
};
|
|
};
|
|
return {
|
|
addProxy: callWithEnv(addProxy),
|
|
removeProxy: callWithEnv(removeProxy),
|
|
setHistoryMode: callWithEnv(setHistoryMode),
|
|
rename: callWithEnv(renameInner),
|
|
move: callWithEnv(moveInner),
|
|
emptyTrash: callWithEnv(emptyTrashInner),
|
|
addFolder: callWithEnv(addFolderInner),
|
|
addSharedFolder: callWithEnv(addSharedFolderInner),
|
|
addLink: callWithEnv(addLinkInner),
|
|
restoreSharedFolder: callWithEnv(restoreSharedFolderInner),
|
|
convertFolderToSharedFolder: callWithEnv(convertFolderToSharedFolderInner),
|
|
delete: callWithEnv(deleteInner),
|
|
deleteOwned: callWithEnv(deleteOwnedInner),
|
|
restore: callWithEnv(restoreInner),
|
|
setFolderData: callWithEnv(setFolderDataInner),
|
|
updateStaticAccess: callWithEnv(updateStaticAccessInner),
|
|
getFileData: callWithEnv(getFileData),
|
|
find: callWithEnv(find),
|
|
getTitle: callWithEnv(getTitle),
|
|
isReadOnlyFile: callWithEnv(isReadOnlyFile),
|
|
isStaticFile: callWithEnv(isStaticFile),
|
|
getFiles: callWithEnv(getFiles),
|
|
search: callWithEnv(search),
|
|
getRecentPads: callWithEnv(getRecentPads),
|
|
getOwnedPads: callWithEnv(getOwnedPads),
|
|
getTagsList: callWithEnv(getTagsList),
|
|
findFile: callWithEnv(findFile),
|
|
findFiles: callWithEnv(findFiles),
|
|
findChannels: callWithEnv(findChannels),
|
|
getSharedFolderData: callWithEnv(getSharedFolderData),
|
|
getFolderData: callWithEnv(getFolderData),
|
|
isInSharedFolder: callWithEnv(isInSharedFolder),
|
|
getUserObjectPath: callWithEnv(getUserObjectPath),
|
|
isDuplicateOwned: callWithEnv(isDuplicateOwned),
|
|
ownedInTrash: callWithEnv(ownedInTrash),
|
|
isValidDrive: callWithEnv(isValidDrive),
|
|
isFile: callWithEnv(isFile),
|
|
isFolder: callWithEnv(isFolder),
|
|
isSharedFolder: callWithEnv(isSharedFolder),
|
|
isFolderEmpty: callWithEnv(isFolderEmpty),
|
|
isPathIn: callWithEnv(isPathIn),
|
|
isSubpath: callWithEnv(isSubpath),
|
|
isInTrashRoot: callWithEnv(isInTrashRoot),
|
|
comparePath: callWithEnv(comparePath),
|
|
hasSubfolder: callWithEnv(hasSubfolder),
|
|
hasSubSharedFolder: callWithEnv(hasSubSharedFolder),
|
|
hasFile: callWithEnv(hasFile),
|
|
user: Env.user,
|
|
folders: Env.folders
|
|
};
|
|
};
|
|
return {
|
|
setCustomize,
|
|
create,
|
|
createInner
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireUserObject(), requireCommonUtil(), requireCommonHash(), requireSharedfolder(), undefined, requireCommonFeedback(), requireNthen());
|
|
}
|
|
})();
|
|
})(proxyManager$1);
|
|
return proxyManager$1.exports;
|
|
}
|
|
var proxyManagerExports = requireProxyManager();
|
|
var proxyManager = getDefaultExportFromCjs(proxyManagerExports);
|
|
var ProxyManager = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: proxyManager
|
|
}, [ proxyManagerExports ]);
|
|
var userObjectExports = requireUserObject();
|
|
var userObject = getDefaultExportFromCjs(userObjectExports);
|
|
var UO = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: userObject
|
|
}, [ userObjectExports ]);
|
|
var padTypes$1 = {
|
|
exports: {}
|
|
};
|
|
var currentVersion = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCurrentVersion;
|
|
function requireCurrentVersion() {
|
|
if (hasRequiredCurrentVersion) return currentVersion.exports;
|
|
hasRequiredCurrentVersion = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = () => ({
|
|
currentVersion: "v7"
|
|
});
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
}
|
|
})();
|
|
})(currentVersion);
|
|
return currentVersion.exports;
|
|
}
|
|
var hasRequiredPadTypes;
|
|
function requirePadTypes() {
|
|
if (hasRequiredPadTypes) return padTypes$1.exports;
|
|
hasRequiredPadTypes = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (AppConfig = {}, ApiConfig = {}, OOCurrentVersion) => {
|
|
let availablePadTypes = [];
|
|
const OO_APPS = [ "sheet", "doc", "presentation" ];
|
|
const setCustomize = data => {
|
|
AppConfig = data.AppConfig;
|
|
ApiConfig = data.ApiConfig;
|
|
const ooEnabled = ApiConfig.onlyOffice && ApiConfig.onlyOffice.availableVersions.includes(OOCurrentVersion.currentVersion);
|
|
availablePadTypes = AppConfig.availablePadTypes.filter((t => ooEnabled || !OO_APPS.includes(t)));
|
|
};
|
|
if (Object.keys(AppConfig).length) {
|
|
setCustomize({
|
|
AppConfig,
|
|
ApiConfig
|
|
});
|
|
}
|
|
const Types = {
|
|
setCustomize
|
|
};
|
|
Types.__defineGetter__("availableTypes", (function() {
|
|
if (ApiConfig.appsToDisable) {
|
|
return availablePadTypes.filter((value => !ApiConfig.appsToDisable.includes(value)));
|
|
}
|
|
return availablePadTypes;
|
|
}));
|
|
Types.__defineGetter__("appsToSelect", (function() {
|
|
return availablePadTypes.filter((value => ![ "drive", "teams", "file", "contacts", "convert" ].includes(value)));
|
|
}));
|
|
Types.isAvailable = type => Array.isArray(Types.availableTypes) && Types.availableTypes.includes(type);
|
|
return Types;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, undefined, requireCurrentVersion());
|
|
}
|
|
})();
|
|
})(padTypes$1);
|
|
return padTypes$1.exports;
|
|
}
|
|
var padTypesExports = requirePadTypes();
|
|
var padTypes = getDefaultExportFromCjs(padTypesExports);
|
|
var PadTypes = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: padTypes
|
|
}, [ padTypesExports ]);
|
|
var loginBlock$1 = {
|
|
exports: {}
|
|
};
|
|
var httpCommand = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredHttpCommand;
|
|
function requireHttpCommand() {
|
|
if (hasRequiredHttpCommand) return httpCommand.exports;
|
|
hasRequiredHttpCommand = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (nThen, Util, ApiConfig = {}, Nacl) => {
|
|
const getApiOrigin = function() {
|
|
if (!Object.keys(ApiConfig).length) {
|
|
return;
|
|
}
|
|
var url;
|
|
var unsafeOriginURL = new URL(ApiConfig.httpUnsafeOrigin);
|
|
try {
|
|
url = new URL(ApiConfig.websocketPath, ApiConfig.httpUnsafeOrigin);
|
|
url.protocol = unsafeOriginURL.protocol;
|
|
return url.origin;
|
|
} catch (err) {
|
|
console.error(err);
|
|
return ApiConfig.httpUnsafeOrigin;
|
|
}
|
|
};
|
|
var API_ORIGIN = getApiOrigin();
|
|
const setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
API_ORIGIN = getApiOrigin();
|
|
};
|
|
var clone = o => JSON.parse(JSON.stringify(o));
|
|
var randomToken = () => Nacl.util.encodeBase64(Nacl.randomBytes(24));
|
|
var postData = function(url, data, cb) {
|
|
var CB = Util.once(Util.mkAsync(cb));
|
|
fetch(url, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json"
|
|
},
|
|
body: JSON.stringify(data)
|
|
}).then((response => {
|
|
if (response.ok) {
|
|
return void response.text().then((result => {
|
|
CB(void 0, Util.tryParse(result));
|
|
}));
|
|
}
|
|
response.json().then().then((result => {
|
|
CB(response.status, result);
|
|
}));
|
|
})).catch((error => {
|
|
CB(error);
|
|
}));
|
|
};
|
|
var serverCommand = function(keypair, my_data, cb) {
|
|
var obj = clone(my_data);
|
|
obj.publicKey = Nacl.util.encodeBase64(keypair.publicKey);
|
|
obj.nonce = randomToken();
|
|
var href = new URL("/api/auth/", API_ORIGIN);
|
|
var txid, date;
|
|
nThen((function(w) {
|
|
postData(href, obj, w(((err, data) => {
|
|
if (err) {
|
|
w.abort();
|
|
console.error(err);
|
|
if (data) {
|
|
console.error(data);
|
|
}
|
|
return void cb(err);
|
|
}
|
|
if (!data.date || !data.txid) {
|
|
w.abort();
|
|
return void cb("REQUEST_REJECTED");
|
|
}
|
|
txid = data.txid;
|
|
date = data.date;
|
|
})));
|
|
})).nThen((function(w) {
|
|
var copy = clone(obj);
|
|
copy.txid = txid;
|
|
copy.date = date;
|
|
var toSign = Nacl.util.decodeUTF8(JSON.stringify(copy));
|
|
var sig = Nacl.sign.detached(toSign, keypair.secretKey);
|
|
var encoded = Nacl.util.encodeBase64(sig);
|
|
var obj2 = {
|
|
sig: encoded,
|
|
txid
|
|
};
|
|
postData(href, obj2, w(((err, data) => {
|
|
if (err) {
|
|
w.abort();
|
|
console.error(err);
|
|
if (data) {
|
|
console.error(data);
|
|
}
|
|
return void cb("RESPONSE_REJECTED", data);
|
|
}
|
|
cb(void 0, data);
|
|
})));
|
|
}));
|
|
};
|
|
serverCommand.setCustomize = setCustomize;
|
|
return serverCommand;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireNthen(), requireCommonUtil(), undefined, requireNaclFast());
|
|
}
|
|
})();
|
|
})(httpCommand);
|
|
return httpCommand.exports;
|
|
}
|
|
var hasRequiredLoginBlock;
|
|
function requireLoginBlock() {
|
|
if (hasRequiredLoginBlock) return loginBlock$1.exports;
|
|
hasRequiredLoginBlock = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, ApiConfig = {}, ServerCommand, Nacl) => {
|
|
var Block = {};
|
|
Block.setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
ServerCommand.setCustomize(data);
|
|
};
|
|
Block.join = Util.uint8ArrayJoin;
|
|
Block.seed = function() {
|
|
return Nacl.hash(Nacl.util.decodeUTF8("pewpewpew"));
|
|
};
|
|
Block.genkeys = function(seed) {
|
|
if (!(seed instanceof Uint8Array)) {
|
|
throw new Error("INVALID_SEED_FORMAT");
|
|
}
|
|
if (!seed || typeof seed.length !== "number" || seed.length < 64) {
|
|
throw new Error("INVALID_SEED_LENGTH");
|
|
}
|
|
var signSeed = seed.subarray(0, Nacl.sign.seedLength);
|
|
var symmetric = seed.subarray(Nacl.sign.seedLength, Nacl.sign.seedLength + Nacl.secretbox.keyLength);
|
|
return {
|
|
sign: Nacl.sign.keyPair.fromSeed(signSeed),
|
|
symmetric
|
|
};
|
|
};
|
|
Block.keysToRPCFormat = function(keys) {
|
|
try {
|
|
var sign = keys.sign;
|
|
return {
|
|
edPrivate: Nacl.util.encodeBase64(sign.secretKey),
|
|
edPublic: Nacl.util.encodeBase64(sign.publicKey)
|
|
};
|
|
} catch (err) {
|
|
console.error(err);
|
|
return;
|
|
}
|
|
};
|
|
Block.encrypt = function(version, content, keys) {
|
|
var u8 = Nacl.util.decodeUTF8(content);
|
|
var nonce = Nacl.randomBytes(Nacl.secretbox.nonceLength);
|
|
return Block.join([ [ 0 ], nonce, Nacl.secretbox(u8, nonce, keys.symmetric) ]);
|
|
};
|
|
Block.decrypt = function(u8_content, keys) {
|
|
var nonce = u8_content.subarray(1, 1 + Nacl.secretbox.nonceLength);
|
|
var box = u8_content.subarray(1 + Nacl.secretbox.nonceLength);
|
|
var plaintext = Nacl.secretbox.open(box, nonce, keys.symmetric);
|
|
try {
|
|
return JSON.parse(Nacl.util.encodeUTF8(plaintext));
|
|
} catch (e) {
|
|
console.error(e);
|
|
return;
|
|
}
|
|
};
|
|
Block.sign = function(ciphertext, keys) {
|
|
return Nacl.sign.detached(Nacl.hash(ciphertext), keys.sign.secretKey);
|
|
};
|
|
Block.serialize = function(content, keys) {
|
|
var ciphertext = Block.encrypt(0, content, keys);
|
|
var sig = Block.sign(ciphertext, keys);
|
|
return {
|
|
publicKey: Nacl.util.encodeBase64(keys.sign.publicKey),
|
|
signature: Nacl.util.encodeBase64(sig),
|
|
ciphertext: Nacl.util.encodeBase64(ciphertext)
|
|
};
|
|
};
|
|
Block.proveAncestor = function(O) {
|
|
var u8_pub = Util.find(O, [ "sign", "publicKey" ]);
|
|
var u8_secret = Util.find(O, [ "sign", "secretKey" ]);
|
|
try {
|
|
var u8_sig = Nacl.sign.detached(u8_pub, u8_secret);
|
|
return JSON.stringify([ u8_pub, u8_sig ].map(Nacl.util.encodeBase64));
|
|
} catch (err) {
|
|
return void console.error(err);
|
|
}
|
|
};
|
|
var urlSafeB64 = function(u8) {
|
|
return Nacl.util.encodeBase64(u8).replace(/\//g, "-");
|
|
};
|
|
Block.getBlockUrl = function(keys) {
|
|
var publicKey = urlSafeB64(keys.sign.publicKey);
|
|
return (ApiConfig.fileHost || ApiConfig.httpUnsafeOrigin || window.location.origin) + "/block/" + publicKey.slice(0, 2) + "/" + publicKey;
|
|
};
|
|
Block.getBlockHash = function(keys) {
|
|
var absolute = Block.getBlockUrl(keys);
|
|
var symmetric = urlSafeB64(keys.symmetric);
|
|
return absolute + "#" + symmetric;
|
|
};
|
|
var decodeSafeB64 = function(b64) {
|
|
try {
|
|
return Nacl.util.decodeBase64(b64.replace(/\-/g, "/"));
|
|
} catch (e) {
|
|
console.error(e);
|
|
return;
|
|
}
|
|
};
|
|
Block.parseBlockHash = function(hash) {
|
|
if (typeof hash !== "string") {
|
|
return;
|
|
}
|
|
var parts = hash.split("#");
|
|
if (parts.length !== 2) {
|
|
return;
|
|
}
|
|
try {
|
|
return {
|
|
href: parts[0],
|
|
keys: {
|
|
symmetric: decodeSafeB64(parts[1])
|
|
}
|
|
};
|
|
} catch (e) {
|
|
console.error(e);
|
|
return;
|
|
}
|
|
};
|
|
Block.checkRights = function(data, _cb) {
|
|
const cb = Util.mkAsync(_cb);
|
|
const {blockKeys, auth} = data;
|
|
var command = "MFA_CHECK";
|
|
if (auth && auth.type) {
|
|
command = `${auth.type.toUpperCase()}_` + command;
|
|
}
|
|
ServerCommand(blockKeys.sign, {
|
|
command,
|
|
auth: auth && auth.data
|
|
}, cb);
|
|
};
|
|
Block.writeLoginBlock = function(data, cb) {
|
|
const {content, blockKeys, oldBlockKeys, auth, pw, session, token, userData} = data;
|
|
var command = "WRITE_BLOCK";
|
|
if (auth && auth.type) {
|
|
command = `${auth.type.toUpperCase()}_` + command;
|
|
}
|
|
var block = Block.serialize(JSON.stringify(content), blockKeys);
|
|
block.auth = auth && auth.data;
|
|
block.hasPassword = pw;
|
|
block.registrationProof = oldBlockKeys && Block.proveAncestor(oldBlockKeys);
|
|
if (token) {
|
|
block.inviteToken = token;
|
|
}
|
|
if (userData) {
|
|
block.userData = userData;
|
|
}
|
|
ServerCommand(blockKeys.sign, {
|
|
command,
|
|
content: block,
|
|
session
|
|
}, cb);
|
|
};
|
|
Block.removeLoginBlock = function(data, cb) {
|
|
const {reason, blockKeys, auth, edPublic} = data;
|
|
var command = "REMOVE_BLOCK";
|
|
if (auth && auth.type) {
|
|
command = `${auth.type.toUpperCase()}_` + command;
|
|
}
|
|
ServerCommand(blockKeys.sign, {
|
|
command,
|
|
auth: auth && auth.data,
|
|
edPublic,
|
|
reason
|
|
}, cb);
|
|
};
|
|
Block.updateSSOBlock = function(data, cb) {
|
|
const {blockKeys, oldBlockKeys} = data;
|
|
var oldProof = oldBlockKeys && Block.proveAncestor(oldBlockKeys);
|
|
ServerCommand(blockKeys.sign, {
|
|
command: "SSO_UPDATE_BLOCK",
|
|
ancestorProof: oldProof
|
|
}, cb);
|
|
};
|
|
return Block;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), undefined, requireHttpCommand(), requireNaclFast());
|
|
}
|
|
})();
|
|
})(loginBlock$1);
|
|
return loginBlock$1.exports;
|
|
}
|
|
var loginBlockExports = requireLoginBlock();
|
|
var loginBlock = getDefaultExportFromCjs(loginBlockExports);
|
|
var LoginBlock = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: loginBlock
|
|
}, [ loginBlockExports ]);
|
|
var asyncStore$1 = {
|
|
exports: {}
|
|
};
|
|
var JSON_sortify = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredJSON_sortify;
|
|
function requireJSON_sortify() {
|
|
if (hasRequiredJSON_sortify) return JSON_sortify.exports;
|
|
hasRequiredJSON_sortify = 1;
|
|
(function(module) {
|
|
(function(a) {
|
|
module.exports ? module.exports = a() : JSON.sortify = a();
|
|
})((function() {
|
|
var a = function(b) {
|
|
if (Array.isArray(b)) return b.map(a);
|
|
if (b instanceof Object) {
|
|
var c = [], d = [];
|
|
return Object.keys(b).forEach((function(a) {
|
|
/^(0|[1-9][0-9]*)$/.test(a) ? c.push(+a) : d.push(a);
|
|
})), c.sort((function(c, a) {
|
|
return c - a;
|
|
})).concat(d.sort()).reduce((function(c, d) {
|
|
return c[d] = a(b[d]), c;
|
|
}), {});
|
|
}
|
|
return b;
|
|
}, b = JSON.stringify.bind(JSON);
|
|
return function sortify(c, d, e) {
|
|
var f = b(c, d, 0);
|
|
if (!f || f[0] !== "{" && f[0] !== "[") return f;
|
|
var g = JSON.parse(f);
|
|
return b(a(g), null, e);
|
|
};
|
|
}));
|
|
})(JSON_sortify);
|
|
return JSON_sortify.exports;
|
|
}
|
|
var migrateUserObject$1 = {
|
|
exports: {}
|
|
};
|
|
var commonMessaging = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCommonMessaging;
|
|
function requireCommonMessaging() {
|
|
if (hasRequiredCommonMessaging) return commonMessaging.exports;
|
|
hasRequiredCommonMessaging = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Crypto, Hash, Util, Constants, Realtime) => {
|
|
var Msg = {};
|
|
var createData = Msg.createData = function(proxy, hash) {
|
|
var data = {
|
|
channel: hash || Hash.createChannelId(),
|
|
displayName: proxy["cryptpad.username"],
|
|
profile: proxy.profile && proxy.profile.view,
|
|
edPublic: proxy.edPublic,
|
|
curvePublic: proxy.curvePublic,
|
|
notifications: Util.find(proxy, [ "mailboxes", "notifications", "channel" ]),
|
|
avatar: proxy.profile && proxy.profile.avatar,
|
|
uid: proxy.uid
|
|
};
|
|
if (hash === false) {
|
|
delete data.channel;
|
|
}
|
|
return data;
|
|
};
|
|
var getFriend = Msg.getFriend = function(proxy, pubkey) {
|
|
if (!pubkey) {
|
|
return;
|
|
}
|
|
if (pubkey === proxy.curvePublic) {
|
|
var data = createData(proxy);
|
|
delete data.channel;
|
|
return data;
|
|
}
|
|
return proxy.friends ? proxy.friends[pubkey] : undefined;
|
|
};
|
|
var getFriendList = Msg.getFriendList = function(proxy) {
|
|
if (!proxy.friends) {
|
|
proxy.friends = {};
|
|
}
|
|
return proxy.friends;
|
|
};
|
|
var eachFriend = function(friends, cb) {
|
|
Object.keys(friends).forEach((function(id) {
|
|
if (id === "me") {
|
|
return;
|
|
}
|
|
cb(friends[id], id, friends);
|
|
}));
|
|
};
|
|
Msg.getFriendChannelsList = function(proxy) {
|
|
var list = [];
|
|
eachFriend(proxy.friends, (function(friend) {
|
|
list.push(friend.channel);
|
|
}));
|
|
return list;
|
|
};
|
|
Msg.declineFriendRequest = function(store, data, cb) {
|
|
store.mailbox.sendTo("DECLINE_FRIEND_REQUEST", {}, {
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
}, (function(obj) {
|
|
cb(obj);
|
|
}));
|
|
};
|
|
Msg.acceptFriendRequest = function(store, data, cb) {
|
|
var friend = getFriend(store.proxy, data.curvePublic) || {};
|
|
var myData = createData(store.proxy, friend.channel || data.channel);
|
|
store.mailbox.sendTo("ACCEPT_FRIEND_REQUEST", {
|
|
user: myData
|
|
}, {
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
}, (function(obj) {
|
|
cb(obj);
|
|
}));
|
|
};
|
|
Msg.addToFriendList = function(cfg, data, cb) {
|
|
var proxy = cfg.proxy;
|
|
var friends = getFriendList(proxy);
|
|
var pubKey = data.curvePublic;
|
|
if (pubKey === proxy.curvePublic) {
|
|
return void cb("E_MYKEY");
|
|
}
|
|
friends[pubKey] = data;
|
|
Realtime.whenRealtimeSyncs(cfg.realtime, (function() {
|
|
cb();
|
|
cfg.pinPads([ data.channel ], (function(res) {
|
|
if (res.error) {
|
|
console.error(res.error);
|
|
}
|
|
}));
|
|
}));
|
|
};
|
|
Msg.updateMyData = function(store, curve) {
|
|
var myData = createData(store.proxy, false);
|
|
if (store.proxy.friends) {
|
|
store.proxy.friends.me = Util.clone(myData);
|
|
delete store.proxy.friends.me.channel;
|
|
}
|
|
if (store.modules["team"]) {
|
|
store.modules["team"].updateMyData(myData);
|
|
}
|
|
var todo = function(friend) {
|
|
if (!friend || !friend.notifications) {
|
|
return;
|
|
}
|
|
delete friend.user;
|
|
myData.channel = friend.channel;
|
|
store.mailbox.sendTo("UPDATE_DATA", myData, {
|
|
channel: friend.notifications,
|
|
curvePublic: friend.curvePublic
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
console.error(obj);
|
|
}
|
|
}));
|
|
};
|
|
if (curve) {
|
|
var friend = getFriend(store.proxy, curve);
|
|
return void todo(friend);
|
|
}
|
|
eachFriend(store.proxy.friends || {}, todo);
|
|
};
|
|
Msg.removeFriend = function(store, curvePublic, cb) {
|
|
var proxy = store.proxy;
|
|
var friend = proxy.friends[curvePublic];
|
|
if (!friend) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!friend.notifications) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
store.mailbox.sendTo("UNFRIEND", {
|
|
curvePublic: proxy.curvePublic
|
|
}, {
|
|
channel: friend.notifications,
|
|
curvePublic: friend.curvePublic
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb(obj);
|
|
}
|
|
store.messenger.onFriendRemoved(curvePublic, friend.channel);
|
|
delete proxy.friends[curvePublic];
|
|
Realtime.whenRealtimeSyncs(store.realtime, (function() {
|
|
cb(obj);
|
|
}));
|
|
}));
|
|
};
|
|
return Msg;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCrypto(), requireCommonHash(), requireCommonUtil(), requireCommonConstants(), requireCommonRealtime());
|
|
}
|
|
})();
|
|
})(commonMessaging);
|
|
return commonMessaging.exports;
|
|
}
|
|
var cryptget = {
|
|
exports: {}
|
|
};
|
|
var pinpad = {
|
|
exports: {}
|
|
};
|
|
var rpc = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredRpc;
|
|
function requireRpc() {
|
|
if (hasRequiredRpc) return rpc.exports;
|
|
hasRequiredRpc = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Util, Nacl) {
|
|
var uid = Util.uid;
|
|
var tryParse = Util.tryParse;
|
|
var signMsg = function(data, signKey) {
|
|
var buffer = Nacl.util.decodeUTF8(JSON.stringify(data));
|
|
return Nacl.util.encodeBase64(Nacl.sign.detached(buffer, signKey));
|
|
};
|
|
var sendMsg = function(ctx, data, cb) {
|
|
if (typeof cb !== "function") {
|
|
throw new Error("expected callback");
|
|
}
|
|
var network = ctx.network;
|
|
var hkn = network.historyKeeper;
|
|
if (typeof hkn !== "string") {
|
|
return void cb("NO_HISTORY_KEEPER");
|
|
}
|
|
var txid = uid();
|
|
var pending = ctx.pending[txid] = function(err, response) {
|
|
cb(err, response);
|
|
};
|
|
pending.data = data;
|
|
pending.called = 0;
|
|
return network.sendto(hkn, JSON.stringify([ txid, data ]));
|
|
};
|
|
var matchesAnon = function(ctx, txid) {
|
|
if (!ctx.anon) {
|
|
return false;
|
|
}
|
|
if (typeof ctx.anon.pending[txid] !== "function") {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
var handleAnon = function(ctx, txid, body) {
|
|
var pending = ctx.pending[txid];
|
|
if (body[0] === "ERROR") {
|
|
pending(body[1]);
|
|
} else {
|
|
pending(void 0, body.slice(1));
|
|
}
|
|
delete ctx.pending[txid];
|
|
};
|
|
var onMsg = function(ctx, msg) {
|
|
if (typeof msg !== "string") {
|
|
console.error("received non-string message [%s]", msg);
|
|
}
|
|
var parsed = tryParse(msg);
|
|
if (!parsed) {
|
|
return void console.error(new Error("could not parse message: %s", msg));
|
|
}
|
|
if (!Array.isArray(parsed)) {
|
|
return;
|
|
}
|
|
if (/(FULL_HISTORY|HISTORY_RANGE)/.test(parsed[0])) {
|
|
return;
|
|
}
|
|
var txid = parsed[0];
|
|
if (typeof txid !== "string") {
|
|
return;
|
|
}
|
|
if (matchesAnon(ctx, txid)) {
|
|
return void handleAnon(ctx.anon, txid, parsed.slice(1));
|
|
}
|
|
if (ctx.authenticated.some((function(rpc_ctx) {
|
|
var pending = rpc_ctx.pending[txid];
|
|
if (typeof pending !== "function") {
|
|
return false;
|
|
}
|
|
if (parsed[1] !== "ERROR") {
|
|
if (/\|/.test(parsed[1]) && rpc_ctx.cookie !== parsed[1]) {
|
|
rpc_ctx.cookie = parsed[1];
|
|
}
|
|
pending(void 0, parsed.slice(2));
|
|
delete rpc_ctx.pending[txid];
|
|
return true;
|
|
}
|
|
if (parsed[2] === "NO_COOKIE") {
|
|
rpc_ctx.send("COOKIE", "", (function(e) {
|
|
if (e) {
|
|
console.error(e);
|
|
return void pending(e);
|
|
}
|
|
if (rpc_ctx.resend(txid)) {
|
|
delete rpc_ctx.pending[txid];
|
|
}
|
|
}));
|
|
return true;
|
|
}
|
|
pending(parsed[2]);
|
|
delete rpc_ctx.pending[txid];
|
|
return true;
|
|
}))) {
|
|
return;
|
|
}
|
|
console.error("UNHANDLED RPC MESSAGE", msg);
|
|
};
|
|
var networks = [];
|
|
var contexts = [];
|
|
var initNetworkContext = function(network) {
|
|
var ctx = {
|
|
network,
|
|
connected: true,
|
|
anon: undefined,
|
|
authenticated: []
|
|
};
|
|
networks.push(network);
|
|
contexts.push(ctx);
|
|
network.on("message", (function(msg, sender) {
|
|
if (sender !== network.historyKeeper) {
|
|
return;
|
|
}
|
|
onMsg(ctx, msg);
|
|
}));
|
|
network.on("disconnect", (function() {
|
|
ctx.connected = false;
|
|
if (ctx.anon) {
|
|
ctx.anon.connected = false;
|
|
}
|
|
ctx.authenticated.forEach((function(ctx) {
|
|
ctx.connected = false;
|
|
}));
|
|
}));
|
|
network.on("reconnect", (function() {
|
|
if (ctx.anon) {
|
|
ctx.anon.connected = true;
|
|
}
|
|
ctx.authenticated.forEach((function(ctx) {
|
|
ctx.connected = true;
|
|
}));
|
|
}));
|
|
return ctx;
|
|
};
|
|
var getNetworkContext = function(network) {
|
|
var i;
|
|
networks.some((function(current, j) {
|
|
if (network !== current) {
|
|
return false;
|
|
}
|
|
i = j;
|
|
return true;
|
|
}));
|
|
if (contexts[i]) {
|
|
return contexts[i];
|
|
}
|
|
return initNetworkContext(network);
|
|
};
|
|
var initAuthenticatedRpc = function(networkContext, keys) {
|
|
var ctx = {
|
|
network: networkContext.network,
|
|
publicKey: keys.publicKeyString,
|
|
timeouts: {},
|
|
pending: {},
|
|
cookie: null,
|
|
connected: true
|
|
};
|
|
var send = ctx.send = function(type, msg, _cb) {
|
|
var cb = Util.mkAsync(_cb);
|
|
if (!ctx.connected && type !== "COOKIE") {
|
|
return void cb("DISCONNECTED");
|
|
}
|
|
var data = [ type, msg ];
|
|
if (ctx.cookie && ctx.cookie.join) {
|
|
data.unshift(ctx.cookie.join("|"));
|
|
} else {
|
|
data.unshift(ctx.cookie);
|
|
}
|
|
var sig = signMsg(data, keys.signKey);
|
|
data.unshift(keys.publicKeyString);
|
|
data.unshift(sig);
|
|
return sendMsg(ctx, data, cb);
|
|
};
|
|
ctx.resend = function(txid) {
|
|
var pending = ctx.pending[txid];
|
|
if (pending.called) {
|
|
console.error("[%s] called too many times", txid);
|
|
return true;
|
|
}
|
|
pending.called++;
|
|
pending.data[2] = ctx.cookie;
|
|
pending.data[0] = signMsg(pending.data.slice(2), keys.signKey);
|
|
var new_txid = uid();
|
|
ctx.pending[new_txid] = pending;
|
|
delete ctx.pending[txid];
|
|
try {
|
|
return ctx.network.sendto(ctx.network.historyKeeper, JSON.stringify([ new_txid, pending.data ]));
|
|
} catch (e) {
|
|
console.log("failed to resend");
|
|
console.error(e);
|
|
}
|
|
};
|
|
send.unauthenticated = function(type, msg, _cb) {
|
|
var cb = Util.mkAsync(_cb);
|
|
if (!ctx.connected) {
|
|
return void cb("DISCONNECTED");
|
|
}
|
|
var data = [ null, keys.publicKeyString, null, type, msg ];
|
|
if (ctx.cookie && ctx.cookie.join) {
|
|
data[2] = ctx.cookie.join("|");
|
|
} else {
|
|
data[2] = ctx.cookie;
|
|
}
|
|
return sendMsg(ctx, data, cb);
|
|
};
|
|
ctx.destroy = function() {
|
|
Object.keys(ctx.timeouts).forEach((function(to) {
|
|
clearTimeout(to);
|
|
}));
|
|
var idx = networkContext.authenticated.indexOf(ctx);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
networkContext.authenticated.splice(idx, 1);
|
|
};
|
|
networkContext.authenticated.push(ctx);
|
|
return ctx;
|
|
};
|
|
var getAuthenticatedContext = function(networkContext, keys) {
|
|
if (!networkContext) {
|
|
throw new Error("expected network context");
|
|
}
|
|
var publicKey = keys.publicKeyString;
|
|
var i;
|
|
networkContext.authenticated.some((function(ctx, j) {
|
|
if (ctx.publicKey !== publicKey) {
|
|
return false;
|
|
}
|
|
i = j;
|
|
return true;
|
|
}));
|
|
if (networkContext.authenticated[i]) {
|
|
return networkContext.authenticated[i];
|
|
}
|
|
return initAuthenticatedRpc(networkContext, keys);
|
|
};
|
|
var create = function(network, edPrivateKey, edPublicKey, _cb) {
|
|
if (typeof _cb !== "function") {
|
|
throw new Error("expected callback");
|
|
}
|
|
var cb = Util.mkAsync(_cb);
|
|
var signKey;
|
|
try {
|
|
signKey = Nacl.util.decodeBase64(edPrivateKey);
|
|
if (signKey.length !== 64) {
|
|
throw new Error("private key did not match expected length of 64");
|
|
}
|
|
} catch (err) {
|
|
return void cb(err);
|
|
}
|
|
try {
|
|
if (Nacl.util.decodeBase64(edPublicKey).length !== 32) {
|
|
return void cb("expected public key to be 32 uint");
|
|
}
|
|
} catch (err) {
|
|
return void cb(err);
|
|
}
|
|
if (!network) {
|
|
return void cb("NO_NETWORK");
|
|
}
|
|
var net_ctx = getNetworkContext(network);
|
|
var rpc_ctx = getAuthenticatedContext(net_ctx, {
|
|
publicKeyString: edPublicKey,
|
|
signKey
|
|
});
|
|
rpc_ctx.send("COOKIE", "", (function(e) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
cb(void 0, {
|
|
send: rpc_ctx.send,
|
|
destroy: rpc_ctx.destroy
|
|
});
|
|
}));
|
|
};
|
|
var initAnonRpc = function(networkContext) {
|
|
var ctx = {
|
|
network: networkContext.network,
|
|
timeouts: {},
|
|
pending: {},
|
|
connected: true
|
|
};
|
|
networkContext.anon = ctx;
|
|
ctx.send = function(type, msg, _cb) {
|
|
var cb = Util.mkAsync(_cb);
|
|
if (!ctx.connected) {
|
|
return void cb("DISCONNECTED");
|
|
}
|
|
var data = [ type, msg ];
|
|
return sendMsg(ctx, data, cb);
|
|
};
|
|
ctx.resend = function(txid) {
|
|
var pending = ctx.pending[txid];
|
|
if (pending.called) {
|
|
console.error("[%s] called too many times", txid);
|
|
return true;
|
|
}
|
|
pending.called++;
|
|
try {
|
|
return ctx.network.sendto(ctx.network.historyKeeper, JSON.stringify([ txid, pending.data ]));
|
|
} catch (e) {
|
|
console.log("failed to resend");
|
|
console.error(e);
|
|
}
|
|
};
|
|
ctx.destroy = function() {
|
|
Object.keys(ctx.timeouts).forEach((function(to) {
|
|
clearTimeout(to);
|
|
}));
|
|
networkContext.anon = undefined;
|
|
};
|
|
return ctx;
|
|
};
|
|
var getAnonContext = function(networkContext) {
|
|
return networkContext.anon || initAnonRpc(networkContext);
|
|
};
|
|
var createAnonymous = function(network, _cb) {
|
|
var cb = Util.mkAsync(_cb);
|
|
if (typeof cb !== "function") {
|
|
throw new Error("expected callback");
|
|
}
|
|
if (!network) {
|
|
return void cb("NO_NETWORK");
|
|
}
|
|
var ctx = getAnonContext(getNetworkContext(network));
|
|
cb(void 0, {
|
|
send: ctx.send,
|
|
destroy: ctx.destroy
|
|
});
|
|
};
|
|
return {
|
|
create,
|
|
createAnonymous
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireNaclFast());
|
|
}
|
|
})();
|
|
})(rpc);
|
|
return rpc.exports;
|
|
}
|
|
var hasRequiredPinpad;
|
|
function requirePinpad() {
|
|
if (hasRequiredPinpad) return pinpad.exports;
|
|
hasRequiredPinpad = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Util, Rpc) {
|
|
var create = function(network, proxy, _cb, Cache) {
|
|
if (typeof _cb !== "function") {
|
|
throw new Error("Expected callback");
|
|
}
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!network) {
|
|
return void cb("INVALID_NETWORK");
|
|
}
|
|
if (!proxy) {
|
|
return void cb("INVALID_PROXY");
|
|
}
|
|
var edPrivate = proxy.edPrivate;
|
|
var edPublic = proxy.edPublic;
|
|
if (!(edPrivate && edPublic)) {
|
|
return void cb("INVALID_KEYS");
|
|
}
|
|
Rpc.create(network, edPrivate, edPublic, (function(e, rpc) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
var exp = {};
|
|
exp.destroy = rpc.destroy;
|
|
exp.publicKey = edPublic;
|
|
exp.send = rpc.send;
|
|
exp.pin = function(channels, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!Array.isArray(channels)) {
|
|
return void cb("[TypeError] pin expects an array");
|
|
}
|
|
rpc.send("PIN", channels, cb);
|
|
};
|
|
exp.unpin = function(channels, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!Array.isArray(channels)) {
|
|
return void cb("[TypeError] pin expects an array");
|
|
}
|
|
rpc.send("UNPIN", channels, cb);
|
|
};
|
|
exp.adminRpc = function(obj, cb) {
|
|
if (!obj.cmd) {
|
|
setTimeout((function() {
|
|
cb("[TypeError] admin rpc expects a command");
|
|
}));
|
|
return;
|
|
}
|
|
var params = [ obj.cmd, obj.data ];
|
|
rpc.send("ADMIN", params, cb);
|
|
};
|
|
exp.getServerHash = function(cb) {
|
|
rpc.send("GET_HASH", edPublic, (function(e, hash) {
|
|
if (!(hash && hash[0])) {
|
|
return void cb("NO_HASH_RETURNED");
|
|
}
|
|
cb(e, Array.isArray(hash) && hash[0] || undefined);
|
|
}));
|
|
};
|
|
exp.reset = function(channels, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!Array.isArray(channels)) {
|
|
return void cb("[TypeError] pin expects an array");
|
|
}
|
|
rpc.send("RESET", channels, cb);
|
|
};
|
|
exp.getFileListSize = function(cb) {
|
|
rpc.send("GET_TOTAL_SIZE", undefined, (function(e, response) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
if (response && response.length && typeof response[0] === "number") {
|
|
cb(void 0, response[0]);
|
|
} else {
|
|
cb("INVALID_RESPONSE");
|
|
}
|
|
}));
|
|
};
|
|
exp.updatePinLimits = function(cb) {
|
|
rpc.send("UPDATE_LIMITS", undefined, (function(e, response) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
if (response && response.length && typeof response[0] === "number") {
|
|
cb(void 0, response[0], response[1], response[2]);
|
|
} else {
|
|
cb("INVALID_RESPONSE");
|
|
}
|
|
}));
|
|
};
|
|
exp.getLimit = function(cb) {
|
|
rpc.send("GET_LIMIT", undefined, (function(e, response) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
if (response && response.length && typeof response[0] === "number") {
|
|
cb(void 0, response[0], response[1], response[2]);
|
|
} else {
|
|
cb("INVALID_RESPONSE");
|
|
}
|
|
}));
|
|
};
|
|
exp.trimHistory = function(data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (typeof data !== "object" || !data.channel || !data.hash) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
rpc.send("TRIM_HISTORY", data, (function(e) {
|
|
if (e) {
|
|
return cb(e);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
exp.clearOwnedChannel = function(channel, cb) {
|
|
if (typeof channel !== "string" || channel.length !== 32) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
rpc.send("CLEAR_OWNED_CHANNEL", channel, (function(e) {
|
|
if (e) {
|
|
return cb(e);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
exp.removeOwnedChannel = function(channel, cb, reason) {
|
|
if (typeof channel !== "string" || [ 32, 48 ].indexOf(channel.length) === -1) {
|
|
console.error("invalid channel to remove", channel);
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
rpc.send("REMOVE_OWNED_CHANNEL", {
|
|
channel,
|
|
reason
|
|
}, (function(e, response) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
if (response && response.length && response[0] === "OK") {
|
|
cb();
|
|
if (Cache && Cache.clearChannel) {
|
|
Cache.clearChannel(channel);
|
|
}
|
|
} else {
|
|
cb("INVALID_RESPONSE");
|
|
}
|
|
}));
|
|
};
|
|
exp.removePins = function(cb) {
|
|
rpc.send("REMOVE_PINS", undefined, (function(e, response) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
if (response && response.length && response[0] === "OK") {
|
|
cb();
|
|
} else {
|
|
cb("INVALID_RESPONSE");
|
|
}
|
|
}));
|
|
};
|
|
exp.uploadComplete = function(id, cb) {
|
|
rpc.send("UPLOAD_COMPLETE", id, (function(e, res) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
var id = res[0];
|
|
if (typeof id !== "string") {
|
|
return void cb("INVALID_ID");
|
|
}
|
|
cb(void 0, id);
|
|
}));
|
|
};
|
|
exp.ownedUploadComplete = function(id, cb) {
|
|
rpc.send("OWNED_UPLOAD_COMPLETE", id, (function(e, res) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
var id = res[0];
|
|
if (typeof id !== "string") {
|
|
return void cb("INVALID_ID");
|
|
}
|
|
cb(void 0, id);
|
|
}));
|
|
};
|
|
exp.uploadStatus = function(size, cb) {
|
|
if (typeof size !== "number") {
|
|
return void setTimeout((function() {
|
|
cb("INVALID_SIZE");
|
|
}));
|
|
}
|
|
rpc.send("UPLOAD_STATUS", size, (function(e, res) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
var pending = res[0];
|
|
if (typeof pending !== "boolean") {
|
|
return void cb("INVALID_RESPONSE");
|
|
}
|
|
cb(void 0, pending);
|
|
}));
|
|
};
|
|
exp.uploadCancel = function(size, cb) {
|
|
rpc.send("UPLOAD_CANCEL", size, (function(e) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
exp.setMetadata = function(obj, cb) {
|
|
rpc.send("SET_METADATA", {
|
|
channel: obj.channel,
|
|
command: obj.command,
|
|
value: obj.value
|
|
}, cb);
|
|
};
|
|
cb(e, exp);
|
|
}));
|
|
};
|
|
return {
|
|
create
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireRpc());
|
|
}
|
|
})();
|
|
})(pinpad);
|
|
return pinpad.exports;
|
|
}
|
|
var hasRequiredCryptget;
|
|
function requireCryptget() {
|
|
if (hasRequiredCryptget) return cryptget.exports;
|
|
hasRequiredCryptget = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Crypto, CPNetflux, Netflux, Util, Hash, Realtime, NetConfig, Cache, Pinpad, nThen) => {
|
|
var finish = function(S, err, doc) {
|
|
if (S.done) {
|
|
return;
|
|
}
|
|
S.cb(err && err.error, doc, err);
|
|
S.done = true;
|
|
if (!S.hasNetwork) {
|
|
var disconnect = Util.find(S, [ "network", "disconnect" ]);
|
|
if (typeof disconnect === "function") {
|
|
disconnect();
|
|
}
|
|
}
|
|
if (S.realtime && S.realtime.stop) {
|
|
try {
|
|
S.realtime.stop();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
var abort = Util.find(S, [ "session", "realtime", "abort" ]);
|
|
if (typeof abort === "function") {
|
|
S.session.realtime.sync();
|
|
abort();
|
|
}
|
|
};
|
|
var makeNetwork = function(cb) {
|
|
var wsUrl = NetConfig.getWebsocketURL();
|
|
Netflux.connect(wsUrl).then((function(network) {
|
|
cb(null, network);
|
|
}), (function(err) {
|
|
cb(err);
|
|
}));
|
|
};
|
|
var start = function(Session, config) {
|
|
nThen((function(waitFor) {
|
|
if (Session.hasNetwork) {
|
|
return;
|
|
}
|
|
makeNetwork(waitFor((function(err, network) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
config.network = network;
|
|
})));
|
|
})).nThen((function() {
|
|
Session.realtime = CPNetflux.start(config);
|
|
}));
|
|
};
|
|
var onRejected = function(config, Session, data, cb) {
|
|
if (!Array.isArray(data) || !data.length || data[0].length !== 16) {
|
|
return void cb(true);
|
|
}
|
|
if (!Array.isArray(Session.accessKeys)) {
|
|
return void cb(true);
|
|
}
|
|
config.network.historyKeeper = data[0];
|
|
nThen((function(waitFor) {
|
|
Session.accessKeys.forEach((function(obj) {
|
|
Pinpad.create(config.network, obj, waitFor((function(e) {
|
|
console.log("done", obj);
|
|
if (e) {
|
|
console.error(e);
|
|
}
|
|
})));
|
|
}));
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var makeConfig = function(hash, opt) {
|
|
var secret;
|
|
if (typeof hash === "string") {
|
|
secret = Hash.getSecrets("pad", hash, opt.password);
|
|
} else if (typeof hash === "object") {
|
|
secret = hash;
|
|
}
|
|
if (!secret.keys) {
|
|
secret.keys = secret.key;
|
|
}
|
|
var config = {
|
|
websocketURL: NetConfig.getWebsocketURL(opt.origin),
|
|
channel: secret.channel,
|
|
validateKey: secret.keys.validateKey || undefined,
|
|
crypto: Crypto.createEncryptor(secret.keys),
|
|
logLevel: 0,
|
|
initialState: opt.initialState,
|
|
Cache
|
|
};
|
|
return config;
|
|
};
|
|
var isObject = function(o) {
|
|
return typeof o === "object";
|
|
};
|
|
var overwrite = function(a, b) {
|
|
if (!(isObject(a) && isObject(b))) {
|
|
return;
|
|
}
|
|
Object.keys(b).forEach((function(k) {
|
|
a[k] = b[k];
|
|
}));
|
|
};
|
|
var get = function(hash, cb, opt, progress) {
|
|
if (typeof cb !== "function") {
|
|
throw new Error("Cryptget expects a callback");
|
|
}
|
|
opt = opt || {};
|
|
progress = progress || function() {};
|
|
var config = makeConfig(hash, opt);
|
|
var Session = {
|
|
cb,
|
|
accessKeys: opt.accessKeys,
|
|
hasNetwork: Boolean(opt.network)
|
|
};
|
|
config.onRejected = function(data, cb) {
|
|
onRejected(config, Session, data, cb);
|
|
};
|
|
config.onReady = function(info) {
|
|
var rt = Session.session = info.realtime;
|
|
Session.network = info.network;
|
|
progress(1);
|
|
finish(Session, void 0, rt.getUserDoc());
|
|
};
|
|
config.onError = function(info) {
|
|
console.warn(info);
|
|
finish(Session, info);
|
|
};
|
|
config.onChannelError = function(info) {
|
|
console.error(info);
|
|
finish(Session, info);
|
|
};
|
|
config.onCacheReady = opt.onCacheReady;
|
|
var i = 0;
|
|
config.onMessage = function() {
|
|
i++;
|
|
progress(Math.min(.99, i / 100));
|
|
};
|
|
overwrite(config, opt);
|
|
start(Session, config);
|
|
};
|
|
var put = function(hash, doc, cb, opt) {
|
|
if (typeof cb !== "function") {
|
|
throw new Error("Cryptput expects a callback");
|
|
}
|
|
opt = opt || {};
|
|
var config = makeConfig(hash, opt);
|
|
var Session = {
|
|
cb,
|
|
accessKeys: opt.accessKeys,
|
|
hasNetwork: Boolean(opt.network)
|
|
};
|
|
config.onRejected = function(data, cb) {
|
|
onRejected(config, Session, data, cb);
|
|
};
|
|
config.onReady = function(info) {
|
|
var realtime = Session.session = info.realtime;
|
|
Session.network = info.network;
|
|
realtime.contentUpdate(doc);
|
|
var to = setTimeout((function() {
|
|
cb(new Error("Timeout"));
|
|
}), 15e3);
|
|
Realtime.whenRealtimeSyncs(realtime, (function() {
|
|
clearTimeout(to);
|
|
var doc = realtime.getAuthDoc();
|
|
realtime.abort();
|
|
finish(Session, void 0, doc);
|
|
}));
|
|
};
|
|
config.onChannelError = function(info) {
|
|
finish(Session, info);
|
|
};
|
|
overwrite(config, opt);
|
|
start(Session, config);
|
|
};
|
|
return {
|
|
get,
|
|
put
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCrypto(), requireChainpadNetflux(), requireNetfluxClient(), requireCommonUtil(), requireCommonHash(), requireCommonRealtime(), requireNetworkConfig(), requireCacheStore(), requirePinpad(), requireNthen(), requireChainpad_dist());
|
|
}
|
|
})();
|
|
})(cryptget);
|
|
return cryptget.exports;
|
|
}
|
|
var mailbox$1 = {
|
|
exports: {}
|
|
};
|
|
var notify = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredNotify;
|
|
function requireNotify() {
|
|
if (hasRequiredNotify) return notify.exports;
|
|
hasRequiredNotify = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (ApiConfig = {}) => {
|
|
let window = globalThis;
|
|
var Module = {};
|
|
ApiConfig.requireConf = ApiConfig.requireConf || {};
|
|
Module.setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
};
|
|
var apps = [ "code", "slide", "pad", "kanban", "whiteboard", "diagram", "sheet", "poll", "teams", "form", "doc", "presentation" ];
|
|
var app = window.location && window.location.pathname.slice(1, -1);
|
|
var suffix = apps.indexOf(app) !== -1 ? "-" + app : "";
|
|
var DEFAULT_MAIN = "/customize/favicon/main-favicon" + suffix + ".png?" + ApiConfig.requireConf.urlArgs;
|
|
var DEFAULT_ALT = "/customize/favicon/alt-favicon" + suffix + ".png?" + ApiConfig.requireConf.urlArgs;
|
|
var DEFAULT_MAIN_ICO = "/customize/favicon/main-favicon" + suffix + ".ico?" + ApiConfig.requireConf.urlArgs;
|
|
var DEFAULT_ALT_ICO = "/customize/favicon/alt-favicon" + suffix + ".ico?" + ApiConfig.requireConf.urlArgs;
|
|
var document = window.document;
|
|
var isSupported = Module.isSupported = function() {
|
|
return typeof window.Notification === "function" && window.isSecureContext;
|
|
};
|
|
var hasPermission = Module.hasPermission = function() {
|
|
return Notification.permission === "granted";
|
|
};
|
|
var getPermission = Module.getPermission = function(f) {
|
|
f = f || function() {};
|
|
if (!Notification || typeof Notification.requestPermission !== "function") {
|
|
return void f(false);
|
|
}
|
|
Notification.requestPermission((function(permission) {
|
|
if (permission === "granted") {
|
|
f(true);
|
|
} else {
|
|
f(false);
|
|
}
|
|
}));
|
|
};
|
|
var create = Module.create = function(msg, title, icon) {
|
|
if (document && !icon) {
|
|
var favicon = document.getElementById("favicon");
|
|
icon = favicon.getAttribute("data-main-favicon") || DEFAULT_ALT;
|
|
} else if (!icon) {
|
|
icon = DEFAULT_ALT;
|
|
}
|
|
var n = new Notification(title, {
|
|
icon,
|
|
body: msg
|
|
});
|
|
n.onclick = function() {
|
|
if (!document) {
|
|
return;
|
|
}
|
|
try {
|
|
parent.focus();
|
|
window.focus();
|
|
this.close();
|
|
} catch (e) {}
|
|
};
|
|
return n;
|
|
};
|
|
Module.system = function(msg, title, icon) {
|
|
if (!isSupported()) {
|
|
return;
|
|
} else if (hasPermission()) {
|
|
return create(msg, title, icon);
|
|
} else if (Notification.permission !== "denied") {
|
|
getPermission((function(state) {
|
|
if (state) {
|
|
create(msg, title, icon);
|
|
}
|
|
}));
|
|
}
|
|
};
|
|
var createFavicon = function() {
|
|
if (!document) {
|
|
return void console.error("document is not available in this context");
|
|
}
|
|
console.debug("creating favicon");
|
|
var attrs = {
|
|
id: "favicon",
|
|
type: "image/png",
|
|
rel: "icon",
|
|
"data-main-favicon": DEFAULT_MAIN,
|
|
"data-alt-favicon": DEFAULT_ALT,
|
|
href: DEFAULT_MAIN
|
|
};
|
|
if (!document.getElementById("favicon")) {
|
|
var fav = document.createElement("link");
|
|
Object.keys(attrs).forEach((function(k) {
|
|
fav.setAttribute(k, attrs[k]);
|
|
}));
|
|
document.head.appendChild(fav);
|
|
}
|
|
if (!document.getElementById("favicon-ico")) {
|
|
var faviconLink = document.createElement("link");
|
|
attrs.href = attrs.href.replace(/\.png/g, ".ico");
|
|
attrs.id = "favicon-ico";
|
|
attrs.type = "image/x-icon";
|
|
Object.keys(attrs).forEach((function(k) {
|
|
faviconLink.setAttribute(k, attrs[k]);
|
|
}));
|
|
document.head.appendChild(faviconLink);
|
|
}
|
|
};
|
|
if (document && !document.getElementById("favicon")) {
|
|
createFavicon();
|
|
}
|
|
Module.tab = function(frequency, count) {
|
|
if (!document) {
|
|
return void console.error("document is not available in this context");
|
|
}
|
|
var key = "_pendingTabNotification";
|
|
var favicon = document.getElementById("favicon");
|
|
var faviconIco = document.getElementById("favicon-ico");
|
|
var main = DEFAULT_MAIN;
|
|
var alt = DEFAULT_ALT;
|
|
var mainIco = DEFAULT_MAIN_ICO;
|
|
var altIco = DEFAULT_ALT_ICO;
|
|
if (favicon) {
|
|
main = favicon.getAttribute("data-main-favicon") || DEFAULT_MAIN;
|
|
alt = favicon.getAttribute("data-alt-favicon") || DEFAULT_ALT;
|
|
favicon.setAttribute("href", main);
|
|
}
|
|
if (faviconIco) {
|
|
mainIco = faviconIco.getAttribute("data-main-favicon") || DEFAULT_MAIN_ICO;
|
|
altIco = faviconIco.getAttribute("data-alt-favicon") || DEFAULT_ALT_ICO;
|
|
faviconIco.setAttribute("href", mainIco);
|
|
}
|
|
var cancel = function(pending) {
|
|
if (Module[key]) {
|
|
window.clearInterval(Module[key]);
|
|
if (favicon) {
|
|
favicon.setAttribute("href", pending ? alt : main);
|
|
}
|
|
if (faviconIco) {
|
|
faviconIco.setAttribute("href", pending ? altIco : mainIco);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
cancel();
|
|
var step = function() {
|
|
if (favicon) {
|
|
favicon.setAttribute("href", favicon.getAttribute("href") === main ? alt : main);
|
|
}
|
|
if (faviconIco) {
|
|
faviconIco.setAttribute("href", faviconIco.getAttribute("href") === mainIco ? altIco : mainIco);
|
|
}
|
|
--count;
|
|
};
|
|
Module[key] = window.setInterval((function() {
|
|
if (count > 0) {
|
|
return step();
|
|
}
|
|
cancel(true);
|
|
}), frequency);
|
|
step();
|
|
return {
|
|
cancel
|
|
};
|
|
};
|
|
return Module;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined);
|
|
}
|
|
})();
|
|
})(notify);
|
|
return notify.exports;
|
|
}
|
|
var mailboxHandlers = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredMailboxHandlers;
|
|
function requireMailboxHandlers() {
|
|
if (hasRequiredMailboxHandlers) return mailboxHandlers.exports;
|
|
hasRequiredMailboxHandlers = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Messaging, Hash, Util, Crypto, Block) => {
|
|
var getRandomTimeout = function(ctx) {
|
|
var lag = ctx.store.realtime.getLag().lag || 0;
|
|
return (Math.max(0, lag) + 300) * 20 * (.5 + Math.random());
|
|
};
|
|
var handlers = {};
|
|
var removeHandlers = {};
|
|
var isMuted = function(ctx, data) {
|
|
var muted = ctx.store.proxy.mutedUsers || {};
|
|
var curvePublic = Util.find(data, [ "msg", "author" ]);
|
|
if (!curvePublic) {
|
|
return false;
|
|
}
|
|
return Boolean(muted[curvePublic]);
|
|
};
|
|
var isChannelMuted = function(ctx, channel) {
|
|
var muted = ctx.store.proxy.mutedChannels || [];
|
|
return muted.includes(channel);
|
|
};
|
|
var friendRequest = {};
|
|
handlers["FRIEND_REQUEST"] = function(ctx, box, data, cb) {
|
|
var userData = data.msg.content.user || data.msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
if (friendRequest[data.msg.author]) {
|
|
return void cb(true);
|
|
}
|
|
friendRequest[data.msg.author] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
if (Messaging.getFriend(ctx.store.proxy, data.msg.author) || ctx.store.proxy.friends_pending[data.msg.author]) {
|
|
delete ctx.store.proxy.friends_pending[data.msg.author];
|
|
Messaging.acceptFriendRequest(ctx.store, userData, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb();
|
|
}
|
|
Messaging.addToFriendList({
|
|
proxy: ctx.store.proxy,
|
|
realtime: ctx.store.realtime,
|
|
pinPads: ctx.pinPads
|
|
}, userData, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return void cb(true);
|
|
}
|
|
if (ctx.store.messenger) {
|
|
ctx.store.messenger.onFriendAdded(userData);
|
|
}
|
|
ctx.updateMetadata();
|
|
cb(true);
|
|
}));
|
|
}));
|
|
return;
|
|
}
|
|
cb();
|
|
};
|
|
removeHandlers["FRIEND_REQUEST"] = function(ctx, box, data) {
|
|
var userData = data.content.user || data.content;
|
|
if (friendRequest[userData.curvePublic]) {
|
|
delete friendRequest[userData.curvePublic];
|
|
}
|
|
};
|
|
var friendRequestDeclined = {};
|
|
var friendRequestAccepted = {};
|
|
handlers["DECLINE_FRIEND_REQUEST"] = function(ctx, box, data, cb) {
|
|
var userData = data.msg.content.user || data.msg.content;
|
|
if (!userData.curvePublic) {
|
|
userData.curvePublic = data.msg.author;
|
|
}
|
|
setTimeout((function() {
|
|
cb(true);
|
|
if (!ctx.store.proxy.friends_pending[data.msg.author]) {
|
|
return;
|
|
}
|
|
delete ctx.store.proxy.friends_pending[data.msg.author];
|
|
ctx.updateMetadata();
|
|
if (friendRequestDeclined[data.msg.author]) {
|
|
return;
|
|
}
|
|
box.sendMessage({
|
|
type: "FRIEND_REQUEST_DECLINED",
|
|
content: {
|
|
user: userData
|
|
}
|
|
}, (function(hash) {
|
|
friendRequestDeclined[data.msg.author] = {
|
|
type: box.type,
|
|
hash
|
|
};
|
|
}));
|
|
}), getRandomTimeout(ctx));
|
|
};
|
|
handlers["FRIEND_REQUEST_DECLINED"] = function(ctx, box, data, cb) {
|
|
ctx.updateMetadata();
|
|
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
|
|
var toRemove = friendRequestAccepted[curve];
|
|
delete friendRequestAccepted[curve];
|
|
if (friendRequestDeclined[curve]) {
|
|
return void cb(true, toRemove);
|
|
}
|
|
friendRequestDeclined[curve] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["FRIEND_REQUEST_DECLINED"] = function(ctx, box, data) {
|
|
var curve = data.content.user.curvePublic || data.content.user;
|
|
if (friendRequestDeclined[curve]) {
|
|
delete friendRequestDeclined[curve];
|
|
}
|
|
};
|
|
handlers["ACCEPT_FRIEND_REQUEST"] = function(ctx, box, data, cb) {
|
|
var userData = data.msg.content.user || data.msg.content;
|
|
setTimeout((function() {
|
|
cb(true);
|
|
if (!ctx.store.proxy.friends_pending[data.msg.author]) {
|
|
return;
|
|
}
|
|
delete ctx.store.proxy.friends_pending[data.msg.author];
|
|
Messaging.addToFriendList({
|
|
proxy: ctx.store.proxy,
|
|
realtime: ctx.store.realtime,
|
|
pinPads: ctx.pinPads
|
|
}, userData, (function(err) {
|
|
if (err) {
|
|
return void console.error(err);
|
|
}
|
|
if (ctx.store.messenger) {
|
|
ctx.store.messenger.onFriendAdded(userData);
|
|
}
|
|
ctx.updateMetadata();
|
|
if (ctx.store.modules["profile"]) {
|
|
ctx.store.modules["profile"].update();
|
|
}
|
|
if (friendRequestAccepted[data.msg.author]) {
|
|
return;
|
|
}
|
|
box.sendMessage({
|
|
type: "FRIEND_REQUEST_ACCEPTED",
|
|
content: {
|
|
user: userData
|
|
}
|
|
}, (function(hash) {
|
|
friendRequestAccepted[data.msg.author] = {
|
|
type: box.type,
|
|
hash
|
|
};
|
|
}));
|
|
}));
|
|
}), getRandomTimeout(ctx));
|
|
};
|
|
handlers["FRIEND_REQUEST_ACCEPTED"] = function(ctx, box, data, cb) {
|
|
ctx.updateMetadata();
|
|
var curve = data.msg.content.user.curvePublic || data.msg.content.user;
|
|
var toRemove = friendRequestDeclined[curve];
|
|
delete friendRequestDeclined[curve];
|
|
if (friendRequestAccepted[curve]) {
|
|
return void cb(true, toRemove);
|
|
}
|
|
friendRequestAccepted[curve] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["FRIEND_REQUEST_ACCEPTED"] = function(ctx, box, data) {
|
|
var curve = data.content.user.curvePublic || data.content.user;
|
|
if (friendRequestAccepted[curve]) {
|
|
delete friendRequestAccepted[curve];
|
|
}
|
|
};
|
|
handlers["CANCEL_FRIEND_REQUEST"] = function(ctx, box, data, cb) {
|
|
var f = friendRequest[data.msg.author];
|
|
if (!f) {
|
|
return void cb(true);
|
|
}
|
|
cb(true, f);
|
|
};
|
|
handlers["UNFRIEND"] = function(ctx, box, data, cb) {
|
|
var curve = data.msg.author;
|
|
var friend = Messaging.getFriend(ctx.store.proxy, curve);
|
|
if (!friend) {
|
|
return void cb(true);
|
|
}
|
|
delete ctx.store.proxy.friends[curve];
|
|
delete ctx.store.proxy.friends_pending[curve];
|
|
if (ctx.store.messenger) {
|
|
ctx.store.messenger.onFriendRemoved(curve, friend.channel);
|
|
}
|
|
ctx.updateMetadata();
|
|
cb(true);
|
|
};
|
|
handlers["UPDATE_DATA"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var curve = msg.author;
|
|
var friend = ctx.store.proxy.friends && ctx.store.proxy.friends[curve];
|
|
if (!friend || typeof msg.content !== "object") {
|
|
return void cb(true);
|
|
}
|
|
Object.keys(msg.content).forEach((function(key) {
|
|
friend[key] = msg.content[key];
|
|
}));
|
|
if (ctx.store.messenger) {
|
|
ctx.store.messenger.onFriendUpdate(curve);
|
|
}
|
|
ctx.updateMetadata();
|
|
cb(true);
|
|
};
|
|
var encryptPassword = function(ctx, password) {
|
|
let uHash = ctx.store.data.blockHash;
|
|
let uSecret = Block.parseBlockHash(uHash);
|
|
let key = uSecret.keys.symmetric;
|
|
return Crypto.encrypt(password, key);
|
|
};
|
|
var channels = {};
|
|
handlers["SHARE_PAD"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var hash = data.hash;
|
|
var content = msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
var channel = content.isStatic ? content.href : Hash.hrefToHexChannelId(content.href, content.password);
|
|
var parsed = Hash.parsePadUrl(content.href);
|
|
var mode = parsed.hashData && parsed.hashData.mode || "n/a";
|
|
var old = channels[channel];
|
|
var toRemove;
|
|
if (old) {
|
|
if (old.mode === "edit" && mode === "view") {
|
|
return void cb(true);
|
|
}
|
|
toRemove = old.data;
|
|
}
|
|
if (content.password) {
|
|
content.password = encryptPassword(ctx, content.password);
|
|
}
|
|
channels[channel] = {
|
|
mode,
|
|
data: {
|
|
type: box.type,
|
|
hash
|
|
}
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["SHARE_PAD"] = function(ctx, box, data, hash) {
|
|
var content = data.content;
|
|
var channel = Hash.hrefToHexChannelId(content.href, content.password);
|
|
var old = channels[channel];
|
|
if (old && old.data && old.data.hash === hash) {
|
|
delete channels[channel];
|
|
}
|
|
};
|
|
var supportMessage = false;
|
|
handlers["SUPPORT_MESSAGE"] = function(ctx, box, data, cb) {
|
|
if (supportMessage) {
|
|
return void cb(true);
|
|
}
|
|
supportMessage = true;
|
|
cb();
|
|
};
|
|
removeHandlers["SUPPORT_MESSAGE"] = function() {
|
|
supportMessage = false;
|
|
};
|
|
handlers["REQUEST_PAD_ACCESS"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel;
|
|
var res = ctx.store.manager.findChannel(channel);
|
|
if (!res.length) {
|
|
return void cb(true);
|
|
}
|
|
var edPublic = ctx.store.proxy.edPublic;
|
|
var title, href;
|
|
if (!res.some((function(obj) {
|
|
if (obj.data && Array.isArray(obj.data.owners) && obj.data.owners.indexOf(edPublic) !== -1 && obj.data.href) {
|
|
href = obj.data.href;
|
|
title = obj.data.filename || obj.data.title;
|
|
return true;
|
|
}
|
|
}))) {
|
|
return void cb(true);
|
|
}
|
|
content.title = title;
|
|
content.href = href;
|
|
cb(false);
|
|
};
|
|
handlers["GIVE_PAD_ACCESS"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var channel = content.channel;
|
|
var res = ctx.store.manager.findChannel(channel, true);
|
|
var title;
|
|
res.forEach((function(obj) {
|
|
if (obj.data && !obj.data.href) {
|
|
if (!title) {
|
|
title = obj.data.filename || obj.data.title;
|
|
}
|
|
obj.userObject.setHref(channel, null, content.href);
|
|
}
|
|
}));
|
|
content.title = title || content.title;
|
|
cb(false);
|
|
};
|
|
handlers["ADD_TO_ACCESS_LIST"] = function(ctx, common, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var channel = content.channel;
|
|
ctx.Store.getAllStores().forEach((function(store) {
|
|
var res = store.manager.findChannel(channel);
|
|
if (!res.length) {
|
|
return;
|
|
}
|
|
var data = res[0].data;
|
|
var id = res[0].id;
|
|
var teamId = store.id;
|
|
ctx.Store.loadSharedFolder(teamId, id, data, (function() {}), false);
|
|
}));
|
|
cb(true);
|
|
};
|
|
var addOwners = {};
|
|
handlers["ADD_OWNER"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
if (!content.teamChannel && !(content.href && content.title && content.channel)) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel || content.teamChannel;
|
|
if (content.password) {
|
|
content.pw = content.password;
|
|
content.password = encryptPassword(ctx, content.password);
|
|
}
|
|
if (addOwners[channel]) {
|
|
return void cb(true);
|
|
}
|
|
addOwners[channel] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false);
|
|
};
|
|
removeHandlers["ADD_OWNER"] = function(ctx, box, data) {
|
|
var channel = data.content.channel || data.content.teamChannel;
|
|
if (addOwners[channel]) {
|
|
delete addOwners[channel];
|
|
}
|
|
};
|
|
handlers["RM_OWNER"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.channel && !content.teamChannel) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel || content.teamChannel;
|
|
if (content.teamChannel) {
|
|
var teams = ctx.store.proxy.teams || {};
|
|
Object.keys(teams).some((function(id) {
|
|
if (teams[id].channel === channel) {
|
|
teams[id].owner = false;
|
|
return true;
|
|
}
|
|
}));
|
|
}
|
|
if (addOwners[channel] && content.pending) {
|
|
return void cb(false, addOwners[channel]);
|
|
}
|
|
cb(false);
|
|
};
|
|
var invitedTo = {};
|
|
handlers["INVITE_TO_TEAM"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
if (!content.team) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var invited = invitedTo[content.team.channel];
|
|
if (invited) {
|
|
console.log("removing old invitation");
|
|
cb(false, invited);
|
|
invitedTo[content.team.channel] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
return;
|
|
}
|
|
var myTeams = Util.find(ctx, [ "store", "proxy", "teams" ]) || {};
|
|
var alreadyMember = Object.keys(myTeams).some((function(k) {
|
|
var team = myTeams[k];
|
|
return team.channel === content.team.channel;
|
|
}));
|
|
if (alreadyMember) {
|
|
return void cb(true);
|
|
}
|
|
invitedTo[content.team.channel] = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false);
|
|
};
|
|
removeHandlers["INVITE_TO_TEAM"] = function(ctx, box, data) {
|
|
var channel = Util.find(data, [ "content", "team", "channel" ]);
|
|
delete invitedTo[channel];
|
|
};
|
|
handlers["KICKED_FROM_TEAM"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.teamChannel) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
if (invitedTo[content.teamChannel] && content.pending) {
|
|
return void cb(true, invitedTo[content.teamChannel]);
|
|
}
|
|
cb(false);
|
|
};
|
|
handlers["INVITE_TO_TEAM_ANSWER"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.teamChannel) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var myTeams = Util.find(ctx, [ "store", "proxy", "teams" ]) || {};
|
|
var teamId;
|
|
var team;
|
|
Object.keys(myTeams).some((function(k) {
|
|
var _team = myTeams[k];
|
|
if (_team.channel === content.teamChannel) {
|
|
teamId = k;
|
|
team = _team;
|
|
return true;
|
|
}
|
|
}));
|
|
if (!teamId) {
|
|
return void cb(true);
|
|
}
|
|
content.team = team;
|
|
if (!content.answer) {
|
|
try {
|
|
var module = ctx.store.modules["team"];
|
|
module.removeFromTeam(teamId, msg.author, true);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
var userData = content.user || content;
|
|
box.sendMessage({
|
|
type: "INVITE_TO_TEAM_ANSWERED",
|
|
content: {
|
|
user: userData,
|
|
team,
|
|
answer: content.answer
|
|
}
|
|
}, (function() {}));
|
|
cb(true);
|
|
};
|
|
handlers["TEAM_EDIT_RIGHTS"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.teamData) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var myTeams = Util.find(ctx, [ "store", "proxy", "teams" ]) || {};
|
|
var teamId;
|
|
Object.keys(myTeams).some((function(k) {
|
|
var _team = myTeams[k];
|
|
if (_team.channel === content.teamData.channel) {
|
|
teamId = k;
|
|
return true;
|
|
}
|
|
}));
|
|
if (!teamId) {
|
|
return void cb(true);
|
|
}
|
|
try {
|
|
var module = ctx.store.modules["team"];
|
|
module.changeMyRights(teamId, content.state, content.teamData, (function(done) {
|
|
if (!done) {
|
|
console.error("Can't update team rights");
|
|
}
|
|
cb(true);
|
|
}));
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
handlers["OWNED_PAD_REMOVED"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.channel) {
|
|
console.log("Remove invalid notification");
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel;
|
|
var res = ctx.store.manager.findChannel(channel);
|
|
res.forEach((function(obj) {
|
|
var paths = ctx.store.manager.findFile(obj.id);
|
|
ctx.store.manager.delete({
|
|
paths
|
|
}, (function() {
|
|
ctx.updateDrive();
|
|
}));
|
|
}));
|
|
cb(true);
|
|
};
|
|
handlers["MOVE_TODO"] = function(ctx, box, data, cb) {
|
|
var curve = ctx.store.proxy.curvePublic;
|
|
if (data.msg.author !== curve) {
|
|
return void cb(true);
|
|
}
|
|
cb();
|
|
};
|
|
handlers["SAFE_LINKS_DEFAULT"] = function(ctx, box, data, cb) {
|
|
var curve = ctx.store.proxy.curvePublic;
|
|
if (data.msg.author !== curve) {
|
|
return void cb(true);
|
|
}
|
|
cb();
|
|
};
|
|
var formNotifs = {};
|
|
handlers["FORM_RESPONSE"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var hash = data.hash;
|
|
var content = msg.content;
|
|
var channel = content.channel;
|
|
if (!channel) {
|
|
return void cb(true);
|
|
}
|
|
if (isChannelMuted(ctx, channel)) {
|
|
return void cb(true);
|
|
}
|
|
var title, href;
|
|
ctx.Store.getAllStores().some((function(s) {
|
|
var res = s.manager.findChannel(channel);
|
|
return res.some((function(obj) {
|
|
if (!obj.data) {
|
|
return;
|
|
}
|
|
if (href && !obj.data.href) {
|
|
return;
|
|
}
|
|
href = obj.data.href || obj.data.roHref;
|
|
title = obj.data.filename || obj.data.title;
|
|
if (obj.data.href) {
|
|
return true;
|
|
}
|
|
}));
|
|
}));
|
|
if (!href) {
|
|
return void cb(true);
|
|
}
|
|
content.href = href;
|
|
content.title = title;
|
|
var old = formNotifs[channel];
|
|
var toRemove = old ? old.data : undefined;
|
|
formNotifs[channel] = {
|
|
data: {
|
|
type: box.type,
|
|
hash
|
|
}
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["FORM_RESPONSE"] = function(ctx, box, data, hash) {
|
|
var content = data.content;
|
|
var channel = content.channel;
|
|
var old = formNotifs[channel];
|
|
if (old && old.data && old.data.hash === hash) {
|
|
delete formNotifs[channel];
|
|
}
|
|
};
|
|
var comments = {};
|
|
handlers["COMMENT_REPLY"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var hash = data.hash;
|
|
var content = msg.content;
|
|
if (Util.find(ctx.store.proxy, [ "settings", "pad", "disableNotif" ])) {
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel;
|
|
if (!channel) {
|
|
return void cb(true);
|
|
}
|
|
var title, href;
|
|
ctx.Store.getAllStores().some((function(s) {
|
|
var res = s.manager.findChannel(channel);
|
|
return res.some((function(obj) {
|
|
if (!obj.data) {
|
|
return;
|
|
}
|
|
if (href && !obj.data.href) {
|
|
return;
|
|
}
|
|
href = obj.data.href || obj.data.roHref;
|
|
title = obj.data.filename || obj.data.title;
|
|
if (obj.data.href) {
|
|
return true;
|
|
}
|
|
}));
|
|
}));
|
|
if (!href) {
|
|
return void cb(true);
|
|
}
|
|
content.href = href;
|
|
content.title = title;
|
|
var old = comments[channel];
|
|
var toRemove = old ? old.data : undefined;
|
|
comments[channel] = {
|
|
data: {
|
|
type: box.type,
|
|
hash
|
|
}
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["COMMENT_REPLY"] = function(ctx, box, data, hash) {
|
|
var content = data.content;
|
|
var channel = content.channel;
|
|
var old = comments[channel];
|
|
if (old && old.data && old.data.hash === hash) {
|
|
delete comments[channel];
|
|
}
|
|
};
|
|
var mentions = {};
|
|
handlers["MENTION"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var hash = data.hash;
|
|
var content = msg.content;
|
|
if (isMuted(ctx, data)) {
|
|
return void cb(true);
|
|
}
|
|
var channel = content.channel;
|
|
if (!channel) {
|
|
return void cb(true);
|
|
}
|
|
var title, href;
|
|
ctx.Store.getAllStores().some((function(s) {
|
|
var res = s.manager.findChannel(channel);
|
|
return res.some((function(obj) {
|
|
if (!obj.data) {
|
|
return;
|
|
}
|
|
if (href && !obj.data.href) {
|
|
return;
|
|
}
|
|
href = obj.data.href || obj.data.roHref;
|
|
title = obj.data.filename || obj.data.title;
|
|
if (obj.data.href) {
|
|
return true;
|
|
}
|
|
}));
|
|
}));
|
|
content.href = href;
|
|
content.title = title;
|
|
var old = mentions[channel];
|
|
var toRemove = old ? old.data : undefined;
|
|
mentions[channel] = {
|
|
data: {
|
|
type: box.type,
|
|
hash
|
|
}
|
|
};
|
|
cb(false, toRemove);
|
|
};
|
|
removeHandlers["MENTION"] = function(ctx, box, data, hash) {
|
|
var content = data.content;
|
|
var channel = content.channel;
|
|
var old = mentions[channel];
|
|
if (old && old.data && old.data.hash === hash) {
|
|
delete mentions[channel];
|
|
}
|
|
};
|
|
handlers["BROADCAST_MAINTENANCE"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var uid = msg.uid;
|
|
ctx.Store.onMaintenanceUpdate(uid);
|
|
cb(true);
|
|
};
|
|
var activeSurvey;
|
|
handlers["BROADCAST_SURVEY"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var uid = msg.uid;
|
|
var old = activeSurvey;
|
|
activeSurvey = {
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
ctx.Store.onSurveyUpdate(uid);
|
|
var dismiss = !content.url;
|
|
cb(dismiss, old);
|
|
};
|
|
var activeCustom;
|
|
handlers["BROADCAST_CUSTOM"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var uid = msg.uid;
|
|
var old = activeCustom;
|
|
activeCustom = {
|
|
uid,
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false, old);
|
|
};
|
|
handlers["BROADCAST_DELETE"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var uid = content.uid;
|
|
if (activeCustom && activeCustom.uid === uid) {
|
|
cb(true, activeCustom);
|
|
activeCustom = undefined;
|
|
return;
|
|
}
|
|
cb(true);
|
|
};
|
|
var sfDeleted = {};
|
|
handlers["SF_DELETED"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var teamId = content.team;
|
|
var sfId = content.sfId;
|
|
if (sfDeleted[sfId]) {
|
|
return void cb(true);
|
|
}
|
|
sfDeleted[sfId] = 1;
|
|
if (!teamId) {
|
|
return void cb(false);
|
|
}
|
|
var team = ctx.store.proxy.teams[teamId];
|
|
content.teamName = team.metadata && team.metadata.name;
|
|
cb(false);
|
|
};
|
|
removeHandlers["SF_DELETED"] = function(ctx, box, data) {
|
|
var id = data.content.sfId;
|
|
delete sfDeleted[id];
|
|
};
|
|
handlers["NEW_TICKET"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.time) {
|
|
content.time = data.time;
|
|
}
|
|
var support = Util.find(ctx, [ "store", "modules", "support" ]);
|
|
if (content.isAdmin) {
|
|
support.addUserTicket(content, cb);
|
|
}
|
|
support.addAdminTicket(content, cb);
|
|
};
|
|
var supportNotif, adminSupportNotif;
|
|
handlers["NOTIF_TICKET"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
if (!content.time) {
|
|
content.time = data.time;
|
|
}
|
|
var support = Util.find(ctx, [ "store", "modules", "support" ]);
|
|
if (content.isAdmin) {
|
|
let exists = Util.find(ctx, [ "store", "proxy", "support", content.channel ]);
|
|
if (!exists) {
|
|
return void cb(true);
|
|
}
|
|
support.updateUserTicket(content);
|
|
if (supportNotif) {
|
|
return void cb(false, supportNotif);
|
|
}
|
|
supportNotif = {
|
|
channel: content.channel,
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
return void cb(false);
|
|
}
|
|
support.checkAdminTicket(content, (exists => {
|
|
if (!exists) {
|
|
return void cb(true);
|
|
}
|
|
support.updateAdminTicket(content);
|
|
if (Util.find(ctx.store.proxy, [ "settings", "general", "disableSupportNotif" ])) {
|
|
return void cb(true);
|
|
}
|
|
if (adminSupportNotif) {
|
|
return void cb(false, adminSupportNotif);
|
|
}
|
|
adminSupportNotif = {
|
|
channel: content.channel,
|
|
type: box.type,
|
|
hash: data.hash
|
|
};
|
|
cb(false);
|
|
}));
|
|
};
|
|
removeHandlers["NOTIF_TICKET"] = function(ctx, box, data) {
|
|
var id = data.content.channel;
|
|
if (supportNotif && supportNotif.channel === id) {
|
|
supportNotif = undefined;
|
|
}
|
|
if (adminSupportNotif && adminSupportNotif.channel === id) {
|
|
adminSupportNotif = undefined;
|
|
}
|
|
};
|
|
handlers["ADD_MODERATOR"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var support = Util.find(ctx, [ "store", "modules", "support" ]);
|
|
support.updateAdminKey(content, cb);
|
|
};
|
|
handlers["MODERATOR_NEW_KEY"] = function(ctx, box, data, cb) {
|
|
var msg = data.msg;
|
|
var content = msg.content;
|
|
var support = Util.find(ctx, [ "store", "modules", "support" ]);
|
|
support.updateAdminKey(content, (function() {
|
|
cb(true);
|
|
}));
|
|
};
|
|
return {
|
|
add: function(ctx, box, data, cb) {
|
|
if (!data.msg) {
|
|
return void cb(null, null, true);
|
|
}
|
|
var myCurve = Util.find(ctx, [ "store", "proxy", "curvePublic" ]);
|
|
var curve = Util.find(data, [ "msg", "content", "user", "curvePublic" ]) || Util.find(data, [ "msg", "content", "curvePublic" ]);
|
|
if (curve && data.msg.author !== curve && data.msg.author !== myCurve) {
|
|
console.error("blocked");
|
|
return void cb(null, null, true);
|
|
}
|
|
var type = data.msg.type;
|
|
if (handlers[type]) {
|
|
try {
|
|
handlers[type](ctx, box, data, cb);
|
|
} catch (e) {
|
|
console.error(e);
|
|
cb();
|
|
}
|
|
} else {
|
|
cb();
|
|
}
|
|
},
|
|
remove: function(ctx, box, data, h) {
|
|
if (!data) {
|
|
return;
|
|
}
|
|
var type = data.type;
|
|
if (removeHandlers[type]) {
|
|
try {
|
|
removeHandlers[type](ctx, box, data, h);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonMessaging(), requireCommonHash(), requireCommonUtil(), requireCrypto(), requireLoginBlock());
|
|
}
|
|
})();
|
|
})(mailboxHandlers);
|
|
return mailboxHandlers.exports;
|
|
}
|
|
var hasRequiredMailbox;
|
|
function requireMailbox() {
|
|
if (hasRequiredMailbox) return mailbox$1.exports;
|
|
hasRequiredMailbox = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (BCast = {}, Util, Hash, Realtime, Messaging, Notify, Handlers, CpNetflux, Crypto) => {
|
|
var Mailbox = {};
|
|
Mailbox.setCustomize = data => {
|
|
BCast = data.Broadcast;
|
|
};
|
|
var TYPES = [ "notifications", "supportteam", "broadcast" ];
|
|
var BLOCKING_TYPES = [];
|
|
var BROADCAST_CHAN = "000000000000000000000000000000000";
|
|
var initializeMailboxes = function(ctx, mailboxes) {
|
|
if (!mailboxes["notifications"] && ctx.loggedIn) {
|
|
mailboxes.notifications = {
|
|
channel: Hash.createChannelId(),
|
|
lastKnownHash: "",
|
|
viewed: []
|
|
};
|
|
ctx.pinPads([ mailboxes.notifications.channel ], (function(res) {
|
|
if (res.error) {
|
|
console.error(res);
|
|
}
|
|
}));
|
|
}
|
|
if (mailboxes.support) {
|
|
delete mailboxes.support;
|
|
}
|
|
if (!mailboxes["broadcast"]) {
|
|
mailboxes.broadcast = {
|
|
channel: BROADCAST_CHAN,
|
|
lastKnownHash: BCast.lastBroadcastHash,
|
|
decrypted: true,
|
|
viewed: []
|
|
};
|
|
}
|
|
};
|
|
var isMessageNew = function(hash, m) {
|
|
return (m.viewed || []).indexOf(hash) === -1 && hash !== m.lastKnownHash;
|
|
};
|
|
var showMessage = function(ctx, type, msg, cId, cb) {
|
|
ctx.emit("MESSAGE", {
|
|
type,
|
|
content: msg
|
|
}, cId ? [ cId ] : ctx.clients, cb);
|
|
};
|
|
var hideMessage = function(ctx, type, hash, clients) {
|
|
ctx.emit("VIEWED", {
|
|
type,
|
|
hash
|
|
}, clients || ctx.clients);
|
|
};
|
|
var getMyKeys = function(ctx) {
|
|
var proxy = ctx.store && ctx.store.proxy;
|
|
if (!proxy.curvePrivate || !proxy.curvePublic) {
|
|
return;
|
|
}
|
|
return {
|
|
curvePrivate: proxy.curvePrivate,
|
|
curvePublic: proxy.curvePublic
|
|
};
|
|
};
|
|
var sendTo = Mailbox.sendTo = function(ctx, type, msg, user, _cb) {
|
|
user = user || {};
|
|
var cb = _cb || function(obj) {
|
|
if (obj && obj.error) {
|
|
console.error(obj.error);
|
|
}
|
|
};
|
|
if (!Crypto.Mailbox) {
|
|
return void cb({
|
|
error: "chainpad-crypto is outdated and doesn't support mailboxes."
|
|
});
|
|
}
|
|
var anonRpc = Util.find(ctx, [ "store", "anon_rpc" ]);
|
|
if (!anonRpc) {
|
|
return void cb({
|
|
error: "anonymous rpc session not ready"
|
|
});
|
|
}
|
|
var crypto = {
|
|
encrypt: function(x) {
|
|
return x;
|
|
}
|
|
};
|
|
var channel = BROADCAST_CHAN;
|
|
var obj = {
|
|
uid: Util.uid(),
|
|
type,
|
|
content: msg
|
|
};
|
|
if (!/^BROADCAST/.test(type)) {
|
|
var keys = getMyKeys(ctx);
|
|
if (!keys) {
|
|
return void cb({
|
|
error: "missing asymmetric encryption keys"
|
|
});
|
|
}
|
|
if (!user || !user.channel || !user.curvePublic) {
|
|
return void cb({
|
|
error: "no notification channel"
|
|
});
|
|
}
|
|
channel = user.channel;
|
|
crypto = Crypto.Mailbox.createEncryptor(keys);
|
|
if (typeof msg === "object" && !msg.user) {
|
|
var myData = Messaging.createData(ctx.store.proxy, false);
|
|
msg.user = myData;
|
|
}
|
|
obj = {
|
|
type,
|
|
content: msg
|
|
};
|
|
}
|
|
var text = JSON.stringify(obj);
|
|
var ciphertext = crypto.encrypt(text, user.curvePublic);
|
|
if (user.viewed) {
|
|
var team = Util.find(ctx, [ "store", "proxy", "teams", user.viewed ]);
|
|
if (team) {
|
|
var hash = ciphertext.slice(0, 64);
|
|
var viewed = Util.find(team, [ "keys", "mailbox", "viewed" ]);
|
|
if (Array.isArray(viewed)) {
|
|
viewed.push(hash);
|
|
}
|
|
}
|
|
}
|
|
anonRpc.send("WRITE_PRIVATE_MESSAGE", [ channel, ciphertext ], (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
return void cb({
|
|
hash: ciphertext.slice(0, 64)
|
|
});
|
|
}));
|
|
};
|
|
Mailbox.sendToAnon = function(anonRpc, type, msg, user, cb) {
|
|
var Nacl = Crypto.Nacl;
|
|
var curveSeed = Nacl.randomBytes(32);
|
|
var curvePair = Nacl.box.keyPair.fromSecretKey(new Uint8Array(curveSeed));
|
|
var curvePrivate = Nacl.util.encodeBase64(curvePair.secretKey);
|
|
var curvePublic = Nacl.util.encodeBase64(curvePair.publicKey);
|
|
sendTo({
|
|
store: {
|
|
anon_rpc: anonRpc,
|
|
proxy: {
|
|
curvePrivate,
|
|
curvePublic
|
|
}
|
|
}
|
|
}, type, msg, user, cb);
|
|
};
|
|
var dismiss = function(ctx, data, cId, cb) {
|
|
var type = data.type;
|
|
var hash = data.hash;
|
|
if (/^REMINDER\|/.test(hash)) {
|
|
cb();
|
|
delete ctx.boxes.reminders.content[hash];
|
|
hideMessage(ctx, type, hash, ctx.clients.filter((function(clientId) {
|
|
return clientId !== cId;
|
|
})));
|
|
var uid = hash.slice(9).split("-")[0];
|
|
var d = Util.find(ctx, [ "store", "proxy", "hideReminders", uid ]);
|
|
if (!d) {
|
|
var h = ctx.store.proxy.hideReminders = ctx.store.proxy.hideReminders || {};
|
|
d = h[uid] = h[uid] || [];
|
|
}
|
|
var delay = hash.split("-")[1];
|
|
if (delay && !d.includes(delay)) {
|
|
d.push(Number(delay));
|
|
}
|
|
return;
|
|
}
|
|
var box = ctx.boxes[type];
|
|
if (!box) {
|
|
return void cb({
|
|
error: "NOT_LOADED"
|
|
});
|
|
}
|
|
var m = box.data || {};
|
|
var idx = box.history.indexOf(hash);
|
|
if (idx !== -1) {
|
|
if (idx === 0) {
|
|
m.lastKnownHash = hash;
|
|
box.history.shift();
|
|
} else if (m.viewed.indexOf(hash) === -1) {
|
|
m.viewed.push(hash);
|
|
}
|
|
}
|
|
var sliceIdx;
|
|
var lastKnownHash;
|
|
var toForget = [];
|
|
box.history.some((function(hash, i) {
|
|
var isViewed = m.viewed.indexOf(hash);
|
|
if (isViewed === -1) {
|
|
return true;
|
|
}
|
|
sliceIdx = i + 1;
|
|
toForget.push(hash);
|
|
lastKnownHash = hash;
|
|
}));
|
|
m.viewed = m.viewed.filter((function(hash) {
|
|
return toForget.indexOf(hash) === -1;
|
|
}));
|
|
if (sliceIdx) {
|
|
box.history = box.history.slice(sliceIdx);
|
|
m.lastKnownHash = lastKnownHash;
|
|
}
|
|
Object.keys(box.content).forEach((function(h) {
|
|
if (box.history.indexOf(h) === -1 || m.viewed.indexOf(h) !== -1) {
|
|
Handlers.remove(ctx, box, box.content[h], h);
|
|
delete box.content[h];
|
|
}
|
|
}));
|
|
Realtime.whenRealtimeSyncs(ctx.store.realtime, (function() {
|
|
cb();
|
|
hideMessage(ctx, type, hash, ctx.clients.filter((function(clientId) {
|
|
return clientId !== cId;
|
|
})));
|
|
}));
|
|
};
|
|
var leaveChannel = function(ctx, type, cb) {
|
|
cb = cb || function() {};
|
|
var box = ctx.boxes[type];
|
|
if (!box) {
|
|
return void cb();
|
|
}
|
|
if (!box.cpNf || typeof box.cpNf.stop !== "function") {
|
|
return void cb("EINVAL");
|
|
}
|
|
box.cpNf.stop();
|
|
Object.keys(box.content).forEach((function(h) {
|
|
Handlers.remove(ctx, box, box.content[h], h);
|
|
hideMessage(ctx, type, h, ctx.clients);
|
|
}));
|
|
delete ctx.boxes[type];
|
|
};
|
|
var openChannel = function(ctx, type, m, onReady, opts) {
|
|
opts = opts || {};
|
|
var box = ctx.boxes[type] = {
|
|
channel: m.channel,
|
|
type,
|
|
queue: [],
|
|
history: [],
|
|
content: {},
|
|
sendMessage: function(msg) {
|
|
if (typeof msg === "object" && !msg.user) {
|
|
var myData = Messaging.createData(ctx.store.proxy, false);
|
|
msg.user = myData;
|
|
}
|
|
try {
|
|
msg = JSON.stringify(msg);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
box.queue.push(msg);
|
|
},
|
|
data: m
|
|
};
|
|
if (!Crypto.Mailbox) {
|
|
return void console.error("chainpad-crypto is outdated and doesn't support mailboxes.");
|
|
}
|
|
var keys = m.keys || getMyKeys(ctx);
|
|
if (!keys && !m.decrypted) {
|
|
return void console.error("missing asymmetric encryption keys");
|
|
}
|
|
var crypto = m.decrypted ? {
|
|
encrypt: function(x) {
|
|
return x;
|
|
},
|
|
decrypt: function(x) {
|
|
return x;
|
|
}
|
|
} : Crypto.Mailbox.createEncryptor(keys);
|
|
box.encryptor = crypto;
|
|
var cfg = {
|
|
network: ctx.store.network,
|
|
channel: m.channel,
|
|
noChainPad: true,
|
|
crypto,
|
|
owners: type === "broadcast" ? [] : opts.owners || [ ctx.store.proxy.edPublic ],
|
|
lastKnownHash: m.lastKnownHash
|
|
};
|
|
cfg.onConnectionChange = function() {};
|
|
cfg.onConnect = function(wc, sendMessage) {
|
|
box.sendMessage = function(_msg, cb) {
|
|
cb = cb || function() {};
|
|
var msg;
|
|
try {
|
|
msg = JSON.stringify(_msg);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
sendMessage(msg, (function(err, hash) {
|
|
if (err) {
|
|
return void console.error(err);
|
|
}
|
|
box.history.push(hash);
|
|
_msg.ctime = +new Date;
|
|
box.content[hash] = _msg;
|
|
var message = {
|
|
msg: _msg,
|
|
hash
|
|
};
|
|
showMessage(ctx, type, message);
|
|
cb(hash);
|
|
}), keys.curvePublic);
|
|
};
|
|
box.queue.forEach((function(msg) {
|
|
box.sendMessage(msg);
|
|
}));
|
|
box.queue = [];
|
|
};
|
|
var lastReceivedHash;
|
|
box.onMessage = cfg.onMessage = function(msg, user, vKey, isCp, hash, author, data) {
|
|
if (hash === m.lastKnownHash) {
|
|
return;
|
|
}
|
|
if (hash === lastReceivedHash) {
|
|
return;
|
|
}
|
|
var time = data && data.time;
|
|
lastReceivedHash = hash;
|
|
try {
|
|
msg = JSON.parse(msg);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
if (author) {
|
|
msg.author = author;
|
|
}
|
|
box.history.push(hash);
|
|
if (isMessageNew(hash, m)) {
|
|
var message = {
|
|
msg,
|
|
hash,
|
|
time
|
|
};
|
|
var notify = box.ready;
|
|
Handlers.add(ctx, box, message, (function(dismissed, toDismiss, invalid) {
|
|
if (toDismiss) {
|
|
dismiss(ctx, toDismiss, "", (function() {
|
|
console.log("Notification handled automatically");
|
|
}));
|
|
}
|
|
if (invalid || dismissed) {
|
|
dismiss(ctx, {
|
|
type,
|
|
hash
|
|
}, "", (function() {
|
|
console.log("Notification handled automatically");
|
|
}));
|
|
return;
|
|
}
|
|
msg.ctime = time || 0;
|
|
box.content[hash] = msg;
|
|
if (opts.dump) {
|
|
return;
|
|
}
|
|
showMessage(ctx, type, message, null, (function(obj) {
|
|
if (!obj || !obj.msg || !notify) {
|
|
return;
|
|
}
|
|
Notify.system(undefined, obj.msg);
|
|
}));
|
|
}));
|
|
} else {
|
|
if (Object.keys(box.content).length === 0) {
|
|
m.lastKnownHash = hash;
|
|
box.history = [];
|
|
var idxViewed = m.viewed.indexOf(hash);
|
|
if (idxViewed !== -1) {
|
|
m.viewed.splice(idxViewed, 1);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
cfg.onReady = function() {
|
|
var toClean = [];
|
|
m.viewed.forEach((function(h, i) {
|
|
if (box.history.indexOf(h) === -1) {
|
|
toClean.push(i);
|
|
}
|
|
}));
|
|
for (var i = toClean.length - 1; i >= 0; i--) {
|
|
m.viewed.splice(toClean[i], 1);
|
|
}
|
|
var view = function(h) {
|
|
Handlers.remove(ctx, box, box.content[h], h);
|
|
delete box.content[h];
|
|
hideMessage(ctx, type, h);
|
|
};
|
|
ctx.store.proxy.on("change", [ "mailboxes", type ], (function(o, n, p) {
|
|
if (p[2] === "lastKnownHash") {
|
|
var sliceIdx;
|
|
box.history.some((function(h, i) {
|
|
sliceIdx = i + 1;
|
|
view(h);
|
|
if (h === n) {
|
|
return true;
|
|
}
|
|
}));
|
|
box.history = box.history.slice(sliceIdx);
|
|
}
|
|
if (p[2] === "viewed") {
|
|
view(n);
|
|
}
|
|
}));
|
|
box.ready = true;
|
|
onReady(box.content);
|
|
};
|
|
box.cpNf = CpNetflux.start(cfg);
|
|
};
|
|
var initializeHistory = function(ctx) {
|
|
var network = ctx.store.network;
|
|
network.on("message", (function(msg, sender) {
|
|
if (sender !== network.historyKeeper) {
|
|
return;
|
|
}
|
|
var parsed = JSON.parse(msg);
|
|
if (!/HISTORY_RANGE/.test(parsed[0])) {
|
|
return;
|
|
}
|
|
var txid = parsed[1];
|
|
var req = ctx.req[txid];
|
|
if (!req) {
|
|
return;
|
|
}
|
|
var type = parsed[0];
|
|
var _msg = parsed[2];
|
|
var box = req.box;
|
|
if (type === "HISTORY_RANGE") {
|
|
if (!Array.isArray(_msg)) {
|
|
return;
|
|
}
|
|
var message;
|
|
if (req.box.type === "broadcast") {
|
|
message = Util.tryParse(_msg[4]);
|
|
} else {
|
|
try {
|
|
var decrypted = box.encryptor.decrypt(_msg[4]);
|
|
message = JSON.parse(decrypted.content);
|
|
message.author = decrypted.author;
|
|
} catch (e) {
|
|
console.log(e);
|
|
}
|
|
}
|
|
var hash = _msg[4].slice(0, 64);
|
|
ctx.emit("HISTORY", {
|
|
txid,
|
|
time: _msg[5],
|
|
message,
|
|
hash
|
|
}, [ req.cId ]);
|
|
} else if (type === "HISTORY_RANGE_END") {
|
|
ctx.emit("HISTORY", {
|
|
txid,
|
|
complete: true
|
|
}, [ req.cId ]);
|
|
delete ctx.req[txid];
|
|
}
|
|
}));
|
|
};
|
|
var loadHistory = function(ctx, clientId, data, cb) {
|
|
var box = ctx.boxes[data.type];
|
|
if (!box) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var msg = [ "GET_HISTORY_RANGE", box.channel, {
|
|
from: data.lastKnownHash,
|
|
count: data.count,
|
|
txid: data.txid
|
|
} ];
|
|
if (data.type === "broadcast") {
|
|
msg = [ "GET_HISTORY_RANGE", box.channel, {
|
|
to: data.lastKnownHash,
|
|
txid: data.txid
|
|
} ];
|
|
}
|
|
ctx.req[data.txid] = {
|
|
cId: clientId,
|
|
box
|
|
};
|
|
var network = ctx.store.network;
|
|
network.sendto(network.historyKeeper, JSON.stringify(msg)).then((function() {}), (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
var subscribe = function(ctx, data, cId, cb) {
|
|
Object.keys(ctx.boxes).forEach((function(type) {
|
|
Object.keys(ctx.boxes[type].content).forEach((function(h) {
|
|
var message = {
|
|
msg: ctx.boxes[type].content[h],
|
|
hash: h
|
|
};
|
|
showMessage(ctx, type, message, cId, (function(obj) {
|
|
if (obj.error) {
|
|
return;
|
|
}
|
|
if (!message.msg || !message.msg.requiresNotif) {
|
|
return;
|
|
}
|
|
Notify.system(undefined, obj.msg);
|
|
delete message.msg.requiresNotif;
|
|
}));
|
|
}));
|
|
}));
|
|
var idx = ctx.clients.indexOf(cId);
|
|
if (idx === -1) {
|
|
ctx.clients.push(cId);
|
|
}
|
|
cb();
|
|
};
|
|
var removeClient = function(ctx, cId) {
|
|
var idx = ctx.clients.indexOf(cId);
|
|
ctx.clients.splice(idx, 1);
|
|
};
|
|
Mailbox.init = function(cfg, waitFor, emit) {
|
|
var mailbox = {};
|
|
var store = cfg.store;
|
|
var mailboxes = store.proxy.mailboxes = store.proxy.mailboxes || {};
|
|
var ctx = {
|
|
Store: cfg.Store,
|
|
store,
|
|
pinPads: cfg.pinPads,
|
|
updateMetadata: cfg.updateMetadata,
|
|
updateDrive: cfg.updateDrive,
|
|
mailboxes,
|
|
emit,
|
|
clients: [],
|
|
boxes: {},
|
|
req: {},
|
|
loggedIn: store.loggedIn && store.proxy.edPublic
|
|
};
|
|
initializeMailboxes(ctx, mailboxes);
|
|
if (ctx.loggedIn) {
|
|
initializeHistory(ctx);
|
|
}
|
|
ctx.boxes.reminders = {
|
|
content: {}
|
|
};
|
|
Object.keys(mailboxes).forEach((function(key) {
|
|
if (TYPES.indexOf(key) === -1) {
|
|
return;
|
|
}
|
|
var m = mailboxes[key];
|
|
if (BLOCKING_TYPES.indexOf(key) === -1) {
|
|
openChannel(ctx, key, m, (function() {}));
|
|
} else {
|
|
openChannel(ctx, key, m, waitFor((function() {})));
|
|
}
|
|
}));
|
|
if (ctx.loggedIn) {
|
|
Object.keys(store.proxy.teams || {}).forEach((function(teamId) {
|
|
var team = store.proxy.teams[teamId];
|
|
if (!team) {
|
|
return;
|
|
}
|
|
var teamMailbox = team.keys.mailbox || {};
|
|
if (!teamMailbox.channel) {
|
|
return;
|
|
}
|
|
var opts = {
|
|
owners: [ Util.find(team, [ "keys", "drive", "edPublic" ]) ]
|
|
};
|
|
openChannel(ctx, "team-" + teamId, teamMailbox, (function() {}), opts);
|
|
}));
|
|
}
|
|
mailbox.post = function(box, type, content) {
|
|
var b = ctx.boxes[box];
|
|
if (!b) {
|
|
return;
|
|
}
|
|
b.sendMessage({
|
|
type,
|
|
content,
|
|
sender: store.proxy.curvePublic
|
|
});
|
|
};
|
|
mailbox.hideMessage = function(type, msg) {
|
|
hideMessage(ctx, type, msg.hash, ctx.clients);
|
|
};
|
|
mailbox.showMessage = function(type, msg, cId, cb) {
|
|
if (type === "reminders" && msg) {
|
|
ctx.boxes.reminders.content[msg.hash] = msg.msg;
|
|
if (!ctx.clients.length) {
|
|
ctx.boxes.reminders.content[msg.hash].requiresNotif = true;
|
|
}
|
|
hideMessage(ctx, type, msg.hash, ctx.clients);
|
|
}
|
|
showMessage(ctx, type, msg, cId, (function(obj) {
|
|
Notify.system(undefined, obj.msg);
|
|
if (cb) {
|
|
cb();
|
|
}
|
|
}));
|
|
};
|
|
mailbox.open = function(key, m, cb, team, opts) {
|
|
if (TYPES.indexOf(key) === -1 && !team) {
|
|
return;
|
|
}
|
|
openChannel(ctx, key, m, cb, opts);
|
|
};
|
|
mailbox.close = function(key, cb) {
|
|
leaveChannel(ctx, key, cb);
|
|
};
|
|
mailbox.dismiss = function(data, cb) {
|
|
dismiss(ctx, data, "", cb);
|
|
};
|
|
mailbox.sendTo = function(type, msg, user, cb) {
|
|
if (!ctx.loggedIn) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
sendTo(ctx, type, msg, user, cb);
|
|
};
|
|
mailbox.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
mailbox.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "SUBSCRIBE") {
|
|
return void subscribe(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DISMISS") {
|
|
return void dismiss(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SENDTO") {
|
|
return void sendTo(ctx, data.type, data.msg, data.user, cb);
|
|
}
|
|
if (cmd === "LOAD_HISTORY") {
|
|
return void loadHistory(ctx, clientId, data, cb);
|
|
}
|
|
};
|
|
return mailbox;
|
|
};
|
|
return Mailbox;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, requireCommonUtil(), requireCommonHash(), requireCommonRealtime(), requireCommonMessaging(), requireNotify(), requireMailboxHandlers(), requireChainpadNetflux(), requireCrypto());
|
|
}
|
|
})();
|
|
})(mailbox$1);
|
|
return mailbox$1.exports;
|
|
}
|
|
var hasRequiredMigrateUserObject;
|
|
function requireMigrateUserObject() {
|
|
if (hasRequiredMigrateUserObject) return migrateUserObject$1.exports;
|
|
hasRequiredMigrateUserObject = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Feedback, Hash, Util, Messaging, Crypt, Mailbox, Messages = {}, Realtime, nThen, Crypto) => {
|
|
const setCustomize = data => {
|
|
Messages = data.Messages;
|
|
};
|
|
let migrate = function(userObject, cb, progress, store) {
|
|
var version = userObject.version || 0;
|
|
nThen((function() {})).nThen((function() {
|
|
var migrateAttributes = function() {
|
|
var drawer = "cryptpad.userlist-drawer";
|
|
var polls = "cryptpad.hide_poll_text";
|
|
var indentKey = "cryptpad.indentUnit";
|
|
var useTabsKey = "cryptpad.indentWithTabs";
|
|
var settings = userObject.settings = userObject.settings || {};
|
|
if (typeof userObject[indentKey] !== "undefined") {
|
|
settings.codemirror = settings.codemirror || {};
|
|
settings.codemirror.indentUnit = userObject[indentKey];
|
|
delete userObject[indentKey];
|
|
}
|
|
if (typeof userObject[useTabsKey] !== "undefined") {
|
|
settings.codemirror = settings.codemirror || {};
|
|
settings.codemirror.indentWithTabs = userObject[useTabsKey];
|
|
delete userObject[useTabsKey];
|
|
}
|
|
if (typeof userObject[drawer] !== "undefined") {
|
|
settings.toolbar = settings.toolbar || {};
|
|
settings.toolbar["userlist-drawer"] = userObject[drawer];
|
|
delete userObject[drawer];
|
|
}
|
|
if (typeof userObject[polls] !== "undefined") {
|
|
settings.poll = settings.poll || {};
|
|
settings.poll["hide-text"] = userObject[polls];
|
|
delete userObject[polls];
|
|
}
|
|
};
|
|
if (version < 2) {
|
|
migrateAttributes();
|
|
Feedback.send("Migrate-2", true);
|
|
userObject.version = version = 2;
|
|
}
|
|
})).nThen((function() {
|
|
var migrateLanguage = function() {
|
|
if (!localStorage.CRYPTPAD_LANG) {
|
|
return;
|
|
}
|
|
var l = localStorage.CRYPTPAD_LANG;
|
|
userObject.settings.language = l;
|
|
};
|
|
if (version < 3) {
|
|
migrateLanguage();
|
|
Feedback.send("Migrate-3", true);
|
|
userObject.version = version = 3;
|
|
}
|
|
})).nThen((function() {
|
|
var migrateFeedback = function() {
|
|
var settings = userObject.settings = userObject.settings || {};
|
|
if (typeof userObject["allowUserFeedback"] !== "undefined") {
|
|
settings.general = settings.general || {};
|
|
settings.general.allowUserFeedback = userObject["allowUserFeedback"];
|
|
delete userObject["allowUserFeedback"];
|
|
}
|
|
};
|
|
if (version < 4) {
|
|
migrateFeedback();
|
|
Feedback.send("Migrate-4", true);
|
|
userObject.version = version = 4;
|
|
}
|
|
})).nThen((function() {
|
|
var migrateDates = function() {
|
|
var data = userObject.drive && userObject.drive.filesData;
|
|
if (data) {
|
|
for (var id in data) {
|
|
if (typeof data[id].ctime !== "number") {
|
|
data[id].ctime = +new Date(data[id].ctime);
|
|
}
|
|
if (typeof data[id].atime !== "number") {
|
|
data[id].atime = +new Date(data[id].atime);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
if (version < 5) {
|
|
migrateDates();
|
|
Feedback.send("Migrate-5", true);
|
|
userObject.version = version = 5;
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
var addChannelId = function() {
|
|
var data = userObject.drive.filesData || {};
|
|
var el, parsed;
|
|
var n = nThen((function() {}));
|
|
var padsLength = Object.keys(data).length;
|
|
Object.keys(data).forEach((function(k, i) {
|
|
n = n.nThen((function(w) {
|
|
setTimeout(w((function() {
|
|
el = data[k];
|
|
parsed = Hash.parsePadUrl(el.href);
|
|
if (!el.href) {
|
|
return;
|
|
}
|
|
if (!el.channel) {
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
|
el.channel = secret.channel;
|
|
progress(6, Math.round(100 * i / padsLength));
|
|
console.log("Adding missing channel in filesData ", el.channel);
|
|
}
|
|
})));
|
|
}));
|
|
}));
|
|
n.nThen(waitFor((function() {
|
|
Feedback.send("Migrate-6", true);
|
|
userObject.version = version = 6;
|
|
})));
|
|
};
|
|
if (version < 6) {
|
|
addChannelId();
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
var addRoHref = function() {
|
|
var data = userObject.drive.filesData;
|
|
var el, parsed;
|
|
var n = nThen((function() {}));
|
|
var padsLength = Object.keys(data).length;
|
|
Object.keys(data).forEach((function(k, i) {
|
|
n = n.nThen((function(w) {
|
|
setTimeout(w((function() {
|
|
el = data[k];
|
|
if (!el.href) {
|
|
return void progress(7, Math.round(100 * i / padsLength));
|
|
}
|
|
if (el.href.indexOf("#") === -1) {
|
|
return void progress(7, Math.round(100 * i / padsLength));
|
|
}
|
|
parsed = Hash.parsePadUrl(el.href);
|
|
if (parsed.hashData.type !== "pad") {
|
|
return void progress(7, Math.round(100 * i / padsLength));
|
|
}
|
|
if (parsed.hashData.mode === "view") {
|
|
el.roHref = el.href;
|
|
delete el.href;
|
|
console.log("Move href to roHref in filesData ", el.roHref);
|
|
} else {
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, el.password);
|
|
var hash = Hash.getViewHashFromKeys(secret);
|
|
if (hash) {
|
|
el.roHref = "/" + parsed.type + "/#" + hash;
|
|
console.log("Adding missing roHref in filesData ", el.href);
|
|
}
|
|
}
|
|
progress(6, Math.round(100 * i / padsLength));
|
|
})));
|
|
}));
|
|
}));
|
|
n.nThen(waitFor((function() {
|
|
Feedback.send("Migrate-7", true);
|
|
userObject.version = version = 7;
|
|
})));
|
|
};
|
|
if (version < 7) {
|
|
addRoHref();
|
|
}
|
|
})).nThen((function() {
|
|
var fixDuplicate = function() {
|
|
userObject.FS_hashes = Util.deduplicateString(userObject.FS_hashes || []);
|
|
};
|
|
if (version < 8) {
|
|
fixDuplicate();
|
|
Feedback.send("Migrate-8", true);
|
|
userObject.version = version = 8;
|
|
}
|
|
})).nThen((function() {
|
|
var migrateFriends = function() {
|
|
var network = store.network;
|
|
var channels = {};
|
|
var ctx = {
|
|
store
|
|
};
|
|
var myData = Messaging.createData(userObject);
|
|
var close = function(chan) {
|
|
var channel = channels[chan];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
try {
|
|
channel.wc.leave();
|
|
} catch (e) {}
|
|
delete channels[chan];
|
|
};
|
|
var onDirectMessage = function(msg, sender) {
|
|
if (sender !== network.historyKeeper) {
|
|
return;
|
|
}
|
|
var parsed = JSON.parse(msg);
|
|
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
|
|
return;
|
|
}
|
|
if (parsed.channel && channels[parsed.channel]) {
|
|
if (parsed.error && parsed.error === "EINVAL") {
|
|
var histMsg = [ "GET_HISTORY", parsed.channel, {} ];
|
|
network.sendto(network.historyKeeper, JSON.stringify(histMsg)).then((function() {}), (function() {}));
|
|
return;
|
|
}
|
|
if (parsed.state && parsed.state === 1) {
|
|
myData.channel = parsed.channel;
|
|
var updateMsg = [ "UPDATE", myData.curvePublic, +new Date, myData ];
|
|
var cryptMsg = channels[parsed.channel].encrypt(JSON.stringify(updateMsg));
|
|
channels[parsed.channel].wc.bcast(cryptMsg).then((function() {}), (function(err) {
|
|
console.error("Can't migrate this friend", channels[parsed.channel].friend, err);
|
|
}));
|
|
close(parsed.channel);
|
|
return;
|
|
}
|
|
} else if (parsed.channel) {
|
|
return;
|
|
}
|
|
var chan = parsed[3];
|
|
if (!chan || !channels[chan]) {
|
|
return;
|
|
}
|
|
var channel = channels[chan];
|
|
var msgIn = channel.decrypt(parsed[4]);
|
|
var parsedMsg = JSON.parse(msgIn);
|
|
if (parsedMsg[0] === "UPDATE") {
|
|
if (parsedMsg[1] === myData.curvePublic) {
|
|
return;
|
|
}
|
|
var data = parsedMsg[3];
|
|
if (!data.notifications) {
|
|
return;
|
|
}
|
|
channel.friend.notifications = data.notifications;
|
|
myData.channel = chan;
|
|
Mailbox.sendTo(ctx, "UPDATE_DATA", myData, {
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void console.error(obj);
|
|
}
|
|
console.log("friend migrated", channel.friend);
|
|
}));
|
|
close(chan);
|
|
}
|
|
};
|
|
network.on("message", (function(msg, sender) {
|
|
try {
|
|
onDirectMessage(msg, sender);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
var friends = userObject.friends || {};
|
|
Object.keys(friends).forEach((function(curve) {
|
|
if (curve.length !== 44) {
|
|
return;
|
|
}
|
|
var friend = friends[curve];
|
|
if (friend.notifications) {
|
|
return;
|
|
}
|
|
network.join(friend.channel).then((function(wc) {
|
|
var keys = Crypto.Curve.deriveKeys(friend.curvePublic, userObject.curvePrivate);
|
|
var encryptor = Crypto.Curve.createEncryptor(keys);
|
|
channels[friend.channel] = {
|
|
wc,
|
|
friend,
|
|
decrypt: encryptor.decrypt,
|
|
encrypt: encryptor.encrypt
|
|
};
|
|
var cfg = {
|
|
lastKnownHash: friend.lastKnownHash
|
|
};
|
|
var msg = [ "GET_HISTORY", friend.channel, cfg ];
|
|
network.sendto(network.historyKeeper, JSON.stringify(msg)).then((function() {}), (function(err) {
|
|
console.error("Can't migrate this friend", friend, err);
|
|
}));
|
|
}), (function(err) {
|
|
console.error("Can't migrate this friend", friend, err);
|
|
}));
|
|
}));
|
|
};
|
|
if (version < 9) {
|
|
migrateFriends();
|
|
Feedback.send("Migrate-9", true);
|
|
userObject.version = version = 9;
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
var fixTodo = function() {
|
|
var h = store.proxy.todo;
|
|
if (!h) {
|
|
return;
|
|
}
|
|
var next = waitFor((function() {
|
|
Feedback.send("Migrate-10", true);
|
|
userObject.version = version = 10;
|
|
}));
|
|
var old;
|
|
var opts = {
|
|
network: store.network,
|
|
initialState: "{}",
|
|
metadata: {
|
|
owners: store.proxy.edPublic ? [ store.proxy.edPublic ] : []
|
|
}
|
|
};
|
|
nThen((function(w) {
|
|
Crypt.get(h, w((function(err, val) {
|
|
if (err || !val) {
|
|
w.abort();
|
|
next();
|
|
return;
|
|
}
|
|
try {
|
|
old = JSON.parse(val);
|
|
} catch (e) {}
|
|
})), opts);
|
|
})).nThen((function(w) {
|
|
if (!old || typeof old !== "object") {
|
|
w.abort();
|
|
next();
|
|
return;
|
|
}
|
|
var k = {
|
|
content: {
|
|
data: {
|
|
1: {
|
|
id: "1",
|
|
color: "color6",
|
|
item: [],
|
|
title: Messages.kanban_todo
|
|
},
|
|
2: {
|
|
id: "2",
|
|
color: "color3",
|
|
item: [],
|
|
title: Messages.kanban_working
|
|
},
|
|
3: {
|
|
id: "3",
|
|
color: "color5",
|
|
item: [],
|
|
title: Messages.kanban_done
|
|
}
|
|
},
|
|
items: {},
|
|
list: [ 1, 2, 3 ]
|
|
},
|
|
metadata: {
|
|
title: Messages.type.todo,
|
|
defaultTitle: Messages.type.todo,
|
|
type: "kanban"
|
|
}
|
|
};
|
|
var i = 4;
|
|
var items = false;
|
|
(old.order || []).forEach((function(key) {
|
|
var data = old.data[key];
|
|
if (!data || !data.task) {
|
|
return;
|
|
}
|
|
items = true;
|
|
var column = data.state ? "3" : "1";
|
|
k.content.data[column].item.push(i);
|
|
k.content.items[i] = {
|
|
id: i,
|
|
title: data.task
|
|
};
|
|
i++;
|
|
}));
|
|
if (!items) {
|
|
w.abort();
|
|
next();
|
|
return;
|
|
}
|
|
var newH = Hash.createRandomHash("kanban");
|
|
var secret = Hash.getSecrets("kanban", newH);
|
|
var oldSecret = Hash.getSecrets("todo", h);
|
|
Crypt.put(newH, JSON.stringify(k), w((function(err) {
|
|
if (err) {
|
|
w.abort();
|
|
next();
|
|
return;
|
|
}
|
|
if (store.rpc) {
|
|
store.rpc.pin([ secret.channel ], (function() {}));
|
|
store.rpc.unpin([ oldSecret.channel ], (function() {}));
|
|
}
|
|
var href = Hash.hashToHref(newH, "kanban");
|
|
store.manager.addPad([ "root" ], {
|
|
title: Messages.type.todo,
|
|
owners: opts.metadata.owners,
|
|
channel: secret.channel,
|
|
href,
|
|
roHref: Hash.hashToHref(Hash.getViewHashFromKeys(secret), "kanban"),
|
|
atime: +new Date,
|
|
ctime: +new Date
|
|
}, w((function(e) {
|
|
if (e) {
|
|
return void console.error(e);
|
|
}
|
|
delete store.proxy.todo;
|
|
var myData = Messaging.createData(userObject);
|
|
var ctx = {
|
|
store
|
|
};
|
|
Mailbox.sendTo(ctx, "MOVE_TODO", {
|
|
user: myData,
|
|
href
|
|
}, {
|
|
channel: myData.notifications,
|
|
curvePublic: myData.curvePublic
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void console.error(obj);
|
|
}
|
|
}));
|
|
})));
|
|
})), opts);
|
|
})).nThen((function() {
|
|
next();
|
|
}));
|
|
};
|
|
if (version < 10) {
|
|
fixTodo();
|
|
}
|
|
})).nThen((function(waitFor) {
|
|
if (version >= 11) {
|
|
return;
|
|
}
|
|
var done = function() {
|
|
Feedback.send("Migrate-11", true);
|
|
userObject.version = version = 11;
|
|
};
|
|
var unsafeLinks = Util.find(userObject, [ "settings", "security", "unsafeLinks" ]);
|
|
if (unsafeLinks !== undefined) {
|
|
return void done();
|
|
}
|
|
var ctx = {
|
|
store
|
|
};
|
|
var myData = Messaging.createData(userObject);
|
|
if (!myData.curvePublic) {
|
|
return void done();
|
|
}
|
|
Mailbox.sendTo(ctx, "SAFE_LINKS_DEFAULT", {
|
|
user: myData
|
|
}, {
|
|
channel: myData.notifications,
|
|
curvePublic: myData.curvePublic
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
return void console.error(obj);
|
|
}
|
|
done();
|
|
})));
|
|
})).nThen((function() {
|
|
Realtime.whenRealtimeSyncs(store.realtime, Util.mkAsync(Util.bake(cb)));
|
|
}));
|
|
};
|
|
migrate.setCustomize = setCustomize;
|
|
return migrate;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonFeedback(), requireCommonHash(), requireCommonUtil(), requireCommonMessaging(), requireCryptget(), requireMailbox(), undefined, requireCommonRealtime(), requireNthen(), requireCrypto());
|
|
}
|
|
})();
|
|
})(migrateUserObject$1);
|
|
return migrateUserObject$1.exports;
|
|
}
|
|
var mergeDrive = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredMergeDrive;
|
|
function requireMergeDrive() {
|
|
if (hasRequiredMergeDrive) return mergeDrive.exports;
|
|
hasRequiredMergeDrive = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Crypt, FO, Hash, Realtime) => {
|
|
var exp = {};
|
|
exp.anonDriveIntoUser = function(proxyData, fsHash, cb) {
|
|
if (!fsHash || !proxyData.loggedIn) {
|
|
if (typeof cb === "function") {
|
|
return void cb();
|
|
}
|
|
}
|
|
var todo = function(err, doc) {
|
|
if (err) {
|
|
console.error("Cannot migrate recent pads", err);
|
|
return;
|
|
}
|
|
var parsed;
|
|
if (!doc) {
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
return;
|
|
}
|
|
try {
|
|
parsed = JSON.parse(doc);
|
|
} catch (e) {
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
console.error("Cannot parsed recent pads", e);
|
|
return;
|
|
}
|
|
if (parsed) {
|
|
var proxy = proxyData.proxy;
|
|
var oldFo = FO.init(parsed.drive, {
|
|
readOnly: false,
|
|
loggedIn: true,
|
|
outer: true
|
|
});
|
|
var onMigrated = function() {
|
|
oldFo.fixFiles(true);
|
|
var manager = proxyData.manager;
|
|
var oldFiles = oldFo.getFiles([ oldFo.FILES_DATA ]);
|
|
oldFiles.forEach((function(id) {
|
|
var data = oldFo.getFileData(id);
|
|
var channel = data.channel;
|
|
var datas = manager.findChannel(channel);
|
|
if (datas.length !== 0) {
|
|
if (!data.href) {
|
|
return;
|
|
}
|
|
datas.forEach((function(pad) {
|
|
if (pad.data && !pad.data.href) {
|
|
pad.userObject.setHref(channel, null, data.href);
|
|
}
|
|
}));
|
|
return;
|
|
}
|
|
if (data) {
|
|
manager.addPad(null, data, (function(err) {
|
|
if (err) {
|
|
return void console.error("Cannot import file:", data, err);
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
if (!proxy.FS_hashes || !Array.isArray(proxy.FS_hashes)) {
|
|
proxy.FS_hashes = [];
|
|
}
|
|
if (proxy.FS_hashes.indexOf(fsHash) === -1) {
|
|
proxy.FS_hashes.push(fsHash);
|
|
}
|
|
if (typeof cb === "function") {
|
|
Realtime.whenRealtimeSyncs(proxyData.realtime, cb);
|
|
}
|
|
};
|
|
if (oldFo && typeof oldFo.migrate === "function") {
|
|
oldFo.migrate(onMigrated);
|
|
} else {
|
|
console.log("oldFo.migrate is not a function");
|
|
onMigrated();
|
|
}
|
|
return;
|
|
}
|
|
if (typeof cb === "function") {
|
|
cb();
|
|
}
|
|
};
|
|
Crypt.get(fsHash, todo);
|
|
};
|
|
return exp;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCryptget(), requireUserObject(), requireCommonHash(), requireCommonRealtime());
|
|
}
|
|
})();
|
|
})(mergeDrive);
|
|
return mergeDrive.exports;
|
|
}
|
|
var require$$15 = getAugmentedNamespace(account);
|
|
const onCacheReadyEvt = commonUtilExports.mkEvent(true);
|
|
const onReadyEvt = commonUtilExports.mkEvent(true);
|
|
const onDisconnectEvt = commonUtilExports.mkEvent();
|
|
const onReconnectEvt = commonUtilExports.mkEvent();
|
|
const init = config => {
|
|
var _a;
|
|
const {broadcast, store, account} = config;
|
|
const drive = store.drive = store.drive || {};
|
|
let data = (_a = store.proxy) === null || _a === void 0 ? void 0 : _a.drive;
|
|
console.error(data, store.proxy);
|
|
(data === null || data === void 0 ? void 0 : data.hash) || commonHashExports.createRandomHash("drive");
|
|
if (!data.hash) {
|
|
drive.proxy = data;
|
|
account.onAccountCacheReady((() => {
|
|
onCacheReadyEvt.fire();
|
|
}));
|
|
account.onAccountReady((() => {
|
|
onReadyEvt.fire();
|
|
}));
|
|
return {
|
|
channel: "",
|
|
onDriveCacheReady: onCacheReadyEvt.reg,
|
|
onDriveReady: onReadyEvt.reg,
|
|
onDisconnect: onDisconnectEvt.reg,
|
|
onReconnect: onReconnectEvt.reg
|
|
};
|
|
}
|
|
throw new Error("NOT IMPLEMENTED");
|
|
};
|
|
const Drive = {
|
|
init
|
|
};
|
|
var drive = Object.freeze({
|
|
__proto__: null,
|
|
Drive
|
|
});
|
|
var require$$16 = getAugmentedNamespace(drive);
|
|
var cursor$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredCursor;
|
|
function requireCursor() {
|
|
if (hasRequiredCursor) return cursor$1.exports;
|
|
hasRequiredCursor = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Constants, Messages = {}, AppConfig = {}, Crypto) => {
|
|
var Cursor = {};
|
|
Cursor.setCustomize = data => {
|
|
Messages = data.Messages;
|
|
AppConfig = data.AppConfig;
|
|
};
|
|
var DEGRADED = AppConfig.degradedLimit || 8;
|
|
var convertToUint8 = function(obj) {
|
|
var l = Object.keys(obj).length;
|
|
var u = new Uint8Array(l);
|
|
for (var i = 0; i < l; i++) {
|
|
u[i] = obj[i];
|
|
}
|
|
return u;
|
|
};
|
|
var sendMyCursor = function(ctx, clientId) {
|
|
var client = ctx.clients[clientId];
|
|
if (!client || !client.cursor) {
|
|
return;
|
|
}
|
|
var chan = ctx.channels[client.channel];
|
|
if (!chan) {
|
|
return;
|
|
}
|
|
if (chan.degraded) {
|
|
return;
|
|
}
|
|
if (!chan.sendMsg) {
|
|
return;
|
|
}
|
|
var data = {
|
|
id: client.id,
|
|
cursor: client.cursor
|
|
};
|
|
chan.sendMsg(JSON.stringify(data));
|
|
ctx.emit("MESSAGE", data, chan.clients.filter((function(cl) {
|
|
return cl !== clientId;
|
|
})));
|
|
};
|
|
var sendOurCursors = function(ctx, chan) {
|
|
if (chan.degraded) {
|
|
return;
|
|
}
|
|
chan.clients.forEach((function(c) {
|
|
var client = ctx.clients[c];
|
|
if (!client) {
|
|
return;
|
|
}
|
|
var data = {
|
|
id: client.id,
|
|
cursor: client.cursor
|
|
};
|
|
chan.sendMsg(JSON.stringify(data));
|
|
}));
|
|
};
|
|
var updateDegraded = function(ctx, wc, chan) {
|
|
var m = wc.members;
|
|
chan.degraded = m.length - 1 >= DEGRADED;
|
|
ctx.emit("DEGRADED", {
|
|
degraded: chan.degraded
|
|
}, chan.clients);
|
|
};
|
|
var initCursor = function(ctx, obj, client, cb) {
|
|
var channel = obj.channel;
|
|
var secret = obj.secret;
|
|
if (secret.keys.cryptKey) {
|
|
secret.keys.cryptKey = convertToUint8(secret.keys.cryptKey);
|
|
}
|
|
var padChan = secret.channel;
|
|
var network = ctx.store.network;
|
|
var first = true;
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
c = ctx.clients[client] = {
|
|
channel,
|
|
cursor: {}
|
|
};
|
|
} else {
|
|
return void cb();
|
|
}
|
|
var chan = ctx.channels[channel];
|
|
if (chan) {
|
|
if (!c.id) {
|
|
c.id = chan.wc.myID + "-" + client;
|
|
}
|
|
chan.clients.forEach((function(cl) {
|
|
var clientObj = ctx.clients[cl];
|
|
if (chan.degraded) {
|
|
return;
|
|
}
|
|
if (!clientObj) {
|
|
return;
|
|
}
|
|
ctx.emit("MESSAGE", {
|
|
id: clientObj.id,
|
|
cursor: clientObj.cursor
|
|
}, [ client ]);
|
|
}));
|
|
chan.sendMsg(JSON.stringify({
|
|
join: true,
|
|
id: c.id
|
|
}));
|
|
chan.clients.push(client);
|
|
updateDegraded(ctx, chan.wc, chan);
|
|
return void cb();
|
|
}
|
|
var onOpen = function(wc) {
|
|
ctx.channels[channel] = ctx.channels[channel] || {};
|
|
var chan = ctx.channels[channel];
|
|
chan.padChan = padChan;
|
|
if (!c.id) {
|
|
c.id = wc.myID + "-" + client;
|
|
}
|
|
if (chan.clients) {
|
|
chan.clients.forEach((function(cl) {
|
|
if (ctx.clients[cl] && !ctx.clients[cl].id) {
|
|
ctx.clients[cl].id = wc.myID + "-" + cl;
|
|
}
|
|
}));
|
|
}
|
|
if (!chan.encryptor) {
|
|
chan.encryptor = Crypto.createEncryptor(secret.keys);
|
|
}
|
|
wc.on("join", (function() {
|
|
sendOurCursors(ctx, chan);
|
|
updateDegraded(ctx, wc, chan);
|
|
}));
|
|
wc.on("leave", (function(peer) {
|
|
ctx.emit("MESSAGE", {
|
|
leave: true,
|
|
id: peer
|
|
}, chan.clients);
|
|
updateDegraded(ctx, wc, chan);
|
|
}));
|
|
wc.on("message", (function(cryptMsg) {
|
|
if (chan.degraded) {
|
|
return;
|
|
}
|
|
var msg = chan.encryptor.decrypt(cryptMsg, secret.keys && secret.keys.validateKey);
|
|
var parsed;
|
|
try {
|
|
parsed = JSON.parse(msg);
|
|
if (parsed && parsed.join) {
|
|
return void sendOurCursors(ctx, chan);
|
|
}
|
|
ctx.emit("MESSAGE", parsed, chan.clients);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
chan.wc = wc;
|
|
chan.sendMsg = function(msg, cb) {
|
|
cb = cb || function() {};
|
|
var cmsg = chan.encryptor.encrypt(msg);
|
|
wc.bcast(cmsg).then((function() {
|
|
cb();
|
|
}), (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
}));
|
|
};
|
|
if (!first) {
|
|
return;
|
|
}
|
|
chan.clients = [ client ];
|
|
first = false;
|
|
cb();
|
|
updateDegraded(ctx, wc, chan);
|
|
};
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}));
|
|
var onReconnect = function() {
|
|
if (!ctx.channels[channel]) {
|
|
console.log("cant reconnect", channel);
|
|
return;
|
|
}
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
ctx.channels[channel] = ctx.channels[channel] || {};
|
|
ctx.channels[channel].onReconnect = onReconnect;
|
|
network.on("reconnect", onReconnect);
|
|
};
|
|
var updateCursor = function(ctx, data, client, cb) {
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "NO_CLIENT"
|
|
});
|
|
}
|
|
var proxy = ctx.store.proxy || {};
|
|
data.color = Util.find(proxy, [ "settings", "general", "cursor", "color" ]);
|
|
data.name = proxy[Constants.displayNameKey] || ctx.store.noDriveName || Messages.anonymous;
|
|
data.avatar = Util.find(proxy, [ "profile", "avatar" ]);
|
|
data.uid = Util.find(proxy, [ "uid" ]) || ctx.store.noDriveUid;
|
|
c.cursor = data;
|
|
sendMyCursor(ctx, client);
|
|
cb();
|
|
};
|
|
var leaveChannel = function(ctx, padChan) {
|
|
Object.keys(ctx.channels).some((function(cursorChan) {
|
|
var channel = ctx.channels[cursorChan];
|
|
if (channel.padChan !== padChan) {
|
|
return;
|
|
}
|
|
if (channel.wc) {
|
|
channel.wc.leave();
|
|
}
|
|
if (channel.onReconnect) {
|
|
var network = ctx.store.network;
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
delete ctx.channels[cursorChan];
|
|
return true;
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, clientId) {
|
|
var filter = function(c) {
|
|
return c !== clientId;
|
|
};
|
|
var chan;
|
|
for (var k in ctx.channels) {
|
|
chan = ctx.channels[k];
|
|
chan.clients = chan.clients.filter(filter);
|
|
if (chan.clients.length === 0) {
|
|
if (chan.wc) {
|
|
chan.wc.leave();
|
|
}
|
|
if (chan.onReconnect) {
|
|
var network = ctx.store.network;
|
|
network.off("reconnect", chan.onReconnect);
|
|
}
|
|
delete ctx.channels[k];
|
|
}
|
|
}
|
|
if (ctx.clients[clientId]) {
|
|
var leaveMsg = {
|
|
leave: true,
|
|
id: ctx.clients[clientId].id
|
|
};
|
|
chan = ctx.channels[ctx.clients[clientId].channel];
|
|
if (chan) {
|
|
chan.sendMsg(JSON.stringify(leaveMsg));
|
|
ctx.emit("MESSAGE", leaveMsg, chan.clients);
|
|
}
|
|
}
|
|
delete ctx.clients[clientId];
|
|
};
|
|
Cursor.init = function(cfg, waitFor, emit) {
|
|
var cursor = {};
|
|
if (cfg.store && cfg.store.modules && cfg.store.modules["cursor"]) {
|
|
return cfg.store.modules["cursor"];
|
|
}
|
|
var ctx = {
|
|
store: cfg.store,
|
|
emit,
|
|
channels: {},
|
|
clients: {}
|
|
};
|
|
cursor.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
cursor.leavePad = function(padChan) {
|
|
leaveChannel(ctx, padChan);
|
|
};
|
|
cursor.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "INIT_CURSOR") {
|
|
return void initCursor(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "UPDATE") {
|
|
return void updateCursor(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return cursor;
|
|
};
|
|
return Cursor;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonConstants(), undefined, undefined, requireCrypto());
|
|
}
|
|
})();
|
|
})(cursor$1);
|
|
return cursor$1.exports;
|
|
}
|
|
var support$1 = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredSupport;
|
|
function requireSupport() {
|
|
if (hasRequiredSupport) return support$1.exports;
|
|
hasRequiredSupport = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (ApiConfig = {}, Util, Hash, Realtime, Pinpad, Crypt, nThen, Crypto, Listmap, ChainPad, CpNetflux) => {
|
|
var Support = {};
|
|
Support.setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
};
|
|
var Nacl = Crypto.Nacl;
|
|
var notifyClient = function(ctx, admin, type, channel) {
|
|
let notifyList = Object.keys(ctx.clients).filter((cId => Boolean(ctx.clients[cId].admin) === admin));
|
|
if (!notifyList.length) {
|
|
return;
|
|
}
|
|
ctx.emit(type, {
|
|
channel
|
|
}, [ notifyList ]);
|
|
};
|
|
var getKeys = function(ctx, isAdmin, data, _cb) {
|
|
var cb = Util.mkAsync(_cb);
|
|
if (isAdmin && !ctx.adminRdyEvt) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
let origin = ApiConfig.httpUnsafeOrigin;
|
|
Util.fetchApi(origin, "config", true, (NewConfig => {
|
|
ctx.moderatorKeys = NewConfig.moderatorKeys;
|
|
ctx.adminKeys = NewConfig.adminKeys;
|
|
var supportKey = NewConfig.supportMailboxKey;
|
|
if (!supportKey) {
|
|
return void cb("E_NOT_INIT");
|
|
}
|
|
if (isAdmin && Util.find(ctx.store.proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]) !== supportKey) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
if (isAdmin) {
|
|
return ctx.adminRdyEvt.reg((() => {
|
|
cb(null, {
|
|
supportKey,
|
|
myCurve: data.adminCurvePrivate || Util.find(ctx.store.proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]),
|
|
theirPublic: data.curvePublic,
|
|
notifKey: data.curvePublic
|
|
});
|
|
}));
|
|
}
|
|
cb(null, {
|
|
supportKey,
|
|
myCurve: ctx.store.proxy.curvePrivate,
|
|
theirPublic: data.curvePublic || supportKey,
|
|
notifKey: supportKey
|
|
});
|
|
}));
|
|
};
|
|
var getContent = function(ctx, data, isAdmin, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var theirPublic, myCurve;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, isAdmin, data, waitFor(((err, obj) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
theirPublic = obj.theirPublic;
|
|
myCurve = obj.myCurve;
|
|
})));
|
|
})).nThen((() => {
|
|
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
|
|
var crypto = Crypto.Curve.createEncryptor(keys);
|
|
var cfg = {
|
|
network: ctx.store.network,
|
|
channel: data.channel,
|
|
noChainPad: true,
|
|
crypto,
|
|
owners: []
|
|
};
|
|
var all = [];
|
|
var cpNf;
|
|
var close = function() {
|
|
if (!cpNf) {
|
|
return;
|
|
}
|
|
if (typeof cpNf.stop !== "function") {
|
|
return;
|
|
}
|
|
cpNf.stop();
|
|
};
|
|
cb = Util.both(close, cb);
|
|
cfg.onMessage = function(msg, user, vKey, isCp, hash, author, data) {
|
|
var time = data && data.time;
|
|
try {
|
|
msg = JSON.parse(msg);
|
|
} catch (e) {
|
|
console.error(e);
|
|
return;
|
|
}
|
|
msg.time = time;
|
|
if (author) {
|
|
msg.author = author;
|
|
}
|
|
all.push(msg);
|
|
};
|
|
cfg.onError = cb;
|
|
cfg.onChannelError = cb;
|
|
cfg.onReady = function() {
|
|
cb(null, all);
|
|
};
|
|
cpNf = CpNetflux.start(cfg);
|
|
}));
|
|
};
|
|
var makeTicket = function(ctx, data, isAdmin, cb) {
|
|
var mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
var anonRpc = Util.find(ctx, [ "store", "anon_rpc" ]);
|
|
if (!mailbox) {
|
|
return void cb({
|
|
error: "E_NOT_READY"
|
|
});
|
|
}
|
|
if (!anonRpc) {
|
|
return void cb({
|
|
error: "anonymous rpc session not ready"
|
|
});
|
|
}
|
|
var channel = data.channel;
|
|
var title = data.title;
|
|
var ticket = data.ticket;
|
|
var supportKey, theirPublic, myCurve;
|
|
var time = +new Date;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, isAdmin, data, waitFor(((err, obj) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
supportKey = obj.supportKey;
|
|
theirPublic = obj.theirPublic;
|
|
myCurve = obj.myCurve;
|
|
})));
|
|
})).nThen((waitFor => {
|
|
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
|
|
var crypto = Crypto.Curve.createEncryptor(keys);
|
|
var text = JSON.stringify(ticket);
|
|
var ciphertext = crypto.encrypt(text);
|
|
anonRpc.send("WRITE_PRIVATE_MESSAGE", [ channel, ciphertext ], waitFor(((err, res) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
time = res && res[0];
|
|
})));
|
|
})).nThen((waitFor => {
|
|
if (!isAdmin) {
|
|
return;
|
|
}
|
|
var doc = ctx.adminDoc.proxy;
|
|
var active = doc.tickets.active;
|
|
if (active[channel]) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EEXISTS"
|
|
});
|
|
}
|
|
active[channel] = {
|
|
title: ticket.title,
|
|
restored: ticket.legacy,
|
|
premium: false,
|
|
time,
|
|
author: data.name,
|
|
supportKey,
|
|
lastAdmin: true,
|
|
authorKey: data.curvePublic,
|
|
notifications: data.notifications
|
|
};
|
|
})).nThen((waitFor => {
|
|
if (isAdmin) {
|
|
return;
|
|
}
|
|
ctx.supportData[channel] = {
|
|
time: +new Date,
|
|
title,
|
|
curvePublic: supportKey
|
|
};
|
|
ctx.Store.onSync(null, waitFor());
|
|
})).nThen((() => {
|
|
var notifChannel = isAdmin ? data.notifications : Hash.getChannelIdFromKey(supportKey);
|
|
mailbox.sendTo("NEW_TICKET", {
|
|
title,
|
|
channel,
|
|
time,
|
|
isAdmin,
|
|
supportKey,
|
|
premium: isAdmin ? "" : Util.find(ctx, [ "store", "account", "plan" ]),
|
|
user: Util.find(data.ticket, [ "sender", "curvePublic" ]) ? undefined : {
|
|
supportTeam: true
|
|
}
|
|
}, {
|
|
channel: notifChannel,
|
|
curvePublic: theirPublic
|
|
}, (obj => {
|
|
console.error(obj);
|
|
if (obj && obj.error) {
|
|
delete ctx.supportData[channel];
|
|
}
|
|
cb(obj);
|
|
}));
|
|
mailbox.sendTo("NOTIF_TICKET", {
|
|
title,
|
|
channel,
|
|
time,
|
|
isAdmin,
|
|
isNewTicket: true,
|
|
user: Util.find(data.ticket, [ "sender", "curvePublic" ]) ? undefined : {
|
|
supportTeam: true
|
|
}
|
|
}, {
|
|
channel: notifChannel,
|
|
curvePublic: theirPublic
|
|
}, (() => {}));
|
|
}));
|
|
};
|
|
var replyTicket = function(ctx, data, isAdmin, cb) {
|
|
var mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
var anonRpc = Util.find(ctx, [ "store", "anon_rpc" ]);
|
|
if (!mailbox) {
|
|
return void cb("E_NOT_READY");
|
|
}
|
|
if (!anonRpc) {
|
|
return void cb("anonymous rpc session not ready");
|
|
}
|
|
if (!data?.ticket) {
|
|
return void cb("E_NO_DATA");
|
|
}
|
|
var theirPublic, myCurve, notifKey;
|
|
var time;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, isAdmin, data, waitFor(((err, obj) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb(err);
|
|
}
|
|
theirPublic = obj.theirPublic;
|
|
myCurve = obj.myCurve;
|
|
notifKey = obj.notifKey;
|
|
})));
|
|
})).nThen((waitFor => {
|
|
var keys = Crypto.Curve.deriveKeys(theirPublic, myCurve);
|
|
var crypto = Crypto.Curve.createEncryptor(keys);
|
|
var text = JSON.stringify(data.ticket);
|
|
var ciphertext = crypto.encrypt(text);
|
|
anonRpc.send("WRITE_PRIVATE_MESSAGE", [ data.channel, ciphertext ], waitFor(((err, res) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb(err);
|
|
}
|
|
time = res && res[0];
|
|
cb(void 0, time);
|
|
})));
|
|
})).nThen((() => {
|
|
var notifChannel = isAdmin ? data.notifChannel : Hash.getChannelIdFromKey(notifKey);
|
|
if (!notifChannel) {
|
|
return;
|
|
}
|
|
mailbox.sendTo("NOTIF_TICKET", {
|
|
isAdmin,
|
|
title: data.ticket.title,
|
|
isClose: data.ticket.close,
|
|
channel: data.channel,
|
|
time,
|
|
user: Util.find(data.ticket, [ "sender", "curvePublic" ]) ? undefined : {
|
|
supportTeam: true
|
|
}
|
|
}, {
|
|
channel: notifChannel,
|
|
curvePublic: notifKey
|
|
}, (() => {}));
|
|
}));
|
|
};
|
|
var getPinList = function(ctx) {
|
|
if (!ctx.adminDoc || !ctx.supportRpc) {
|
|
return;
|
|
}
|
|
let adminChan = ctx.adminDoc.metadata && ctx.adminDoc.metadata.channel;
|
|
let doc = ctx.adminDoc.proxy;
|
|
let t = doc.tickets;
|
|
let list = [ adminChan, ...Object.keys(t.active), ...Object.keys(t.pending), ...Object.keys(t.closed) ];
|
|
return Util.deduplicateString(list).sort();
|
|
};
|
|
var initAdminRpc = function(ctx, _cb) {
|
|
let cb = Util.mkAsync(_cb);
|
|
let proxy = ctx.store.proxy;
|
|
let curvePrivate = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
if (!curvePrivate) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
let edPrivate, edPublic;
|
|
try {
|
|
let pair = Nacl.sign.keyPair.fromSeed(Nacl.util.decodeBase64(curvePrivate));
|
|
edPrivate = Nacl.util.encodeBase64(pair.secretKey);
|
|
edPublic = Nacl.util.encodeBase64(pair.publicKey);
|
|
} catch (e) {
|
|
return void cb(e);
|
|
}
|
|
Pinpad.create(ctx.store.network, {
|
|
edPublic,
|
|
edPrivate
|
|
}, ((e, call) => {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
console.log("Support RPC ready, public key is ", edPublic);
|
|
ctx.supportRpc = call;
|
|
cb();
|
|
}));
|
|
};
|
|
var loadAdminDoc = function(ctx, hash, cb) {
|
|
var secret = Hash.getSecrets("support", hash);
|
|
var listmapConfig = {
|
|
data: {},
|
|
channel: secret.channel,
|
|
crypto: Crypto.createEncryptor(secret.keys),
|
|
userName: "support",
|
|
ChainPad,
|
|
classic: true,
|
|
network: ctx.store.network,
|
|
metadata: {
|
|
validateKey: secret.keys.validateKey || undefined
|
|
}
|
|
};
|
|
var rt = ctx.adminDoc = Listmap.create(listmapConfig);
|
|
rt.proxy.on("ready", (function() {
|
|
var doc = rt.proxy;
|
|
doc.tickets = doc.tickets || {};
|
|
doc.tickets.active = doc.tickets.active || {};
|
|
doc.tickets.closed = doc.tickets.closed || {};
|
|
doc.tickets.pending = doc.tickets.pending || {};
|
|
ctx.adminRdyEvt.fire();
|
|
cb();
|
|
if (!ctx.supportRpc) {
|
|
return;
|
|
}
|
|
let list = getPinList(ctx);
|
|
let local = Hash.hashChannelList(list);
|
|
ctx.supportRpc.getServerHash((function(e, hash) {
|
|
if (e) {
|
|
return void console.warn(e);
|
|
}
|
|
if (hash !== local) {
|
|
ctx.supportRpc.reset(list, (function(e) {
|
|
if (e) {
|
|
console.warn(e);
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
}));
|
|
rt.proxy.on("change", [ "recorded" ], (function() {
|
|
notifyClient(ctx, true, "RECORDED_CHANGE", "");
|
|
}));
|
|
rt.proxy.on("remove", [ "recorded" ], (function() {
|
|
notifyClient(ctx, true, "RECORDED_CHANGE", "");
|
|
}));
|
|
};
|
|
var initializeSupportAdmin = function(ctx, isReset, waitFor) {
|
|
let unlock = waitFor();
|
|
let proxy = ctx.store.proxy;
|
|
let supportKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]);
|
|
let privateKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
if (!isReset) {
|
|
ctx.adminRdyEvt = Util.mkEvent(true);
|
|
}
|
|
nThen((waitFor => {
|
|
getKeys(ctx, false, {}, waitFor(((err, obj) => {
|
|
setTimeout(unlock);
|
|
if (err) {
|
|
return void waitFor.abort();
|
|
}
|
|
if (obj.theirPublic !== supportKey) {
|
|
try {
|
|
delete proxy.mailboxes.supportteam;
|
|
ctx.store.mailbox.close("supportteam");
|
|
} catch (e) {}
|
|
delete ctx.adminRdyEvt;
|
|
return void waitFor.abort();
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
initAdminRpc(ctx, waitFor((err => {
|
|
if (err) {
|
|
console.error("Support RPC not ready", err);
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
let seed = privateKey.slice(0, 24);
|
|
let hash = Hash.getEditHashFromKeys({
|
|
version: 2,
|
|
type: "support",
|
|
keys: {
|
|
editKeyStr: seed
|
|
}
|
|
});
|
|
loadAdminDoc(ctx, hash, waitFor());
|
|
})).nThen((() => {
|
|
console.log("Support admin loaded");
|
|
}));
|
|
};
|
|
var getMyTickets = function(ctx, data, cId, cb) {
|
|
var all = [];
|
|
var n = nThen;
|
|
if (!ctx.clients[cId]) {
|
|
ctx.clients[cId] = {
|
|
admin: false
|
|
};
|
|
}
|
|
Object.keys(ctx.supportData).forEach((function(ticket) {
|
|
n = n((waitFor => {
|
|
var t = Util.clone(ctx.supportData[ticket]);
|
|
getContent(ctx, {
|
|
channel: ticket,
|
|
curvePublic: t.curvePublic
|
|
}, false, waitFor(((err, messages) => {
|
|
if (err) {
|
|
if (err.type === "EDELETED") {
|
|
delete ctx.supportData[ticket];
|
|
return;
|
|
}
|
|
t.error = err;
|
|
} else {
|
|
t.messages = messages;
|
|
if (messages.length && messages[messages.length - 1].close) {
|
|
ctx.supportData[ticket].closed = true;
|
|
t.closed = true;
|
|
}
|
|
}
|
|
t.id = ticket;
|
|
all.push(t);
|
|
})));
|
|
})).nThen;
|
|
}));
|
|
n((() => {
|
|
all.sort(((t1, t2) => {
|
|
if (t1.closed && t2.closed) {
|
|
return t1.time - t2.time;
|
|
}
|
|
if (t1.closed) {
|
|
return 1;
|
|
}
|
|
if (t2.closed) {
|
|
return -1;
|
|
}
|
|
return t1.time - t2.time;
|
|
}));
|
|
cb({
|
|
tickets: all
|
|
});
|
|
}));
|
|
};
|
|
var makeMyTicket = function(ctx, data, cId, cb) {
|
|
makeTicket(ctx, data, false, cb);
|
|
};
|
|
var replyMyTicket = function(ctx, data, cId, cb) {
|
|
replyTicket(ctx, data, false, (err => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb({
|
|
sent: true
|
|
});
|
|
}));
|
|
};
|
|
var closeMyTicket = function(ctx, data, cId, cb) {
|
|
replyTicket(ctx, data, false, (err => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb({
|
|
closed: true
|
|
});
|
|
}));
|
|
};
|
|
var deleteMyTicket = function(ctx, data, cId, cb) {
|
|
let support = ctx.supportData;
|
|
let channel = data.channel;
|
|
if (!support[channel] || !support[channel].closed) {
|
|
return void cb({
|
|
error: "ENOTCLOSED"
|
|
});
|
|
}
|
|
delete support[channel];
|
|
cb({
|
|
deleted: true
|
|
});
|
|
};
|
|
var listTicketsAdmin = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (!ctx.clients[cId]) {
|
|
ctx.clients[cId] = {
|
|
admin: true
|
|
};
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
var doc = ctx.adminDoc.proxy;
|
|
if (data.type === "pending") {
|
|
return cb(Util.clone(doc.tickets.pending));
|
|
}
|
|
if (data.type === "closed") {
|
|
return cb(Util.clone(doc.tickets.closed));
|
|
}
|
|
cb(Util.clone(doc.tickets.active));
|
|
}));
|
|
};
|
|
var loadTicketAdmin = function(ctx, data, cId, cb) {
|
|
let supportKey = data.supportKey;
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
if (doc.oldKeys && doc.oldKeys[supportKey]) {
|
|
data.adminCurvePrivate = doc.oldKeys[supportKey].curvePrivate;
|
|
}
|
|
getContent(ctx, data, true, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
var doc = ctx.adminDoc.proxy;
|
|
if (!Array.isArray(res) || !res.length) {
|
|
return void cb(res);
|
|
}
|
|
res.sort(((t1, t2) => t1.time - t2.time));
|
|
let last = res[res.length - 1];
|
|
let premium = res.some((msg => {
|
|
let curve = Util.find(msg, [ "sender", "curvePublic" ]);
|
|
if (data.curvePublic !== curve) {
|
|
return;
|
|
}
|
|
return Util.find(msg, [ "sender", "quota", "plan" ]);
|
|
}));
|
|
var entry = doc.tickets.active[data.channel];
|
|
if (entry) {
|
|
if (last.legacy) {
|
|
let lastMsg = Array.isArray(last.messages) && last.messages[last.messages.length - 1];
|
|
last = lastMsg;
|
|
}
|
|
entry.time = last.time;
|
|
entry.premium = premium;
|
|
if (last.sender) {
|
|
entry.lastAdmin = !last.sender.blockLocation;
|
|
}
|
|
if (last.close) {
|
|
doc.tickets.closed[data.channel] = entry;
|
|
delete doc.tickets.active[data.channel];
|
|
notifyClient(ctx, true, "UPDATE_TICKET", data.channel);
|
|
}
|
|
}
|
|
cb(res);
|
|
}));
|
|
}));
|
|
};
|
|
var makeTicketAdmin = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
makeTicket(ctx, data, true, cb);
|
|
}));
|
|
};
|
|
var replyTicketAdmin = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let supportKey = data.supportKey;
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
if (doc.oldKeys && doc.oldKeys[supportKey]) {
|
|
data.adminCurvePrivate = doc.oldKeys[supportKey].curvePrivate;
|
|
}
|
|
replyTicket(ctx, data, true, ((err, time) => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
var doc = ctx.adminDoc.proxy;
|
|
var entry = doc.tickets.active[data.channel] || doc.tickets.pending[data.channel];
|
|
entry.time = time;
|
|
entry.lastAdmin = true;
|
|
cb({
|
|
sent: true
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var closeTicketAdmin = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let supportKey = data.supportKey;
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
if (doc.oldKeys && doc.oldKeys[supportKey]) {
|
|
data.adminCurvePrivate = doc.oldKeys[supportKey].curvePrivate;
|
|
}
|
|
replyTicket(ctx, data, true, (err => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
var doc = ctx.adminDoc.proxy;
|
|
var entry = doc.tickets.active[data.channel] || doc.tickets.pending[data.channel];
|
|
entry.time = +new Date;
|
|
entry.lastAdmin = true;
|
|
doc.tickets.closed[data.channel] = entry;
|
|
delete doc.tickets.active[data.channel];
|
|
delete doc.tickets.pending[data.channel];
|
|
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, (function() {
|
|
cb({
|
|
closed: true
|
|
});
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
var moveTicketAdmin = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let ticketId = data.channel;
|
|
let from = data.from;
|
|
let to = data.to;
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let fromDoc = doc.tickets[from];
|
|
let toDoc = doc.tickets[to];
|
|
if (!from || !to) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
let ticket = fromDoc[ticketId];
|
|
if (!ticket || toDoc[ticketId]) {
|
|
return void cb({
|
|
error: "CANT_MOVE"
|
|
});
|
|
}
|
|
toDoc[ticketId] = ticket;
|
|
delete fromDoc[ticketId];
|
|
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, (function() {
|
|
cb({
|
|
moved: true
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var getRecorded = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let recorded = doc.recorded = doc.recorded || {};
|
|
cb({
|
|
messages: Util.clone(recorded)
|
|
});
|
|
}));
|
|
};
|
|
var setRecorded = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let id = data.id;
|
|
let content = data.content;
|
|
let remove = Boolean(data.remove);
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let recorded = doc.recorded = doc.recorded || {};
|
|
if (remove) {
|
|
delete recorded[id];
|
|
} else {
|
|
recorded[id] = {
|
|
content,
|
|
count: 0
|
|
};
|
|
}
|
|
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, (function() {
|
|
cb({
|
|
done: true
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var useRecorded = function(ctx, data, cId, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let id = data.id;
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let recorded = doc.recorded = doc.recorded || {};
|
|
let entry = recorded[id];
|
|
if (entry) {
|
|
entry.count = (entry.count || 0) + 1;
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var searchAdmin = (ctx, data, cId, cb) => {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let tags = data.tags || [];
|
|
let text = (data.text || "").toLowerCase();
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let t = doc.tickets;
|
|
let all = {};
|
|
let add = (id, data, category) => {
|
|
let clone = Util.clone(data);
|
|
clone.category = category;
|
|
all[id] = clone;
|
|
};
|
|
[ "active", "pending", "closed" ].some((cat => {
|
|
let tickets = t[cat];
|
|
return Object.keys(tickets).some((id => {
|
|
let ticket = tickets[id];
|
|
let hasTag = !tags.length || (ticket.tags || []).some((tag => tags.includes(tag)));
|
|
if (!hasTag) {
|
|
return;
|
|
}
|
|
let id64 = Util.hexToBase64(id).slice(0, 10);
|
|
if (text === id64) {
|
|
all = {};
|
|
add(id, ticket, cat);
|
|
return true;
|
|
}
|
|
let hasText = !text || ticket.title.toLowerCase().includes(text);
|
|
if (!hasText) {
|
|
return;
|
|
}
|
|
add(id, ticket, cat);
|
|
}));
|
|
}));
|
|
cb({
|
|
tickets: all
|
|
});
|
|
}));
|
|
};
|
|
var setTagsAdmin = (ctx, data, cId, cb) => {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let t = doc.tickets;
|
|
let chan = data.channel;
|
|
let ticket = t.active[chan] || t.pending[chan] || t.closed[chan];
|
|
ticket.tags = data.tags || [];
|
|
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, (function() {
|
|
let allTags = [];
|
|
[ "active", "pending", "closed" ].forEach((cat => {
|
|
let tickets = t[cat];
|
|
Object.keys(tickets).forEach((id => {
|
|
let ticket = tickets[id];
|
|
(ticket.tags || []).forEach((tag => {
|
|
if (!allTags.includes(tag)) {
|
|
allTags.push(tag);
|
|
}
|
|
}));
|
|
}));
|
|
}));
|
|
cb({
|
|
done: true,
|
|
allTags
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var filterTagsAdmin = (ctx, data, cId, cb) => {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let tags = data.tags || [];
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let t = doc.tickets;
|
|
if (!tags.length) {
|
|
return void cb({
|
|
all: true
|
|
});
|
|
}
|
|
let all = [];
|
|
[ "active", "pending", "closed" ].forEach((cat => {
|
|
let tickets = t[cat];
|
|
Object.keys(tickets).forEach((id => {
|
|
let ticket = tickets[id];
|
|
let hasTag = (ticket.tags || []).some((tag => tags.includes(tag)));
|
|
if (!hasTag) {
|
|
all.push(id);
|
|
}
|
|
}));
|
|
}));
|
|
cb({
|
|
tickets: all
|
|
});
|
|
}));
|
|
};
|
|
var clearLegacy = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
ctx.store.mailbox.close("supportadmin", (function() {
|
|
delete proxy.mailboxes.supportadmin;
|
|
ctx.Store.onSync(null, (function() {
|
|
cb({
|
|
done: true
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var dumpLegacy = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
let _legacy = Util.find(proxy, [ "mailboxes", "supportadmin" ]);
|
|
if (!_legacy) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
let legacy = Util.clone(_legacy);
|
|
legacy.lastKnownHash = undefined;
|
|
legacy.viewed = [];
|
|
ctx.store.mailbox.open("supportadmin", legacy, (function(contentByHash) {
|
|
ctx.store.mailbox.close("supportadmin", (function() {}));
|
|
cb(contentByHash);
|
|
}), true, {
|
|
dump: true
|
|
});
|
|
};
|
|
let findLegacy = (ctx, author, title) => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
return [ "active", "pending", "closed" ].some((k => {
|
|
let all = doc.tickets[k];
|
|
return Object.keys(all).some((id => {
|
|
let ticket = all[id];
|
|
return ticket.authorKey === author && ticket.title === title && ticket.restored;
|
|
}));
|
|
}));
|
|
};
|
|
var getLegacy = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
let legacy = Util.find(proxy, [ "mailboxes", "supportadmin" ]);
|
|
if (!legacy) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
ctx.store.mailbox.open("supportadmin", legacy, (function(contentByHash) {
|
|
ctx.store.mailbox.close("supportadmin", (function() {}));
|
|
let c = Util.clone(contentByHash || {});
|
|
let toFilter = [];
|
|
Object.keys(c).forEach((h => {
|
|
let msg = c[h];
|
|
if (msg.type === "CLOSE") {
|
|
if (!toFilter.includes(msg.content.id)) {
|
|
toFilter.push(msg.content.id);
|
|
}
|
|
return;
|
|
}
|
|
let author = msg.author;
|
|
let title = msg.content && msg.content.title;
|
|
if (findLegacy(ctx, author, title)) {
|
|
if (!toFilter.includes(msg.content.id)) {
|
|
toFilter.push(msg.content.id);
|
|
}
|
|
}
|
|
}));
|
|
Object.keys(c).forEach((h => {
|
|
let msg = c[h];
|
|
if (msg.content && toFilter.includes(msg.content.id)) {
|
|
delete c[h];
|
|
}
|
|
}));
|
|
cb(c);
|
|
}), true, {
|
|
dump: true
|
|
});
|
|
};
|
|
var restoreLegacy = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
let legacy = Util.find(proxy, [ "mailboxes", "supportadmin" ]);
|
|
if (!legacy) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
let messages = data.messages;
|
|
let hashes = data.hashes;
|
|
let first = messages[0];
|
|
let last = messages[messages.length - 1];
|
|
if (!first) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let ticketData = {
|
|
name: Util.find(first, [ "sender", "name" ]),
|
|
notifications: Util.find(first, [ "sender", "notifications" ]),
|
|
curvePublic: Util.find(first, [ "sender", "curvePublic" ]),
|
|
channel: Hash.createChannelId(),
|
|
title: first.title,
|
|
time: last.time,
|
|
ticket: {
|
|
legacy: true,
|
|
title: first.title,
|
|
sender: first.sender,
|
|
messages
|
|
}
|
|
};
|
|
makeTicket(ctx, ticketData, true, (obj => {
|
|
if (obj && obj.error) {
|
|
return void cb(obj);
|
|
}
|
|
hashes.forEach((hash => {
|
|
legacy.viewed.push(hash);
|
|
}));
|
|
ctx.Store.onSync(null, (function() {
|
|
cb({
|
|
done: true
|
|
});
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
var addAdminTicket = function(ctx, data, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb(true);
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let supportKey;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, true, data, waitFor(((err, obj) => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb(true);
|
|
}
|
|
supportKey = obj.supportKey;
|
|
})));
|
|
})).nThen((() => {
|
|
var rdmTo = Math.floor(Math.random() * 2e3);
|
|
setTimeout((() => {
|
|
var doc = ctx.adminDoc.proxy;
|
|
if (doc.tickets.active[data.channel] || doc.tickets.closed[data.channel] || doc.tickets.pending[data.channel]) {
|
|
return void cb(true);
|
|
}
|
|
doc.tickets.active[data.channel] = {
|
|
title: data.title,
|
|
premium: data.premium,
|
|
time: data.time,
|
|
author: data.user && data.user.displayName,
|
|
supportKey: data.supportKey || supportKey,
|
|
authorKey: data.user && data.user.curvePublic
|
|
};
|
|
Realtime.whenRealtimeSyncs(ctx.adminDoc.realtime, (function() {
|
|
cb(true);
|
|
}));
|
|
notifyClient(ctx, true, "NEW_TICKET", data.channel);
|
|
if (ctx.supportRpc) {
|
|
ctx.supportRpc.pin([ data.channel ], (() => {}));
|
|
}
|
|
}), rdmTo);
|
|
}));
|
|
}));
|
|
};
|
|
var updateAdminTicket = function(ctx, data) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return;
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
var rdmTo = Math.floor(Math.random() * 2e3);
|
|
setTimeout((() => {
|
|
var doc = ctx.adminDoc.proxy;
|
|
let t = doc.tickets.active[data.channel] || doc.tickets.pending[data.channel];
|
|
if (!t) {
|
|
return;
|
|
}
|
|
if (data.time <= t.time) {
|
|
return;
|
|
}
|
|
if (data.isClose) {
|
|
doc.tickets.closed[data.channel] = t;
|
|
delete doc.tickets.active[data.channel];
|
|
delete doc.tickets.pending[data.channel];
|
|
}
|
|
t.time = data.time;
|
|
t.lastAdmin = false;
|
|
notifyClient(ctx, true, "UPDATE_TICKET", data.channel);
|
|
}), rdmTo);
|
|
}));
|
|
};
|
|
var checkAdminTicket = function(ctx, data, cb) {
|
|
if (!ctx.adminRdyEvt) {
|
|
return void cb(true);
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let doc = ctx.adminDoc.proxy;
|
|
let exists = doc.tickets.active[data.channel] || doc.tickets.pending[data.channel];
|
|
cb(exists);
|
|
}));
|
|
};
|
|
var addUserTicket = function(ctx, data, cb) {
|
|
if (!ctx.supportData) {
|
|
return void cb(true);
|
|
}
|
|
let channel = data.channel;
|
|
ctx.supportData[channel] = {
|
|
time: data.time,
|
|
title: data.title,
|
|
curvePublic: data.supportKey
|
|
};
|
|
ctx.Store.onSync(null, (function() {
|
|
cb(true);
|
|
}));
|
|
};
|
|
var updateUserTicket = function(ctx, data) {
|
|
notifyClient(ctx, false, "UPDATE_TICKET", data.channel);
|
|
if (data.isClose) {
|
|
let ticket = ctx.supportData[data.channel];
|
|
if (!ticket) {
|
|
return;
|
|
}
|
|
ticket.closed = true;
|
|
}
|
|
};
|
|
var updateAdminKey = (ctx, data, cb) => {
|
|
let newKey = data.supportKey;
|
|
let newKeyPub = Hash.getBoxPublicFromSecret(newKey);
|
|
let proxy = ctx.store.proxy;
|
|
const oldKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
const oldKeyPub = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]);
|
|
getKeys(ctx, false, {}, ((err, obj) => {
|
|
if (err) {
|
|
return void cb(true);
|
|
}
|
|
if (newKeyPub !== obj.theirPublic) {
|
|
return void cb(true);
|
|
}
|
|
if (oldKey === newKey || oldKeyPub === newKeyPub) {
|
|
return void cb(true);
|
|
}
|
|
let mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
try {
|
|
if (ctx.adminDoc) {
|
|
ctx.adminDoc.stop();
|
|
}
|
|
if (mailbox) {
|
|
mailbox.close("supportteam");
|
|
}
|
|
if (ctx.supportRpc) {
|
|
ctx.supportRpc.destroy();
|
|
}
|
|
ctx.adminRdyEvt = Util.mkEvent(true);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
ctx.Store.addAdminMailbox(null, {
|
|
version: 2,
|
|
priv: newKey
|
|
}, (obj => {
|
|
if (obj && obj.error) {
|
|
return void cb(true);
|
|
}
|
|
nThen((waitFor => {
|
|
initializeSupportAdmin(ctx, true, waitFor);
|
|
ctx.adminRdyEvt.reg((() => {
|
|
notifyClient(ctx, true, "UPDATE_RIGHTS");
|
|
cb(false);
|
|
}));
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
let updateServerKey = (ctx, curvePublic, curvePrivate, cb) => {
|
|
let edPublic;
|
|
try {
|
|
let pair = Nacl.sign.keyPair.fromSeed(Nacl.util.decodeBase64(curvePrivate));
|
|
edPublic = Nacl.util.encodeBase64(pair.publicKey);
|
|
} catch (e) {
|
|
return void cb(e);
|
|
}
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "ADMIN_DECREE",
|
|
data: [ "SET_SUPPORT_KEYS", [ curvePublic, edPublic ] ]
|
|
}, cb);
|
|
};
|
|
let getModerators = (ctx, data, cId, cb) => {
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "GET_MODERATORS",
|
|
data: {}
|
|
}, cb);
|
|
};
|
|
let rotateKeys = function(ctx, data, cId, _cb) {
|
|
let cb = Util.once(Util.mkAsync(_cb));
|
|
let oldSupportKey;
|
|
let proxy = ctx.store.proxy;
|
|
let edPublic = proxy.edPublic;
|
|
const keyPair = Nacl.box.keyPair();
|
|
const newKeyPub = Nacl.util.encodeBase64(keyPair.publicKey);
|
|
const newKey = Nacl.util.encodeBase64(keyPair.secretKey);
|
|
const oldKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
const oldKeyPub = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]);
|
|
if (!newKey || !newKeyPub) {
|
|
return void cb({
|
|
error: "INVALID_KEY"
|
|
});
|
|
}
|
|
let oldAdminChan;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, false, {}, waitFor(((err, obj) => {
|
|
if (err === "E_NOT_INIT") {
|
|
return;
|
|
}
|
|
if (err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
return void waitFor.abort();
|
|
}
|
|
oldSupportKey = obj.theirPublic;
|
|
})));
|
|
})).nThen((waitFor => {
|
|
if (!ctx.adminKeys.includes(edPublic)) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
})).nThen((waitFor => {
|
|
if (!oldSupportKey) {
|
|
return;
|
|
}
|
|
if (!ctx.moderatorKeys.includes(edPublic)) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
if (oldKeyPub !== oldSupportKey) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
})).nThen((waitFor => {
|
|
if (!oldSupportKey) {
|
|
return;
|
|
}
|
|
ctx.adminRdyEvt.reg((() => {
|
|
let oldDoc = ctx.adminDoc.proxy;
|
|
oldAdminChan = ctx.adminDoc.metadata && ctx.adminDoc.metadata.channel;
|
|
let seed = newKey.slice(0, 24);
|
|
let hash = Hash.getEditHashFromKeys({
|
|
version: 2,
|
|
type: "support",
|
|
keys: {
|
|
editKeyStr: seed
|
|
}
|
|
});
|
|
let cfg = {
|
|
network: ctx.store.network,
|
|
initialState: "{}"
|
|
};
|
|
var oldKeys = oldDoc.oldKeys = oldDoc.oldKeys || {};
|
|
oldKeys[oldKeyPub] = {
|
|
curvePrivate: oldKey,
|
|
rotatedOn: +new Date,
|
|
rotatedBy: edPublic
|
|
};
|
|
Crypt.put(hash, JSON.stringify(oldDoc), waitFor((err => {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
})), cfg);
|
|
}));
|
|
})).nThen((waitFor => {
|
|
updateServerKey(ctx, newKeyPub, newKey, waitFor((obj => {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
})));
|
|
})).nThen((() => {
|
|
if (!oldSupportKey) {
|
|
return;
|
|
}
|
|
if (ctx.adminDoc) {
|
|
ctx.adminDoc.stop();
|
|
}
|
|
let mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
if (mailbox) {
|
|
mailbox.close("supportteam");
|
|
}
|
|
if (ctx.supportRpc) {
|
|
ctx.supportRpc.destroy();
|
|
}
|
|
ctx.adminRdyEvt = Util.mkEvent(true);
|
|
})).nThen((waitFor => {
|
|
ctx.Store.addAdminMailbox(null, {
|
|
version: 2,
|
|
priv: newKey
|
|
}, waitFor((obj => {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
if (oldSupportKey) {
|
|
return updateServerKey(ctx, oldKeyPub, oldKey, (() => void cb(obj)));
|
|
}
|
|
return void cb(obj);
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
if (!oldSupportKey) {
|
|
return;
|
|
}
|
|
let mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
getModerators(ctx, null, null, waitFor((obj => {
|
|
if (obj && obj.error) {
|
|
return void cb({
|
|
success: true,
|
|
noNotify: true
|
|
});
|
|
}
|
|
let all = obj && obj[0];
|
|
Object.keys(all || {}).forEach((modEdPub => {
|
|
let modData = all[modEdPub];
|
|
mailbox.sendTo("MODERATOR_NEW_KEY", {
|
|
supportKey: newKey
|
|
}, {
|
|
channel: modData.mailbox,
|
|
curvePublic: modData.curvePublic
|
|
}, (() => {}));
|
|
}));
|
|
})));
|
|
})).nThen((waitFor => {
|
|
initializeSupportAdmin(ctx, true, waitFor);
|
|
})).nThen((waitFor => {
|
|
if (!oldAdminChan) {
|
|
return;
|
|
}
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "ARCHIVE_DOCUMENT",
|
|
data: {
|
|
id: oldAdminChan,
|
|
reason: "Deprecated support pad"
|
|
}
|
|
}, waitFor());
|
|
})).nThen((() => {
|
|
cb({
|
|
success: true
|
|
});
|
|
}));
|
|
};
|
|
let disableSupport = function(ctx, data, cId, _cb) {
|
|
let cb = Util.once(Util.mkAsync(_cb));
|
|
let proxy = ctx.store.proxy;
|
|
let edPublic = proxy.edPublic;
|
|
let moderators;
|
|
nThen((waitFor => {
|
|
getKeys(ctx, false, {}, waitFor((err => {
|
|
if (err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
return void waitFor.abort();
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
if (!ctx.adminKeys.includes(edPublic)) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
})).nThen((waitFor => {
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "ARCHIVE_SUPPORT",
|
|
data: {}
|
|
}, waitFor((obj => {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "ADMIN_DECREE",
|
|
data: [ "SET_SUPPORT_KEYS", [ "", "" ] ]
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
})));
|
|
})).nThen((waitFor => {
|
|
getModerators(ctx, null, null, waitFor((obj => {
|
|
if (!obj || obj.error) {
|
|
waitFor.abort();
|
|
return void cb();
|
|
}
|
|
moderators = obj[0] || {};
|
|
})));
|
|
})).nThen((() => {
|
|
let n = nThen;
|
|
Object.keys(moderators).forEach((ed => {
|
|
n = n((waitFor => {
|
|
ctx.Store.adminRpc(null, {
|
|
cmd: "REMOVE_MODERATOR",
|
|
data: ed
|
|
}, waitFor((obj => {
|
|
if (obj && obj.error) {
|
|
console.error("Error removing moderator data", ed, obj.error);
|
|
}
|
|
})));
|
|
})).nThen;
|
|
}));
|
|
n((() => {
|
|
cb();
|
|
}));
|
|
}));
|
|
};
|
|
var getAdminKey = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
let supportKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]);
|
|
let privateKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
getKeys(ctx, false, {}, ((err, obj) => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (supportKey && obj.theirPublic !== supportKey) {
|
|
privateKey = undefined;
|
|
}
|
|
cb({
|
|
curvePrivate: privateKey,
|
|
curvePublic: obj.theirPublic
|
|
});
|
|
}));
|
|
};
|
|
var addModerator = function(ctx, data, cId, cb) {
|
|
let proxy = ctx.store.proxy;
|
|
var mailbox = Util.find(ctx, [ "store", "mailbox" ]);
|
|
let supportKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePublic" ]);
|
|
let privateKey = Util.find(proxy, [ "mailboxes", "supportteam", "keys", "curvePrivate" ]);
|
|
let lastKnownHash = Util.find(proxy, [ "mailboxes", "supportteam", "lastKnownHash" ]);
|
|
let edPublic = proxy.edPublic;
|
|
getKeys(ctx, false, {}, ((err, obj) => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (obj.theirPublic !== supportKey) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (!ctx.moderatorKeys.includes(edPublic)) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
mailbox.sendTo("ADD_MODERATOR", {
|
|
supportKey: privateKey,
|
|
lastKnownHash
|
|
}, {
|
|
channel: data.mailbox,
|
|
curvePublic: data.curvePublic
|
|
}, (() => {
|
|
cb();
|
|
}));
|
|
}));
|
|
};
|
|
Support.init = function(cfg, waitFor, emit) {
|
|
var support = {};
|
|
if (cfg.store && cfg.store.modules && cfg.store.modules["support"]) {
|
|
return cfg.store.modules["support"];
|
|
}
|
|
var store = cfg.store;
|
|
var proxy = store.proxy.support = store.proxy.support || {};
|
|
var ctx = {
|
|
moderatorKeys: ApiConfig.moderatorKeys,
|
|
adminKeys: ApiConfig.adminKeys,
|
|
supportData: proxy,
|
|
store: cfg.store,
|
|
Store: cfg.Store,
|
|
emit,
|
|
clients: {}
|
|
};
|
|
if (Util.find(store, [ "proxy", "mailboxes", "supportteam" ])) {
|
|
initializeSupportAdmin(ctx, false, waitFor);
|
|
}
|
|
support.ctx = ctx;
|
|
support.removeClient = function(clientId) {
|
|
delete ctx.clients[clientId];
|
|
};
|
|
support.leavePad = function() {};
|
|
support.addAdminTicket = function(content, cb) {
|
|
addAdminTicket(ctx, content, cb);
|
|
};
|
|
support.updateAdminTicket = function(content) {
|
|
updateAdminTicket(ctx, content);
|
|
};
|
|
support.updateAdminKey = function(content, cb) {
|
|
updateAdminKey(ctx, content, cb);
|
|
};
|
|
support.checkAdminTicket = function(content, cb) {
|
|
checkAdminTicket(ctx, content, cb);
|
|
};
|
|
support.addUserTicket = function(content, cb) {
|
|
addUserTicket(ctx, content, cb);
|
|
};
|
|
support.updateUserTicket = function(content) {
|
|
updateUserTicket(ctx, content);
|
|
};
|
|
support.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "MAKE_TICKET") {
|
|
return void makeMyTicket(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_MY_TICKETS") {
|
|
return void getMyTickets(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "REPLY_TICKET") {
|
|
return void replyMyTicket(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CLOSE_TICKET") {
|
|
return void closeMyTicket(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DELETE_TICKET") {
|
|
return void deleteMyTicket(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "MAKE_TICKET_ADMIN") {
|
|
return void makeTicketAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "LIST_TICKETS_ADMIN") {
|
|
return void listTicketsAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "LOAD_TICKET_ADMIN") {
|
|
return void loadTicketAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "REPLY_TICKET_ADMIN") {
|
|
return void replyTicketAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CLOSE_TICKET_ADMIN") {
|
|
return void closeTicketAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "MOVE_TICKET_ADMIN") {
|
|
return void moveTicketAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_RECORDED") {
|
|
return void getRecorded(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SET_RECORDED") {
|
|
return void setRecorded(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "USE_RECORDED") {
|
|
return void useRecorded(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SEARCH_ADMIN") {
|
|
return void searchAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "FILTER_TAGS_ADMIN") {
|
|
return void filterTagsAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SET_TAGS_ADMIN") {
|
|
return void setTagsAdmin(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_LEGACY") {
|
|
return void getLegacy(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DUMP_LEGACY") {
|
|
return void dumpLegacy(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CLEAR_LEGACY") {
|
|
return void clearLegacy(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "RESTORE_LEGACY") {
|
|
return void restoreLegacy(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_PRIVATE_KEY") {
|
|
return void getAdminKey(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DISABLE_SUPPORT") {
|
|
return void disableSupport(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "ROTATE_KEYS") {
|
|
return void rotateKeys(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "ADD_MODERATOR") {
|
|
return void addModerator(ctx, data, clientId, cb);
|
|
}
|
|
cb({
|
|
error: "NOT_SUPPORTED"
|
|
});
|
|
};
|
|
return support;
|
|
};
|
|
return Support;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, requireCommonUtil(), requireCommonHash(), requireCommonRealtime(), requirePinpad(), requireCryptget(), requireNthen(), requireCrypto(), requireChainpadListmap(), requireChainpad_dist(), requireChainpadNetflux());
|
|
}
|
|
})();
|
|
})(support$1);
|
|
return support$1.exports;
|
|
}
|
|
var integration = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredIntegration;
|
|
function requireIntegration() {
|
|
if (hasRequiredIntegration) return integration.exports;
|
|
hasRequiredIntegration = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = Crypto => {
|
|
var Integration = {};
|
|
var convertToUint8 = function(obj) {
|
|
var l = Object.keys(obj).length;
|
|
var u = new Uint8Array(l);
|
|
for (var i = 0; i < l; i++) {
|
|
u[i] = obj[i];
|
|
}
|
|
return u;
|
|
};
|
|
var sendMsg = function(ctx, data, client, cb) {
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "NO_CLIENT"
|
|
});
|
|
}
|
|
var chan = ctx.channels[c.channel];
|
|
if (!chan) {
|
|
return void cb({
|
|
error: "NO_CHAN"
|
|
});
|
|
}
|
|
var obj = {
|
|
id: client,
|
|
msg: data.msg,
|
|
uid: data.uid
|
|
};
|
|
chan.sendMsg(JSON.stringify(obj), cb);
|
|
ctx.emit("MESSAGE", obj, chan.clients.filter((function(cl) {
|
|
return cl !== client;
|
|
})));
|
|
};
|
|
var initIntegration = function(ctx, obj, client, cb) {
|
|
var channel = obj.channel;
|
|
var secret = obj.secret;
|
|
if (secret.keys.cryptKey) {
|
|
secret.keys.cryptKey = convertToUint8(secret.keys.cryptKey);
|
|
}
|
|
var padChan = secret.channel;
|
|
var network = ctx.store.network;
|
|
var first = true;
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
c = ctx.clients[client] = {
|
|
channel
|
|
};
|
|
} else {
|
|
return void cb();
|
|
}
|
|
var chan = ctx.channels[channel];
|
|
if (chan) {
|
|
if (!c.id) {
|
|
c.id = chan.wc.myID + "-" + client;
|
|
}
|
|
chan.clients.push(client);
|
|
return void cb();
|
|
}
|
|
var onOpen = function(wc) {
|
|
ctx.channels[channel] = ctx.channels[channel] || {};
|
|
var chan = ctx.channels[channel];
|
|
chan.padChan = padChan;
|
|
if (!c.id) {
|
|
c.id = wc.myID + "-" + client;
|
|
}
|
|
if (chan.clients) {
|
|
chan.clients.forEach((function(cl) {
|
|
if (ctx.clients[cl] && !ctx.clients[cl].id) {
|
|
ctx.clients[cl].id = wc.myID + "-" + cl;
|
|
}
|
|
}));
|
|
}
|
|
if (!chan.encryptor) {
|
|
chan.encryptor = Crypto.createEncryptor(secret.keys);
|
|
}
|
|
wc.on("message", (function(cryptMsg) {
|
|
var msg = chan.encryptor.decrypt(cryptMsg, secret.keys && secret.keys.validateKey);
|
|
var parsed;
|
|
try {
|
|
parsed = JSON.parse(msg);
|
|
ctx.emit("MESSAGE", parsed, chan.clients);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
chan.wc = wc;
|
|
chan.sendMsg = function(msg, cb) {
|
|
cb = cb || function() {};
|
|
var cmsg = chan.encryptor.encrypt(msg);
|
|
wc.bcast(cmsg).then((function() {
|
|
cb();
|
|
}), (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
}));
|
|
};
|
|
if (!first) {
|
|
return;
|
|
}
|
|
chan.clients = [ client ];
|
|
first = false;
|
|
cb();
|
|
};
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}));
|
|
var onReconnect = function() {
|
|
if (!ctx.channels[channel]) {
|
|
console.log("cant reconnect", channel);
|
|
return;
|
|
}
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
ctx.channels[channel] = ctx.channels[channel] || {};
|
|
ctx.channels[channel].onReconnect = onReconnect;
|
|
network.on("reconnect", onReconnect);
|
|
};
|
|
var leaveChannel = function(ctx, padChan) {
|
|
Object.keys(ctx.channels).some((function(cursorChan) {
|
|
var channel = ctx.channels[cursorChan];
|
|
if (channel.padChan !== padChan) {
|
|
return;
|
|
}
|
|
if (channel.wc) {
|
|
channel.wc.leave();
|
|
}
|
|
if (channel.onReconnect) {
|
|
var network = ctx.store.network;
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
delete ctx.channels[cursorChan];
|
|
return true;
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, clientId) {
|
|
var filter = function(c) {
|
|
return c !== clientId;
|
|
};
|
|
var chan;
|
|
for (var k in ctx.channels) {
|
|
chan = ctx.channels[k];
|
|
chan.clients = chan.clients.filter(filter);
|
|
if (chan.clients.length === 0) {
|
|
if (chan.wc) {
|
|
chan.wc.leave();
|
|
}
|
|
if (chan.onReconnect) {
|
|
var network = ctx.store.network;
|
|
network.off("reconnect", chan.onReconnect);
|
|
}
|
|
delete ctx.channels[k];
|
|
}
|
|
}
|
|
delete ctx.clients[clientId];
|
|
};
|
|
Integration.init = function(cfg, waitFor, emit) {
|
|
var integration = {};
|
|
if (cfg.store && cfg.store.modules && cfg.store.modules["integration"]) {
|
|
return cfg.store.modules["integration"];
|
|
}
|
|
var ctx = {
|
|
store: cfg.store,
|
|
emit,
|
|
channels: {},
|
|
clients: {}
|
|
};
|
|
integration.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
integration.leavePad = function(padChan) {
|
|
leaveChannel(ctx, padChan);
|
|
};
|
|
integration.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "INIT") {
|
|
return void initIntegration(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SEND") {
|
|
return void sendMsg(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return integration;
|
|
};
|
|
return Integration;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCrypto());
|
|
}
|
|
})();
|
|
})(integration);
|
|
return integration.exports;
|
|
}
|
|
var onlyoffice = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredOnlyoffice;
|
|
function requireOnlyoffice() {
|
|
if (hasRequiredOnlyoffice) return onlyoffice.exports;
|
|
hasRequiredOnlyoffice = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = () => {
|
|
var OO = {};
|
|
var getHistory = function(ctx, client, cb) {
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var chan = ctx.channels[c.channel];
|
|
if (!chan) {
|
|
return void cb({
|
|
error: "ENOCHAN"
|
|
});
|
|
}
|
|
cb();
|
|
chan.history.forEach((function(msg) {
|
|
ctx.emit("MESSAGE", {
|
|
msg,
|
|
validateKey: chan.validateKey
|
|
}, [ client ]);
|
|
}));
|
|
ctx.emit("HISTORY_SYNCED", {}, [ client ]);
|
|
};
|
|
var openChannel = function(ctx, obj, client, cb) {
|
|
var channel = obj.channel;
|
|
var padChan = obj.padChan;
|
|
var network = ctx.store.network;
|
|
var first = true;
|
|
var c = ctx.clients[client];
|
|
if (!c) {
|
|
c = ctx.clients[client] = {
|
|
channel
|
|
};
|
|
} else {
|
|
return void cb();
|
|
}
|
|
var chan = ctx.channels[channel];
|
|
if (chan) {
|
|
if (!c.id) {
|
|
c.id = chan.wc.myID + "-" + client;
|
|
}
|
|
getHistory(ctx, client, (function() {
|
|
ctx.emit("READY", chan.clients, [ client ]);
|
|
}));
|
|
chan.clients.push(client);
|
|
return void cb();
|
|
}
|
|
var txid = Math.floor(Math.random() * 1e6);
|
|
var onOpen = function(wc) {
|
|
ctx.channels[channel] = ctx.channels[channel] || {
|
|
history: [],
|
|
validateKey: obj.validateKey
|
|
};
|
|
chan = ctx.channels[channel];
|
|
chan.padChan = padChan;
|
|
if (!c.id) {
|
|
c.id = wc.myID + "-" + client;
|
|
}
|
|
if (chan.clients) {
|
|
chan.clients.forEach((function(cl) {
|
|
if (ctx.clients[cl]) {
|
|
ctx.clients[cl].id = wc.myID + "-" + cl;
|
|
}
|
|
}));
|
|
}
|
|
wc.on("join", (function() {}));
|
|
wc.on("leave", (function() {}));
|
|
wc.on("message", (function(msg) {
|
|
chan.history.push(msg);
|
|
ctx.emit("MESSAGE", {
|
|
msg,
|
|
validateKey: chan.validateKey
|
|
}, chan.clients);
|
|
}));
|
|
chan.wc = wc;
|
|
chan.sendMsg = function(msg, cb) {
|
|
cb = cb || function() {};
|
|
var hash = msg.slice(0, 64);
|
|
wc.bcast(msg).then((function() {
|
|
chan.history.push(msg);
|
|
chan.lastKnownHash = hash;
|
|
cb();
|
|
}), (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
}));
|
|
};
|
|
if (first) {
|
|
chan.clients = [ client ];
|
|
chan.lastCpHash = obj.lastCpHash;
|
|
first = false;
|
|
cb();
|
|
}
|
|
var hk = network.historyKeeper;
|
|
var cfg = {
|
|
txid,
|
|
lastKnownHash: chan.lastKnownHash || chan.lastCpHash,
|
|
metadata: {
|
|
validateKey: obj.validateKey,
|
|
owners: obj.owners,
|
|
expire: obj.expire
|
|
}
|
|
};
|
|
var msg = [ "GET_HISTORY", wc.id, cfg ];
|
|
if (hk) {
|
|
network.sendto(hk, JSON.stringify(msg)).then((function() {}), (function(err) {
|
|
console.error(err);
|
|
}));
|
|
}
|
|
};
|
|
network.on("message", (function(msg, sender) {
|
|
if (!ctx.channels[channel]) {
|
|
return;
|
|
}
|
|
var hk = network.historyKeeper;
|
|
if (sender !== hk) {
|
|
return;
|
|
}
|
|
var parsed;
|
|
try {
|
|
parsed = JSON.parse(msg);
|
|
} catch (e) {}
|
|
if (!parsed) {
|
|
return;
|
|
}
|
|
if (parsed.txid && parsed.txid !== txid) {
|
|
return;
|
|
}
|
|
if (parsed.channel && parsed.channel !== channel) {
|
|
return;
|
|
}
|
|
if (parsed.validateKey && parsed.channel) {
|
|
if (!chan.validateKey) {
|
|
chan.validateKey = parsed.validateKey;
|
|
}
|
|
return;
|
|
}
|
|
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
|
ctx.emit("READY", chan.clients, chan.clients);
|
|
return;
|
|
}
|
|
if (parsed.error && parsed.channel) {
|
|
return;
|
|
}
|
|
if (Array.isArray(parsed) && parsed[0] && parsed[0] !== txid) {
|
|
return;
|
|
}
|
|
msg = parsed[4];
|
|
if (parsed[3] !== channel) {
|
|
return;
|
|
}
|
|
var hash = msg.slice(0, 64);
|
|
if (hash === chan.lastKnownHash || hash === chan.lastCpHash) {
|
|
return;
|
|
}
|
|
chan.lastKnownHash = hash;
|
|
ctx.emit("MESSAGE", {
|
|
msg
|
|
}, chan.clients);
|
|
chan.history.push(msg);
|
|
}));
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}));
|
|
network.on("reconnect", (function() {
|
|
if (!ctx.channels[channel]) {
|
|
return;
|
|
}
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
console.error(err);
|
|
}));
|
|
}));
|
|
};
|
|
var updateHash = function(ctx, data, clientId, cb) {
|
|
var c = ctx.clients[clientId];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "NOT_IN_CHANNEL"
|
|
});
|
|
}
|
|
var chan = ctx.channels[c.channel];
|
|
if (!chan) {
|
|
return void cb({
|
|
error: "INVALID_CHANNEL"
|
|
});
|
|
}
|
|
var hash = data;
|
|
var index = -1;
|
|
chan.history.some((function(msg, idx) {
|
|
if (msg.slice(0, 64) === hash) {
|
|
index = idx + 1;
|
|
return true;
|
|
}
|
|
}));
|
|
if (index !== -1) {
|
|
chan.history = chan.history.slice(index);
|
|
}
|
|
cb();
|
|
};
|
|
var sendMessage = function(ctx, data, clientId, cb) {
|
|
var c = ctx.clients[clientId];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "NOT_IN_CHANNEL"
|
|
});
|
|
}
|
|
var chan = ctx.channels[c.channel];
|
|
if (!chan) {
|
|
return void cb({
|
|
error: "INVALID_CHANNEL"
|
|
});
|
|
}
|
|
var _cb = function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb(obj);
|
|
}
|
|
ctx.emit("MESSAGE", {
|
|
msg: data.msg
|
|
}, chan.clients.filter((function(cl) {
|
|
return cl !== clientId;
|
|
})));
|
|
cb();
|
|
};
|
|
if (data.isCp) {
|
|
return void chan.sendMsg(data.isCp, _cb);
|
|
}
|
|
chan.sendMsg(data.msg, _cb);
|
|
};
|
|
var reencrypt = function(ctx, data, cId, cb) {
|
|
var channel = data.channel;
|
|
var network = ctx.store.network;
|
|
var onOpen = function(wc) {
|
|
var hk = network.historyKeeper;
|
|
var cfg = {
|
|
metadata: data.metadata
|
|
};
|
|
var msg = [ "GET_HISTORY", wc.id, cfg ];
|
|
network.sendto(hk, JSON.stringify(msg));
|
|
data.msgs.forEach((function(msg) {
|
|
wc.bcast(msg);
|
|
}));
|
|
wc.leave();
|
|
cb();
|
|
};
|
|
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, (function(e, response) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
var isNew;
|
|
if (response && response.length && typeof response[0] === "object") {
|
|
isNew = response[0].isNew;
|
|
} else {
|
|
cb({
|
|
error: "INVALID_RESPONSE"
|
|
});
|
|
}
|
|
if (!isNew) {
|
|
return void cb({
|
|
error: "EEXISTS"
|
|
});
|
|
}
|
|
network.join(channel).then(onOpen, (function(err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var leaveChannel = function(ctx, padChan) {
|
|
Object.keys(ctx.channels).some((function(ooChan) {
|
|
var channel = ctx.channels[ooChan];
|
|
if (channel.padChan !== padChan) {
|
|
return;
|
|
}
|
|
if (channel.wc) {
|
|
channel.wc.leave();
|
|
}
|
|
delete ctx.channels[ooChan];
|
|
return true;
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, clientId) {
|
|
var filter = function(c) {
|
|
return c !== clientId;
|
|
};
|
|
var chan;
|
|
for (var k in ctx.channels) {
|
|
chan = ctx.channels[k];
|
|
chan.clients = chan.clients.filter(filter);
|
|
if (chan.clients.length === 0) {
|
|
if (chan.wc) {
|
|
chan.wc.leave();
|
|
}
|
|
delete ctx.channels[k];
|
|
}
|
|
}
|
|
if (ctx.clients[clientId]) {
|
|
var oldChannel = ctx.clients[clientId].channel;
|
|
var oldChan = ctx.channels[oldChannel];
|
|
if (oldChan) {
|
|
ctx.emit("LEAVE", {
|
|
id: clientId
|
|
}, [ oldChan.clients[0] ]);
|
|
}
|
|
delete ctx.clients[clientId];
|
|
}
|
|
};
|
|
OO.init = function(store, emit) {
|
|
var oo = {};
|
|
var ctx = {
|
|
store,
|
|
emit,
|
|
channels: {},
|
|
clients: {}
|
|
};
|
|
oo.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
oo.leavePad = function(padChan) {
|
|
leaveChannel(ctx, padChan);
|
|
};
|
|
oo.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "SEND_MESSAGE") {
|
|
return void sendMessage(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "UPDATE_HASH") {
|
|
return void updateHash(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "OPEN_CHANNEL") {
|
|
return void openChannel(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_HISTORY") {
|
|
return void getHistory(ctx, clientId, cb);
|
|
}
|
|
if (cmd === "REENCRYPT") {
|
|
return void reencrypt(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return oo;
|
|
};
|
|
return OO;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory();
|
|
}
|
|
})();
|
|
})(onlyoffice);
|
|
return onlyoffice.exports;
|
|
}
|
|
var profile = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredProfile;
|
|
function requireProfile() {
|
|
if (hasRequiredProfile) return profile.exports;
|
|
hasRequiredProfile = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, Constants, Realtime, Listmap, Crypto, ChainPad) => {
|
|
var Profile = {};
|
|
var initializeProfile = function(ctx, cb) {
|
|
var profile = ctx.profile;
|
|
if (!profile.edit || !profile.view) {
|
|
var hash = Hash.createRandomHash("profile");
|
|
var secret = Hash.getSecrets("profile", hash);
|
|
ctx.pinPads([ secret.channel ], (function(res) {
|
|
if (res.error) {
|
|
return void cb(res.error);
|
|
}
|
|
profile.edit = Hash.getEditHashFromKeys(secret);
|
|
profile.view = Hash.getViewHashFromKeys(secret);
|
|
setTimeout(cb);
|
|
}));
|
|
return;
|
|
}
|
|
setTimeout(cb);
|
|
};
|
|
var openChannel = function(ctx) {
|
|
var profile = ctx.profile;
|
|
var secret = Hash.getSecrets("profile", profile.edit);
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
var cfg = {
|
|
data: {},
|
|
network: ctx.store.network,
|
|
channel: secret.channel,
|
|
crypto,
|
|
owners: [ ctx.store.proxy.edPublic ],
|
|
ChainPad,
|
|
validateKey: secret.keys.validateKey || undefined,
|
|
userName: "profile",
|
|
classic: true
|
|
};
|
|
var lm = Listmap.create(cfg);
|
|
lm.proxy.on("create", (function() {})).on("ready", (function() {
|
|
lm.proxy.name = ctx.store.proxy[Constants.displayNameKey] || "";
|
|
ctx.listmap = lm;
|
|
if (!lm.proxy.curvePublic) {
|
|
lm.proxy.curvePublic = ctx.store.proxy.curvePublic;
|
|
}
|
|
if (!lm.proxy.notifications) {
|
|
lm.proxy.notifications = Util.find(ctx.store.proxy, [ "mailboxes", "notifications", "channel" ]);
|
|
}
|
|
if (!lm.proxy.edPublic) {
|
|
lm.proxy.edPublic = ctx.store.proxy.edPublic;
|
|
}
|
|
if (ctx.onReadyHandlers.length) {
|
|
ctx.onReadyHandlers.forEach((function(f) {
|
|
try {
|
|
f(lm.proxy);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
ctx.onReadyHandlers = [];
|
|
}
|
|
})).on("change", [], (function() {
|
|
ctx.emit("UPDATE", lm.proxy, ctx.clients);
|
|
}));
|
|
};
|
|
var setName = function(ctx, value) {
|
|
ctx.listmap.proxy.name = value;
|
|
Realtime.whenRealtimeSyncs(ctx.listmap.realtime, (function() {
|
|
if (!ctx.listmap) {
|
|
return;
|
|
}
|
|
ctx.emit("UPDATE", ctx.listmap.proxy, ctx.clients);
|
|
}));
|
|
};
|
|
var subscribe = function(ctx, data, cId, cb) {
|
|
var idx = ctx.clients.indexOf(cId);
|
|
if (idx === -1) {
|
|
ctx.clients.push(cId);
|
|
}
|
|
if (ctx.listmap) {
|
|
return void cb(ctx.listmap.proxy);
|
|
}
|
|
ctx.onReadyHandlers.push((function(proxy) {
|
|
cb(proxy);
|
|
}));
|
|
};
|
|
var setValue = function(ctx, data, cId, cb) {
|
|
var key = data.key;
|
|
var value = data.value;
|
|
if (!key) {
|
|
return;
|
|
}
|
|
ctx.listmap.proxy[key] = value;
|
|
Realtime.whenRealtimeSyncs(ctx.listmap.realtime, (function() {
|
|
ctx.emit("UPDATE", ctx.listmap.proxy, ctx.clients.filter((function(clientId) {
|
|
return clientId !== cId;
|
|
})));
|
|
cb(ctx.listmap.proxy);
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, cId) {
|
|
var idx = ctx.clients.indexOf(cId);
|
|
if (idx !== -1) {
|
|
ctx.clients.splice(idx, 1);
|
|
}
|
|
};
|
|
Profile.init = function(cfg, waitFor, emit) {
|
|
var profile = {};
|
|
var store = cfg.store;
|
|
if (!store.loggedIn || !store.proxy.edPublic) {
|
|
return;
|
|
}
|
|
var ctx = {
|
|
store,
|
|
pinPads: cfg.pinPads,
|
|
updateMetadata: cfg.updateMetadata,
|
|
emit,
|
|
onReadyHandlers: [],
|
|
clients: []
|
|
};
|
|
ctx.profile = store.proxy.profile = store.proxy.profile || {};
|
|
initializeProfile(ctx, waitFor((function(err) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
openChannel(ctx);
|
|
})));
|
|
profile.setName = function(value) {
|
|
setName(ctx, value);
|
|
};
|
|
profile.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
profile.update = function() {
|
|
if (!ctx.listmap) {
|
|
return;
|
|
}
|
|
ctx.emit("UPDATE", ctx.listmap.proxy, ctx.clients);
|
|
};
|
|
profile.execCommand = function(clientId, obj, cb) {
|
|
console.log(obj);
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "SUBSCRIBE") {
|
|
return void subscribe(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SET") {
|
|
return void setValue(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return profile;
|
|
};
|
|
return Profile;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireCommonConstants(), requireCommonRealtime(), requireChainpadListmap(), requireCrypto(), requireChainpad_dist());
|
|
}
|
|
})();
|
|
})(profile);
|
|
return profile.exports;
|
|
}
|
|
var team = {
|
|
exports: {}
|
|
};
|
|
var roster = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredRoster;
|
|
function requireRoster() {
|
|
if (hasRequiredRoster) return roster.exports;
|
|
hasRequiredRoster = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Util, Hash, CPNetflux, Sortify, nThen, Crypto, Feedback) {
|
|
var Roster = {};
|
|
var CHECKPOINT_INTERVAL = 25;
|
|
var TIMEOUT_INTERVAL = 3e4;
|
|
var isMap = function(obj) {
|
|
return Boolean(obj && typeof obj === "object" && !Array.isArray(obj));
|
|
};
|
|
var getMessageId = function(msgString) {
|
|
return msgString.slice(0, 64);
|
|
};
|
|
var canCheckpoint = function(author, members) {
|
|
var role = Util.find(members, [ author, "role" ]);
|
|
return [ "OWNER", "ADMIN" ].indexOf(role) !== -1;
|
|
};
|
|
var isValidRole = function(role) {
|
|
return [ "OWNER", "ADMIN", "MEMBER", "VIEWER" ].indexOf(role) !== -1;
|
|
};
|
|
var isSelfDowngrade = function(author, curve, role, state) {
|
|
var selfDescribe = author === curve && state[curve];
|
|
if (!selfDescribe) {
|
|
return false;
|
|
}
|
|
var authorRole = Util.find(state, [ author, "role" ]);
|
|
if (authorRole === "MEMBER") {
|
|
return role === "VIEWER";
|
|
}
|
|
};
|
|
var canAddRole = function(author, role, members) {
|
|
var authorRole = Util.find(members, [ author, "role" ]);
|
|
if (!authorRole) {
|
|
return false;
|
|
}
|
|
if (!isValidRole(role)) {
|
|
return false;
|
|
}
|
|
if (authorRole === "OWNER") {
|
|
return true;
|
|
}
|
|
if (authorRole === "ADMIN") {
|
|
return [ "ADMIN", "MEMBER", "VIEWER" ].indexOf(role) !== -1;
|
|
}
|
|
return false;
|
|
};
|
|
var isValidId = function(id) {
|
|
return typeof id === "string" && id.length === 44;
|
|
};
|
|
var canDescribeTarget = function(author, curve, state) {
|
|
if (!state[curve]) {
|
|
return false;
|
|
}
|
|
if (author === curve && state[curve]) {
|
|
return true;
|
|
}
|
|
var authorRole = Util.find(state, [ author, "role" ]);
|
|
var targetRole = Util.find(state, [ curve, "role" ]);
|
|
if (!authorRole) {
|
|
return false;
|
|
}
|
|
if (authorRole === "OWNER") {
|
|
return true;
|
|
}
|
|
if (authorRole === "ADMIN" && targetRole !== "OWNER") {
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var canRemoveRole = function(author, role, members) {
|
|
var authorRole = Util.find(members, [ author, "role" ]);
|
|
if (!authorRole) {
|
|
return false;
|
|
}
|
|
if (authorRole === "OWNER") {
|
|
return true;
|
|
}
|
|
if (authorRole === "ADMIN") {
|
|
return [ "ADMIN", "MEMBER", "VIEWER" ].indexOf(role) !== -1;
|
|
}
|
|
return false;
|
|
};
|
|
var canUpdateMetadata = function(author, members) {
|
|
var authorRole = Util.find(members, [ author, "role" ]);
|
|
return Boolean(authorRole && [ "OWNER", "ADMIN" ].indexOf(authorRole) !== -1);
|
|
};
|
|
var shouldCheckpoint = function(me, ref) {
|
|
if (!canCheckpoint(me, ref.state.members)) {
|
|
return false;
|
|
}
|
|
var since = ref.internal.sinceLastCheckpoint;
|
|
if (!since || typeof since !== "number" || since < CHECKPOINT_INTERVAL) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
var commands = Roster.commands = {};
|
|
commands.ADD = function(args, author, roster) {
|
|
if (!isMap(args)) {
|
|
throw new Error("INVALID ARGS");
|
|
}
|
|
if (!roster.internal.initialized) {
|
|
throw new Error("UNITIALIZED");
|
|
}
|
|
if (typeof roster.state.members === "undefined") {
|
|
throw new Error("CANNOT_ADD_TO_UNITIALIZED_ROSTER");
|
|
}
|
|
var members = roster.state.members;
|
|
Object.keys(args).forEach((function(curve) {
|
|
if (!isValidId(curve)) {
|
|
console.log(curve, curve.length);
|
|
throw new Error("INVALID_CURVE_KEY");
|
|
}
|
|
if (!isMap(args[curve])) {
|
|
throw new Error("INVALID_CONTENT");
|
|
}
|
|
if (members[curve]) {
|
|
throw new Error("ALREADY_PRESENT");
|
|
}
|
|
var data = args[curve];
|
|
if (typeof data.role !== "string") {
|
|
data.role = "MEMBER";
|
|
}
|
|
if (!canAddRole(author, data.role, members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
if (typeof data.displayName !== "string") {
|
|
throw new Error("DISPLAYNAME_REQUIRED");
|
|
}
|
|
if (typeof data.notifications !== "string") {
|
|
throw new Error("NOTIFICATIONS_REQUIRED");
|
|
}
|
|
}));
|
|
var changed = false;
|
|
Object.keys(args).forEach((function(curve) {
|
|
changed = true;
|
|
members[curve] = args[curve];
|
|
}));
|
|
return changed;
|
|
};
|
|
commands.RM = function(args, author, roster) {
|
|
if (!Array.isArray(args)) {
|
|
throw new Error("INVALID_ARGS");
|
|
}
|
|
if (typeof roster.state.members === "undefined") {
|
|
throw new Error("CANNOT_RM_FROM_UNITIALIZED_ROSTER");
|
|
}
|
|
var members = roster.state.members;
|
|
args.forEach((function(curve) {
|
|
if (!isValidId(curve)) {
|
|
throw new Error("INVALID_CURVE_KEY");
|
|
}
|
|
if (curve === author) {
|
|
return;
|
|
}
|
|
var role = members[curve].role;
|
|
if (!canRemoveRole(author, role, members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
}));
|
|
var changed = false;
|
|
args.forEach((function(curve) {
|
|
if (!members[curve]) {
|
|
return;
|
|
}
|
|
changed = true;
|
|
delete members[curve];
|
|
}));
|
|
return changed;
|
|
};
|
|
commands.DESCRIBE = function(args, author, roster) {
|
|
if (!args || typeof args !== "object" || Array.isArray(args)) {
|
|
throw new Error("INVALID_ARGUMENTS");
|
|
}
|
|
if (typeof roster.state.members === "undefined") {
|
|
throw new Error("NOT_READY");
|
|
}
|
|
var members = roster.state.members;
|
|
Object.keys(args).forEach((function(curve) {
|
|
if (!isValidId(curve)) {
|
|
throw new Error("INVALID_ID");
|
|
}
|
|
if (!members[curve]) {
|
|
throw new Error("NOT_PRESENT");
|
|
}
|
|
if (!canDescribeTarget(author, curve, members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
var data = args[curve];
|
|
if (!isMap(data)) {
|
|
throw new Error("INVALID_ARGUMENTS");
|
|
}
|
|
var current = Util.clone(members[curve]);
|
|
if (typeof data.role === "string") {
|
|
if (!isSelfDowngrade(author, curve, data.role, members) && !canAddRole(author, data.role, members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
}
|
|
if (typeof current.displayName !== "string" && typeof data.displayName !== "string") {
|
|
throw new Error("DISPLAYNAME_REQUIRED");
|
|
}
|
|
if ([ "undefined", "string" ].indexOf(typeof data.displayName) === -1) {
|
|
throw new Error("INVALID_DISPLAYNAME");
|
|
}
|
|
if (typeof current.notifications !== "string" && typeof data.notifications !== "string") {
|
|
throw new Error("NOTIFICATIONS_REQUIRED");
|
|
}
|
|
if ([ "undefined", "string" ].indexOf(typeof data.notifications) === -1) {
|
|
throw new Error("INVALID_NOTIFICATIONS");
|
|
}
|
|
}));
|
|
var changed = false;
|
|
Object.keys(args).forEach((function(curve) {
|
|
var current = Util.clone(members[curve]);
|
|
var data = args[curve];
|
|
Object.keys(data).forEach((function(key) {
|
|
if (typeof current[key] !== "undefined" && data[key] === null) {
|
|
return void delete current[key];
|
|
}
|
|
current[key] = data[key];
|
|
}));
|
|
if (Sortify(current) !== Sortify(members[curve])) {
|
|
changed = true;
|
|
members[curve] = current;
|
|
}
|
|
}));
|
|
return changed;
|
|
};
|
|
commands.CHECKPOINT = function(args, author, roster) {
|
|
if (!isMap(args)) {
|
|
throw new Error("INVALID_CHECKPOINT_STATE");
|
|
}
|
|
if (!roster.internal.initialized) {
|
|
roster.state = args;
|
|
var metadata = roster.state.metadata = roster.state.metadata || {};
|
|
metadata.topic = metadata.topic || "";
|
|
metadata.name = metadata.name || "";
|
|
metadata.avatar = metadata.avatar || "";
|
|
roster.internal.initialized = true;
|
|
return true;
|
|
} else if (Sortify(args) !== Sortify(roster.state)) {
|
|
throw new Error("CHECKPOINT_DOES_NOT_MATCH_PREVIOUS_STATE");
|
|
}
|
|
if (!canCheckpoint(author, roster.state.members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
roster.state = args;
|
|
return true;
|
|
};
|
|
var MANDATORY_METADATA_FIELDS = [ "avatar", "name", "topic" ];
|
|
commands.METADATA = function(args, author, roster) {
|
|
if (!isMap(args)) {
|
|
throw new Error("INVALID_ARGS");
|
|
}
|
|
if (!canUpdateMetadata(author, roster.state.members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
Object.keys(args).forEach((function(k) {
|
|
if (args[k] === null) {
|
|
if (MANDATORY_METADATA_FIELDS.indexOf(k) === -1) {
|
|
return;
|
|
}
|
|
throw new Error("CANNOT_REMOVE_MANDATORY_METADATA");
|
|
}
|
|
if (typeof args[k] !== "string") {
|
|
throw new Error("INVALID_ARGUMENTS");
|
|
}
|
|
}));
|
|
var changed = false;
|
|
Object.keys(args).forEach((function(k) {
|
|
if (typeof roster.state.metadata[k] !== "undefined" && args[k] === null) {
|
|
changed = true;
|
|
delete roster.state.metadata[k];
|
|
}
|
|
if (args[k] === roster.state.metadata[k]) {
|
|
return;
|
|
}
|
|
changed = true;
|
|
roster.state.metadata[k] = args[k];
|
|
}));
|
|
return changed;
|
|
};
|
|
commands.INVITE = function(args, author, roster) {
|
|
if (!isMap(args)) {
|
|
throw new Error("INVALID_ARGS");
|
|
}
|
|
if (!roster.internal.initialized) {
|
|
throw new Error("UNINITIALIED");
|
|
}
|
|
if (typeof roster.state.members === "undefined") {
|
|
throw new Error("CANNOT+INVITE_TO_UNINITIALIED_ROSTER");
|
|
}
|
|
var members = roster.state.members;
|
|
Object.keys(args).forEach((function(curve) {
|
|
if (!isValidId(curve)) {
|
|
console.log(curve, curve.length);
|
|
throw new Error("INVALID_CURVE_KEY");
|
|
}
|
|
if (!isMap(args[curve])) {
|
|
throw new Error("INVALID_CONTENT");
|
|
}
|
|
if (members[curve]) {
|
|
throw new Error("ARLEADY_PRESENT");
|
|
}
|
|
var data = args[curve];
|
|
if (typeof data.role !== "string") {
|
|
data.role = "VIEWER";
|
|
}
|
|
if (typeof data.pending === "undefined") {
|
|
data.pending = true;
|
|
}
|
|
if (!canAddRole(author, data.role, members)) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
if (typeof data.displayName !== "string" || !data.displayName) {
|
|
throw new Error("DISPLAYNAME_REQUIRED");
|
|
}
|
|
}));
|
|
var changed = false;
|
|
Object.keys(args).forEach((function(curve) {
|
|
changed = true;
|
|
members[curve] = args[curve];
|
|
}));
|
|
return changed;
|
|
};
|
|
commands.ACCEPT = function(args, author, roster) {
|
|
if (!roster.internal.initialized) {
|
|
throw new Error("UNINITIALIED");
|
|
}
|
|
if (typeof roster.state.members === "undefined") {
|
|
throw new Error("CANNOT_ADD_TO_UNINITIALIED_ROSTER");
|
|
}
|
|
var members = roster.state.members;
|
|
if (!isMap(members[author])) {
|
|
throw new Error("INSUFFICIENT_PERMISSIONS");
|
|
}
|
|
if (!members[author].pending) {
|
|
throw new Error("ALREADY_PRESENT");
|
|
}
|
|
if (typeof args !== "string") {
|
|
throw new Error("INVALID_ARGS");
|
|
}
|
|
if (!isValidId(args)) {
|
|
throw new Error("INVALID_CURVE_KEY");
|
|
}
|
|
var curve = args;
|
|
if (typeof members[curve] !== "undefined") {
|
|
throw new Error("MEMBER_ALREADY_PRESENT");
|
|
}
|
|
var clone = Util.clone(members[author]);
|
|
delete clone.remaining;
|
|
delete clone.totalUses;
|
|
delete clone.inviteChannel;
|
|
delete clone.previewChannel;
|
|
members[curve] = clone;
|
|
var remaining = members[author].remaining || 1;
|
|
if (remaining === -1) {
|
|
return true;
|
|
}
|
|
if (remaining > 1) {
|
|
members[author].remaining = remaining - 1;
|
|
} else {
|
|
delete members[author];
|
|
}
|
|
return true;
|
|
};
|
|
var handleCommand = function(content, author, roster) {
|
|
if (!(Array.isArray(content) && typeof author === "string")) {
|
|
throw new Error("INVALID ARGUMENTS");
|
|
}
|
|
var command = content[0];
|
|
if (typeof commands[command] !== "function") {
|
|
throw new Error("INVALID_COMMAND");
|
|
}
|
|
return commands[command](content[1], author, roster);
|
|
};
|
|
var simulate = function(content, author, roster) {
|
|
return handleCommand(content, author, Util.clone(roster));
|
|
};
|
|
Roster.create = function(config, _cb) {
|
|
if (typeof _cb !== "function") {
|
|
throw new Error("EXPECTED_CALLBACK");
|
|
}
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!config.network) {
|
|
return void cb("EXPECTED_NETWORK");
|
|
}
|
|
if (!config.channel || typeof config.channel !== "string" || config.channel.length !== 32) {
|
|
return void cb("EXPECTED_CHANNEL");
|
|
}
|
|
if (!config.keys || typeof config.keys !== "object") {
|
|
return void cb("EXPECTED_CRYPTO_KEYS");
|
|
}
|
|
if (!config.store) {
|
|
return void cb("EXPECTED_STORE");
|
|
}
|
|
var response = Util.response((function(label, info) {
|
|
console.error("ROSTER_RESPONSE__" + label, info);
|
|
}));
|
|
var store = config.store;
|
|
var keys = config.keys;
|
|
var me = keys.myCurvePublic;
|
|
var channel = config.channel;
|
|
var lastKnownHash = config.lastKnownHash || -1;
|
|
if (config.newTeam) {
|
|
lastKnownHash = undefined;
|
|
}
|
|
var ref = {
|
|
state: {
|
|
members: {},
|
|
metadata: {}
|
|
},
|
|
internal: {
|
|
initialized: false,
|
|
sinceLastCheckpoint: 0,
|
|
lastCheckpointHash: lastKnownHash
|
|
}
|
|
};
|
|
var roster = {};
|
|
var events = {
|
|
change: Util.mkEvent(),
|
|
checkpoint: Util.mkEvent()
|
|
};
|
|
roster.on = function(key, handler) {
|
|
if (typeof events[key] !== "object") {
|
|
throw new Error("unsupported event");
|
|
}
|
|
events[key].reg(handler);
|
|
return roster;
|
|
};
|
|
roster.off = function(key, handler) {
|
|
if (typeof events[key] !== "object") {
|
|
throw new Error("unsupported event");
|
|
}
|
|
events[key].unreg(handler);
|
|
return roster;
|
|
};
|
|
roster.once = function(key, handler) {
|
|
if (typeof events[key] !== "object") {
|
|
throw new Error("unsupported event");
|
|
}
|
|
var f = function() {
|
|
handler.apply(null, Array.prototype.slice.call(arguments));
|
|
events[key].unreg(f);
|
|
};
|
|
events[key].reg(f);
|
|
return roster;
|
|
};
|
|
roster.getState = function() {
|
|
return Util.clone(ref.state);
|
|
};
|
|
roster.getLastCheckpointHash = function() {
|
|
return ref.internal.lastCheckpointHash || -1;
|
|
};
|
|
var clearPendingCheckpoints = function() {
|
|
if (ref.internal.pendingCheckpointId) {
|
|
response.clear(ref.internal.pendingCheckpointId);
|
|
delete ref.internal.pendingCheckpointId;
|
|
}
|
|
clearTimeout(ref.internal.checkpointTimeout);
|
|
delete ref.internal.checkpointTimeout;
|
|
};
|
|
roster.stop = function() {
|
|
if (ref.internal.cpNetflux && typeof ref.internal.cpNetflux.stop === "function") {
|
|
ref.internal.cpNetflux.stop();
|
|
clearPendingCheckpoints();
|
|
} else {
|
|
console.log("FAILED TO LEAVE");
|
|
}
|
|
};
|
|
var ready = false;
|
|
var onCacheReady = function() {
|
|
if (!config.onCacheReady) {
|
|
return;
|
|
}
|
|
var state = ref.state;
|
|
if (!Object.keys(state.members || {}).length) {
|
|
try {
|
|
ref.internal.cpNetflux.resetCache();
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
return void config.onCacheReady({
|
|
error: "CORRUPTED"
|
|
});
|
|
}
|
|
config.onCacheReady(roster);
|
|
};
|
|
var onReady = function() {
|
|
ready = true;
|
|
cb(void 0, roster);
|
|
};
|
|
var onChannelError = function(info) {
|
|
if (info && info.type === "EUNKNOWN") {
|
|
return;
|
|
}
|
|
if (!ready) {
|
|
return void cb(info);
|
|
}
|
|
console.error("CHANNEL_ERROR", info);
|
|
};
|
|
var onConnectionChange = function(info) {
|
|
if (info.state) {
|
|
return;
|
|
}
|
|
ready = false;
|
|
};
|
|
var onConnect = function() {
|
|
console.log("ROSTER CONNECTED");
|
|
};
|
|
var isReady = function() {
|
|
return Boolean(ready && me);
|
|
};
|
|
var onMessage = function(msg, user, vKey, isCp, hash, author) {
|
|
ref.internal.sinceLastCheckpoint++;
|
|
var parsed = Util.tryParse(msg);
|
|
if (!parsed) {
|
|
return void console.error("could not parse");
|
|
}
|
|
var changed;
|
|
var error;
|
|
try {
|
|
changed = handleCommand(parsed, author, ref);
|
|
} catch (err) {
|
|
error = err.message;
|
|
}
|
|
var id = getMessageId(hash);
|
|
if (response.expected(id)) {
|
|
if (error) {
|
|
return void response.handle(id, [ error ]);
|
|
}
|
|
try {
|
|
if (!changed) {
|
|
response.handle(id, [ "NO_CHANGE" ]);
|
|
console.log(msg);
|
|
} else {
|
|
response.handle(id, [ void 0, roster.getState() ]);
|
|
}
|
|
} catch (err) {
|
|
console.log("CAUGHT", err);
|
|
}
|
|
}
|
|
if (parsed[0] === "CHECKPOINT" && changed) {
|
|
if (isReady()) {
|
|
events.checkpoint.fire(hash);
|
|
}
|
|
ref.internal.sinceLastCheckpoint = 0;
|
|
ref.internal.lastCheckpointHash = hash;
|
|
} else if (changed) {
|
|
if (isReady()) {
|
|
events.change.fire();
|
|
}
|
|
}
|
|
clearPendingCheckpoints();
|
|
if (!isReady() || !shouldCheckpoint(me, ref)) {
|
|
return;
|
|
}
|
|
var delay = 1e3 * Math.floor(Math.random() * 20) + 5e3;
|
|
ref.internal.checkpointTimeout = setTimeout((function() {
|
|
ref.internal.pendingCheckpointId = roster.checkpoint((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
}), delay);
|
|
};
|
|
var isCacheCheckpoint = function(msg, author) {
|
|
var parsed = Util.tryParse(msg);
|
|
if (parsed[0] !== "CHECKPOINT") {
|
|
return false;
|
|
}
|
|
var changed = simulate(parsed, author, ref);
|
|
return changed;
|
|
};
|
|
var metadata, crypto;
|
|
var send = function(msg, cb) {
|
|
if (!isReady()) {
|
|
return void cb("NOT_READY");
|
|
}
|
|
var anon_rpc = store.anon_rpc;
|
|
if (!anon_rpc) {
|
|
return void cb("ANON_RPC_NOT_READY");
|
|
}
|
|
var changed = false;
|
|
try {
|
|
changed = simulate(msg, keys.myCurvePublic, ref);
|
|
} catch (err) {
|
|
return void cb(err.message);
|
|
}
|
|
if (!changed) {
|
|
return void cb("NO_CHANGE");
|
|
}
|
|
var ciphertext = crypto.encrypt(Sortify(msg));
|
|
var id = getMessageId(ciphertext);
|
|
response.expect(id, (function(err, state) {
|
|
if (err) {
|
|
return void cb(err);
|
|
}
|
|
cb(void 0, state, id);
|
|
}), TIMEOUT_INTERVAL);
|
|
anon_rpc.send("WRITE_PRIVATE_MESSAGE", [ channel, ciphertext ], (function(err) {
|
|
if (err) {
|
|
return response.handle(id, [ err.message || err ]);
|
|
}
|
|
}));
|
|
return id;
|
|
};
|
|
roster.init = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (ref.internal.initialized) {
|
|
return void cb("ALREADY_INITIALIZED");
|
|
}
|
|
if (!isMap(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
data.role = "OWNER";
|
|
var members = {};
|
|
members[me] = data;
|
|
send([ "CHECKPOINT", {
|
|
members
|
|
} ], cb);
|
|
};
|
|
roster.checkpoint = function(_cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
send([ "CHECKPOINT", Util.clone(ref.state) ], cb);
|
|
};
|
|
roster.add = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!ref.internal.initialized) {
|
|
return cb("UNINITIALIZED");
|
|
}
|
|
if (!isMap(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
Object.keys(data).forEach((function(curve) {
|
|
if (!isValidId(curve) || isMap(ref.state.members[curve])) {
|
|
return delete data[curve];
|
|
}
|
|
}));
|
|
send([ "ADD", data ], cb);
|
|
};
|
|
roster.remove = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var state = ref.state;
|
|
if (!state) {
|
|
return cb("UNINITIALIZED");
|
|
}
|
|
if (!Array.isArray(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
var toRemove = [];
|
|
var current = Object.keys(state.members);
|
|
data.forEach((function(curve) {
|
|
if (current.indexOf(curve) === -1) {
|
|
return;
|
|
}
|
|
toRemove.push(curve);
|
|
}));
|
|
send([ "RM", toRemove ], cb);
|
|
};
|
|
roster.describe = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var state = ref.state;
|
|
if (!state) {
|
|
return cb("UNINITIALIZED");
|
|
}
|
|
if (!isMap(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
if (Object.keys(data).some((function(curve) {
|
|
var member = data[curve];
|
|
if (!isMap(member)) {
|
|
delete data[curve];
|
|
}
|
|
if (!isMap(state.members[curve])) {
|
|
return true;
|
|
}
|
|
Object.keys(member).forEach((function(k) {
|
|
if (member[k] === state.members[curve][k]) {
|
|
delete member[k];
|
|
}
|
|
}));
|
|
}))) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
send([ "DESCRIBE", data ], cb);
|
|
};
|
|
roster.metadata = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var metadata = ref.state.metadata;
|
|
if (!isMap(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
Object.keys(data).forEach((function(k) {
|
|
if (data[k] === metadata[k]) {
|
|
delete data[k];
|
|
}
|
|
}));
|
|
send([ "METADATA", data ], cb);
|
|
};
|
|
roster.invite = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var state = ref.state;
|
|
if (!state) {
|
|
return cb("UNINITIALIZED");
|
|
}
|
|
if (!ref.internal.initialized) {
|
|
return cb("UNINITIALIZED");
|
|
}
|
|
if (!isMap(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
var data = Util.clone(_data);
|
|
Object.keys(data).forEach((function(curve) {
|
|
if (!isValidId(curve) || isMap(ref.state.members[curve])) {
|
|
return delete data[curve];
|
|
}
|
|
}));
|
|
send([ "INVITE", data ], cb);
|
|
};
|
|
roster.accept = function(_data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (typeof _data !== "string" || !isValidId(_data)) {
|
|
return void cb("INVALID_ARGUMENTS");
|
|
}
|
|
send([ "ACCEPT", _data ], cb);
|
|
};
|
|
nThen((function(w) {
|
|
if (!store.anon_rpc) {
|
|
return;
|
|
}
|
|
store.anon_rpc.send("GET_METADATA", channel, (function(err, data) {
|
|
if (err) {
|
|
w.abort();
|
|
return void console.error(err);
|
|
}
|
|
metadata = ref.internal.metadata = data && data[0] || undefined;
|
|
}));
|
|
})).nThen((function(w) {
|
|
if (!config.keys.teamEdPublic && metadata && metadata.validateKey) {
|
|
config.keys.teamEdPublic = metadata.validateKey;
|
|
}
|
|
if (!config.keys.teamEdPublic) {
|
|
w.abort();
|
|
return void cb("NO_VALIDATE_KEY");
|
|
}
|
|
try {
|
|
crypto = Crypto.Team.createEncryptor(config.keys);
|
|
} catch (err) {
|
|
w.abort();
|
|
return void cb(err);
|
|
}
|
|
})).nThen((function() {
|
|
if (typeof lastKnownHash === "string") {
|
|
console.log("Synchronizing from checkpoint");
|
|
}
|
|
ref.internal.cpNetflux = CPNetflux.start({
|
|
lastKnownHash,
|
|
network: config.network,
|
|
channel: config.channel,
|
|
crypto,
|
|
validateKey: config.keys.teamEdPublic,
|
|
owners: config.owners,
|
|
Cache: config.Cache,
|
|
isCacheCheckpoint,
|
|
onCacheReady,
|
|
onChannelError,
|
|
onReady,
|
|
onConnect,
|
|
onConnectionChange,
|
|
onMessage,
|
|
noChainPad: true
|
|
});
|
|
}));
|
|
};
|
|
return Roster;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireChainpadNetflux(), requireJSON_sortify(), requireNthen(), requireCrypto());
|
|
}
|
|
})();
|
|
})(roster);
|
|
return roster.exports;
|
|
}
|
|
var invitation = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredInvitation;
|
|
function requireInvitation() {
|
|
if (hasRequiredInvitation) return invitation.exports;
|
|
hasRequiredInvitation = 1;
|
|
(function(module) {
|
|
(function() {
|
|
var factory = function(Util, Cred, Nacl, Crypto) {
|
|
var Invite = {};
|
|
var encode64 = Nacl.util.encodeBase64;
|
|
var decode64 = Nacl.util.decodeBase64;
|
|
Invite.generateKeys = function() {
|
|
var ed = Nacl.sign.keyPair();
|
|
var curve = Nacl.box.keyPair();
|
|
return {
|
|
edPublic: encode64(ed.publicKey),
|
|
edPrivate: encode64(ed.secretKey),
|
|
curvePublic: encode64(curve.publicKey),
|
|
curvePrivate: encode64(curve.secretKey)
|
|
};
|
|
};
|
|
Invite.generateSignPair = function() {
|
|
var ed = Nacl.sign.keyPair();
|
|
return {
|
|
validateKey: encode64(ed.publicKey),
|
|
signKey: encode64(ed.secretKey)
|
|
};
|
|
};
|
|
var b64ToChannelKeys = function(b64) {
|
|
var dispense = Cred.dispenser(decode64(b64));
|
|
return {
|
|
channel: Util.uint8ArrayToHex(dispense(16)),
|
|
cryptKey: dispense(Nacl.secretbox.keyLength)
|
|
};
|
|
};
|
|
Invite.deriveInviteKeys = b64ToChannelKeys;
|
|
Invite.derivePreviewKeys = b64ToChannelKeys;
|
|
Invite.createRosterEntry = function(roster, data, cb) {
|
|
var toInvite = {};
|
|
toInvite[data.curvePublic] = data.content;
|
|
roster.invite(toInvite, cb);
|
|
};
|
|
var decodeUTF8 = Nacl.util.decodeUTF8;
|
|
Invite.encryptHash = function(data, seedStr) {
|
|
var array = decodeUTF8(seedStr);
|
|
var bytes = Nacl.hash(array);
|
|
var cryptKey = bytes.subarray(0, 32);
|
|
return Crypto.encrypt(data, cryptKey);
|
|
};
|
|
Invite.decryptHash = function(encryptedStr, seedStr) {
|
|
var array = decodeUTF8(seedStr);
|
|
var bytes = Nacl.hash(array);
|
|
var cryptKey = bytes.subarray(0, 32);
|
|
return Crypto.decrypt(encryptedStr, cryptKey);
|
|
};
|
|
return Invite;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonCredential(), requireNaclFast(), requireCrypto());
|
|
}
|
|
})();
|
|
})(invitation);
|
|
return invitation.exports;
|
|
}
|
|
var hasRequiredTeam;
|
|
function requireTeam() {
|
|
if (hasRequiredTeam) return team.exports;
|
|
hasRequiredTeam = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, Constants, Realtime, ProxyManager, UserObject, SF, Roster, Messaging, Feedback, Invite, Crypt, Cache, Pinpad, Listmap, Crypto, CpNetflux, ChainPad, nThen, Nacl) => {
|
|
var Team = {};
|
|
Nacl = Nacl || typeof window !== "undefined" && window.nacl;
|
|
var onStoreReady = Util.mkEvent(true);
|
|
var openCachedTeamChat = function() {};
|
|
var registerChangeEvents = function(ctx, team, proxy, fId) {
|
|
if (!team) {
|
|
return;
|
|
}
|
|
if (!fId) {
|
|
proxy.on("change", [ "drive", UserObject.SHARED_FOLDERS ], (function(o, n, p) {
|
|
if (p.length > 3 && p[3] === "password") {
|
|
var id = p[2];
|
|
var data = proxy.drive[UserObject.SHARED_FOLDERS][id];
|
|
var href = team.manager.user.userObject.getHref ? team.manager.user.userObject.getHref(data) : data.href;
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
|
|
setTimeout((function() {
|
|
SF.updatePassword(ctx.Store, {
|
|
oldChannel: secret.channel,
|
|
password: n,
|
|
href
|
|
}, ctx.store.network, (function() {
|
|
console.log("Shared folder password changed");
|
|
}));
|
|
}));
|
|
return false;
|
|
}
|
|
}));
|
|
proxy.on("disconnect", (function() {
|
|
team.offline = true;
|
|
team.sendEvent("NETWORK_DISCONNECT", team.id);
|
|
}));
|
|
proxy.on("reconnect", (function() {
|
|
team.offline = false;
|
|
team.sendEvent("NETWORK_RECONNECT", team.id);
|
|
}));
|
|
}
|
|
proxy.on("change", [], (function(o, n, p) {
|
|
if (fId) {
|
|
if (p[0] === UserObject.FILES_DATA && typeof n === "object" && n.channel && !n.owners) {
|
|
var toPin = [ n.channel ];
|
|
if (n.rtChannel) {
|
|
toPin.push(n.rtChannel);
|
|
}
|
|
if (n.lastVersion) {
|
|
toPin.push(n.lastVersion);
|
|
}
|
|
team.pin(toPin, (function(obj) {
|
|
if (obj && obj.error) {
|
|
console.error(obj.error);
|
|
}
|
|
}));
|
|
}
|
|
if (p[0] === UserObject.FILES_DATA && typeof o === "object" && o.channel && !n) {
|
|
var toUnpin = [ o.channel ];
|
|
var c = team.manager.findChannel(o.channel);
|
|
var exists = c.some((function(data) {
|
|
return data.fId !== fId;
|
|
}));
|
|
if (!exists) {
|
|
if (o.rtChannel) {
|
|
toUnpin.push(o.rtChannel);
|
|
}
|
|
if (o.lastVersion) {
|
|
toUnpin.push(o.lastVersion);
|
|
}
|
|
team.unpin(toUnpin, (function(obj) {
|
|
if (obj && obj.error) {
|
|
console.error(obj);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
if (o && !n && Array.isArray(p) && (p[0] === UserObject.FILES_DATA || p[0] === "drive" && p[1] === UserObject.FILES_DATA)) {
|
|
setTimeout((function() {
|
|
ctx.Store.checkDeletedPad(o && o.channel);
|
|
}));
|
|
}
|
|
team.sendEvent("DRIVE_CHANGE", {
|
|
id: fId,
|
|
old: o,
|
|
new: n,
|
|
path: p
|
|
});
|
|
}));
|
|
proxy.on("remove", [], (function(o, p) {
|
|
team.sendEvent("DRIVE_REMOVE", {
|
|
id: fId,
|
|
old: o,
|
|
path: p
|
|
});
|
|
}));
|
|
};
|
|
var closeTeam = function(ctx, teamId) {
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return;
|
|
}
|
|
try {
|
|
team.listmap.stop();
|
|
} catch (e) {}
|
|
try {
|
|
team.roster.stop();
|
|
} catch (e) {}
|
|
team.proxy = {};
|
|
team.stopped = true;
|
|
delete ctx.teams[teamId];
|
|
delete ctx.cache[teamId];
|
|
delete ctx.store.proxy.teams[teamId];
|
|
ctx.emit("LEAVE_TEAM", teamId, team.clients);
|
|
ctx.updateMetadata();
|
|
if (ctx.store.calendar) {
|
|
ctx.store.calendar.closeTeam(teamId);
|
|
}
|
|
if (ctx.store.mailbox) {
|
|
ctx.store.mailbox.close("team-" + teamId, (function() {}));
|
|
}
|
|
};
|
|
var getTeamChannelList = function(ctx, id) {
|
|
var store = ctx.teams[id];
|
|
if (!store) {
|
|
return null;
|
|
}
|
|
var list = store.manager.getChannelsList("pin");
|
|
var team = ctx.store.proxy.teams[id];
|
|
list.push(`${team.channel}#drive`);
|
|
var chatChannel = Util.find(team, [ "keys", "chat", "channel" ]);
|
|
var membersChannel = Util.find(team, [ "keys", "roster", "channel" ]);
|
|
var mailboxChannel = Util.find(team, [ "keys", "mailbox", "channel" ]);
|
|
if (chatChannel) {
|
|
list.push(chatChannel);
|
|
}
|
|
if (membersChannel) {
|
|
list.push(membersChannel);
|
|
}
|
|
if (mailboxChannel) {
|
|
list.push(mailboxChannel);
|
|
}
|
|
if (store.proxy.calendars) {
|
|
var cList = Object.keys(store.proxy.calendars).map((function(c) {
|
|
return store.proxy.calendars[c].channel;
|
|
}));
|
|
list = list.concat(cList);
|
|
}
|
|
var state = store.roster.getState();
|
|
if (state.members) {
|
|
Object.keys(state.members).forEach((function(curve) {
|
|
var m = state.members[curve];
|
|
if (m.inviteChannel && m.pending) {
|
|
list.push(m.inviteChannel);
|
|
}
|
|
if (m.previewChannel && m.pending) {
|
|
list.push(m.previewChannel);
|
|
}
|
|
}));
|
|
}
|
|
list.sort();
|
|
return list;
|
|
};
|
|
var handleSharedFolder = function(ctx, id, sfId, rt) {
|
|
var t = ctx.teams[id];
|
|
if (!t) {
|
|
return;
|
|
}
|
|
if (!rt) {
|
|
delete t.sharedFolders[sfId];
|
|
return;
|
|
}
|
|
t.sharedFolders[sfId] = rt;
|
|
registerChangeEvents(ctx, t, rt.proxy, sfId);
|
|
};
|
|
var initRpc = function(ctx, team, data, cb) {
|
|
if (team.rpc) {
|
|
return void cb();
|
|
}
|
|
if (!data.edPrivate || !data.edPublic) {
|
|
return void cb("EFORBIDDEN");
|
|
}
|
|
Pinpad.create(ctx.store.network, data, (function(e, call) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
team.rpc = call;
|
|
if (team && team.onRpcReadyEvt) {
|
|
team.onRpcReadyEvt.fire();
|
|
}
|
|
cb();
|
|
}), Cache);
|
|
};
|
|
var onCacheReady = function(ctx, id, lm, roster, keys, cId, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (ctx.cache[id]) {
|
|
return void cb();
|
|
}
|
|
var proxy = lm.proxy;
|
|
var team = {
|
|
id,
|
|
proxy,
|
|
listmap: lm,
|
|
clients: [],
|
|
realtime: lm.realtime,
|
|
handleSharedFolder: function(sfId, rt) {
|
|
handleSharedFolder(ctx, id, sfId, rt);
|
|
},
|
|
sharedFolders: {},
|
|
roster,
|
|
onRpcReadyEvt: Util.mkEvent(true),
|
|
offline: true
|
|
};
|
|
ctx.cache[id] = team;
|
|
if (cId) {
|
|
team.clients.push(cId);
|
|
}
|
|
roster.on("change", (function() {
|
|
var state = roster.getState();
|
|
var me = Util.find(ctx, [ "store", "proxy", "curvePublic" ]);
|
|
if (!state.members || !Object.keys(state.members).length) {
|
|
console.error(JSON.stringify(state));
|
|
return;
|
|
}
|
|
if (!state.members[me]) {
|
|
return void closeTeam(ctx, id);
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", id ]);
|
|
if (teamData) {
|
|
teamData.metadata = state.metadata;
|
|
}
|
|
ctx.updateMetadata();
|
|
ctx.emit("ROSTER_CHANGE", id, team.clients);
|
|
}));
|
|
roster.on("checkpoint", (function(hash) {
|
|
var rosterData = Util.find(ctx, [ "store", "proxy", "teams", id, "keys", "roster" ]);
|
|
rosterData.lastKnownHash = hash;
|
|
}));
|
|
team.sendEvent = function(q, data, sender) {
|
|
ctx.emit(q, data, team.clients.filter((function(cId) {
|
|
return cId !== sender;
|
|
})));
|
|
};
|
|
team.getChatData = function() {
|
|
var chatKeys = keys.chat || {};
|
|
var hash = chatKeys.edit || chatKeys.view;
|
|
if (!hash) {
|
|
return {};
|
|
}
|
|
var secret = Hash.getSecrets("chat", hash);
|
|
return {
|
|
teamId: id,
|
|
channel: secret.channel,
|
|
secret,
|
|
validateKey: chatKeys.validateKey
|
|
};
|
|
};
|
|
team.pin = function(data, cb) {
|
|
if (!keys.drive.edPrivate) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (!team.rpc) {
|
|
return void cb({
|
|
error: "TEAM_RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (typeof cb !== "function") {
|
|
console.error("expected a callback");
|
|
}
|
|
team.rpc.pin(data, (function(e, hash) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
cb({
|
|
hash
|
|
});
|
|
}));
|
|
};
|
|
team.unpin = function(data, cb) {
|
|
if (!keys.drive.edPrivate) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (!team.rpc) {
|
|
return void cb({
|
|
error: "TEAM_RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (typeof cb !== "function") {
|
|
console.error("expected a callback");
|
|
}
|
|
team.rpc.unpin(data, (function(e, hash) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
cb({
|
|
hash
|
|
});
|
|
}));
|
|
};
|
|
var loadSharedFolder = function(id, data, cb, isNew) {
|
|
SF.load({
|
|
isNew,
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
store: team,
|
|
isNewChannel: ctx.Store.isNewChannel,
|
|
Store: ctx.Store
|
|
}, id, data, cb);
|
|
};
|
|
var teamData = ctx.store.proxy.teams[team.id];
|
|
var hash = teamData.hash || teamData.roHash;
|
|
var secret = Hash.getSecrets("team", hash, teamData.password);
|
|
var manager = team.manager = ProxyManager.create(proxy.drive, {
|
|
onSync: function(cb) {
|
|
ctx.Store.onSync(id, cb);
|
|
},
|
|
edPublic: keys.drive.edPublic,
|
|
pin: team.pin,
|
|
unpin: team.unpin,
|
|
loadSharedFolder,
|
|
settings: {
|
|
drive: Util.find(ctx.store, [ "proxy", "settings", "drive" ])
|
|
},
|
|
removeOwnedChannel: function(channel, cb) {
|
|
var data;
|
|
if (typeof channel === "object") {
|
|
channel.teamId = id;
|
|
data = channel;
|
|
} else {
|
|
data = {
|
|
channel,
|
|
teamId: id
|
|
};
|
|
}
|
|
ctx.Store.removeOwnedChannel("", data, cb);
|
|
},
|
|
Store: ctx.Store,
|
|
store: ctx.store
|
|
}, {
|
|
teamId: team.id,
|
|
outer: true,
|
|
edPublic: keys.drive.edPublic,
|
|
loggedIn: true,
|
|
log: function(msg) {
|
|
team.sendEvent("DRIVE_LOG", msg);
|
|
},
|
|
rt: team.realtime,
|
|
editKey: secret.keys.secondaryKey,
|
|
readOnly: Boolean(!secret.keys.secondaryKey)
|
|
});
|
|
team.secondaryKey = secret && secret.keys.secondaryKey;
|
|
team.userObject = manager.user.userObject;
|
|
nThen((function(waitFor) {
|
|
ctx.teams[id] = team;
|
|
registerChangeEvents(ctx, team, proxy);
|
|
var network = ctx.store.network || ctx.store.networkPromise;
|
|
SF.loadSharedFolders(ctx.Store, network, team, team.proxy.drive, team.userObject, waitFor, (function(data) {
|
|
ctx.progress += 70 / (ctx.numberOfTeams * data.max);
|
|
ctx.updateProgress({
|
|
progress: ctx.progress
|
|
});
|
|
}), true);
|
|
})).nThen((function() {
|
|
if (ctx.store.modules.calendar) {
|
|
ctx.store.modules.calendar.openTeam(id);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var onReady = function(ctx, id, lm, roster, keys, cId, cb) {
|
|
var state = roster.getState();
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", id ]);
|
|
if (teamData) {
|
|
teamData.metadata = state.metadata;
|
|
}
|
|
delete ctx.nocache[id];
|
|
var team;
|
|
if (!ctx.store.proxy.teams[id]) {
|
|
return;
|
|
}
|
|
nThen((function(waitFor) {
|
|
onCacheReady(ctx, id, lm, roster, keys, cId, waitFor());
|
|
team = ctx.teams[id] || ctx.cache[id];
|
|
if (!keys.drive.edPrivate) {
|
|
return;
|
|
}
|
|
initRpc(ctx, team, keys.drive, waitFor((function() {})));
|
|
})).nThen((function(waitFor) {
|
|
team.userObject.fixFiles();
|
|
SF.checkMigration(team.secondaryKey, team.proxy?.drive, team.userObject, waitFor());
|
|
SF.loadSharedFolders(ctx.Store, ctx.store.network, team, team.proxy?.drive, team.userObject, waitFor, (function(data) {
|
|
ctx.progress += 70 / (ctx.numberOfTeams * data.max);
|
|
ctx.updateProgress({
|
|
progress: ctx.progress
|
|
});
|
|
}));
|
|
})).nThen((function() {
|
|
if (!team.rpc) {
|
|
return;
|
|
}
|
|
var list = getTeamChannelList(ctx, id);
|
|
var local = Hash.hashChannelList(list);
|
|
team.rpc.getServerHash((function(e, hash) {
|
|
if (e) {
|
|
return void console.warn(e);
|
|
}
|
|
if (hash !== local) {
|
|
team.rpc.reset(list, (function(e) {
|
|
if (e) {
|
|
console.warn(e);
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
})).nThen((function() {
|
|
team.offline = false;
|
|
if (ctx.onReadyHandlers[id]) {
|
|
ctx.onReadyHandlers[id].forEach((function(obj) {
|
|
if (typeof obj.cb === "function") {
|
|
obj.cb();
|
|
}
|
|
if (!obj.cId) {
|
|
return;
|
|
}
|
|
var idx = team.clients.indexOf(obj.cId);
|
|
if (idx === -1) {
|
|
team.clients.push(obj.cId);
|
|
}
|
|
}));
|
|
}
|
|
delete ctx.onReadyHandlers[id];
|
|
if (ctx.store.modules.calendar) {
|
|
ctx.store.modules.calendar.openTeam(id);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var checkTeamChannels = function(ctx, id, channel, roster, waitFor, cb) {
|
|
var close = function() {
|
|
if (ctx.cache[id] || ctx.teams[id]) {
|
|
closeTeam(ctx, id);
|
|
}
|
|
delete ctx.store.proxy.teams[id];
|
|
delete ctx.onReadyHandlers[id];
|
|
waitFor.abort();
|
|
cb({
|
|
error: "ENOENT"
|
|
});
|
|
};
|
|
if (channel) {
|
|
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, waitFor((function(e, res) {
|
|
if (res && res.length && typeof res[0] === "object" && res[0].isNew) {
|
|
close();
|
|
}
|
|
})));
|
|
}
|
|
if (roster) {
|
|
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", roster, waitFor((function(e, res) {
|
|
if (res && res.length && typeof res[0] === "object" && res[0].isNew) {
|
|
close();
|
|
}
|
|
})));
|
|
}
|
|
};
|
|
var openChannel = function(ctx, teamData, id, _cb, cache) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var hash = teamData.hash || teamData.roHash;
|
|
var secret = Hash.getSecrets("team", hash, teamData.password);
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
if (!teamData.roHash) {
|
|
teamData.roHash = Hash.getViewHashFromKeys(secret);
|
|
}
|
|
var keys = teamData.keys;
|
|
if (!keys.chat.validateKey && keys.chat.edit) {
|
|
var chatSecret = Hash.getSecrets("chat", keys.chat.edit);
|
|
keys.chat.validateKey = chatSecret.keys.validateKey;
|
|
}
|
|
var roster;
|
|
var lm;
|
|
var myKeys = {
|
|
curvePublic: ctx.store.proxy.curvePublic,
|
|
curvePrivate: ctx.store.proxy.curvePrivate
|
|
};
|
|
var rosterData = keys.roster || {};
|
|
var rosterKeys = rosterData.edit ? Crypto.Team.deriveMemberKeys(rosterData.edit, myKeys) : Crypto.Team.deriveGuestKeys(rosterData.view || "");
|
|
nThen((function(waitFor) {
|
|
if (cache) {
|
|
Cache.getChannelCache(secret.channel, waitFor((function(err, obj) {
|
|
var c = obj && obj.c;
|
|
if (!c) {
|
|
waitFor.abort();
|
|
cb({
|
|
error: "NOCACHE"
|
|
});
|
|
}
|
|
})));
|
|
Cache.getChannelCache(rosterKeys.channel, waitFor((function(err, obj) {
|
|
var c = obj && obj.c;
|
|
var k = obj && obj.k;
|
|
if (k && !rosterKeys.teamEdPublic) {
|
|
rosterKeys.teamEdPublic = k;
|
|
}
|
|
if (!c) {
|
|
waitFor.abort();
|
|
cb({
|
|
error: "NOCACHE"
|
|
});
|
|
}
|
|
})));
|
|
return;
|
|
}
|
|
ctx.Store.onReadyEvt.reg((() => {
|
|
checkTeamChannels(ctx, id, secret.channel, rosterKeys.channel, waitFor, cb);
|
|
}));
|
|
})).nThen((function(waitFor) {
|
|
var cacheRdy = {
|
|
lm: false,
|
|
roster: false,
|
|
check: function() {
|
|
if (!this.lm || !this.roster) {
|
|
return;
|
|
}
|
|
if (!cache) {
|
|
return;
|
|
}
|
|
ctx.progress += 30 / ctx.numberOfTeams;
|
|
ctx.updateProgress({
|
|
progress: ctx.progress
|
|
});
|
|
onCacheReady(ctx, id, lm, roster, keys, null, waitFor(cb));
|
|
this.check = function() {};
|
|
}
|
|
};
|
|
var cfg = {
|
|
data: {},
|
|
readOnly: !Boolean(secret.keys.signKey),
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
channel: secret.channel,
|
|
crypto,
|
|
ChainPad,
|
|
Cache,
|
|
metadata: {
|
|
validateKey: secret.keys.validateKey || undefined
|
|
},
|
|
userName: "team",
|
|
classic: true
|
|
};
|
|
cfg.onMetadataUpdate = function() {
|
|
var team = ctx.teams[id];
|
|
if (!team) {
|
|
return;
|
|
}
|
|
ctx.emit("ROSTER_CHANGE", id, team.clients);
|
|
};
|
|
lm = Listmap.create(cfg);
|
|
lm.proxy.on("cacheready", (function() {
|
|
cacheRdy.lm = true;
|
|
cacheRdy.check();
|
|
}));
|
|
lm.proxy.on("ready", waitFor());
|
|
lm.proxy.on("error", (function(info) {
|
|
if (info && typeof info.loaded !== "undefined" && !info.loaded) {
|
|
cb({
|
|
error: "ECONNECT"
|
|
});
|
|
}
|
|
if (info && info.error) {
|
|
if (info.error === "EDELETED") {
|
|
closeTeam(ctx, id);
|
|
}
|
|
}
|
|
}));
|
|
Roster.create({
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
channel: rosterKeys.channel,
|
|
keys: rosterKeys,
|
|
store: ctx.store,
|
|
lastKnownHash: rosterData.lastKnownHash,
|
|
onCacheReady: function(_roster) {
|
|
if (!cache) {
|
|
return;
|
|
}
|
|
if (_roster && _roster.error === "CORRUPTED") {
|
|
console.error("Corrupted roster cache, cant load this team offline", teamData);
|
|
if (lm && typeof lm.stop === "function") {
|
|
lm.stop();
|
|
}
|
|
waitFor.abort();
|
|
cb({
|
|
error: "CACHE_CORRUPTED_ROSTER"
|
|
});
|
|
return;
|
|
}
|
|
roster = _roster;
|
|
cacheRdy.roster = true;
|
|
cacheRdy.check();
|
|
},
|
|
Cache
|
|
}, waitFor((function(err, _roster) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
console.error(err);
|
|
return void cb({
|
|
error: "ROSTER_ERROR"
|
|
});
|
|
}
|
|
roster = _roster;
|
|
rosterData.lastKnownHash = roster.getLastCheckpointHash();
|
|
var state = roster.getState();
|
|
var me = Util.find(ctx, [ "store", "proxy", "curvePublic" ]);
|
|
if (!state.members[me]) {
|
|
return;
|
|
}
|
|
onStoreReady.reg((function() {
|
|
if (!rosterData.edit) {
|
|
return;
|
|
}
|
|
var data = {};
|
|
var myData = Messaging.createData(ctx.store.proxy, false);
|
|
myData.pending = false;
|
|
data[ctx.store.proxy.curvePublic] = myData;
|
|
roster.describe(data, (function(err) {
|
|
if (!err) {
|
|
return;
|
|
}
|
|
if (err === "NO_CHANGE") {
|
|
return;
|
|
}
|
|
console.error(err);
|
|
}));
|
|
}));
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
var state = roster.getState();
|
|
var me = Util.find(ctx, [ "store", "proxy", "curvePublic" ]);
|
|
if (!state.members || !Object.keys(state.members).length) {
|
|
lm.stop();
|
|
roster.stop();
|
|
lm.proxy = {};
|
|
cb({
|
|
error: "EINVAL"
|
|
});
|
|
waitFor.abort();
|
|
console.error(JSON.stringify(state));
|
|
Feedback.send("ROSTER_CORRUPTED");
|
|
return;
|
|
}
|
|
if (!state.members[me]) {
|
|
lm.stop();
|
|
roster.stop();
|
|
lm.proxy = {};
|
|
delete ctx.store.proxy.teams[id];
|
|
ctx.updateMetadata();
|
|
cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
waitFor.abort();
|
|
return;
|
|
}
|
|
var s = state.members[me];
|
|
var teamEdPrivate = Util.find(teamData, [ "keys", "drive", "edPrivate" ]);
|
|
if ((!teamData.hash || !teamEdPrivate) && [ "ADMIN", "MEMBER" ].indexOf(s.role) !== -1) {
|
|
console.warn("Missing edit rights: demote to viewer");
|
|
var data = {};
|
|
data[ctx.store.proxy.curvePublic] = {
|
|
role: "VIEWER"
|
|
};
|
|
roster.describe(data, (function(err) {
|
|
Feedback.send("TEAM_RIGHTS_FIXED");
|
|
delete teamData.hash;
|
|
delete teamData.keys.drive.edPrivate;
|
|
delete teamData.keys.chat.edit;
|
|
if (!err) {
|
|
return;
|
|
}
|
|
if (err === "NO_CHANGE") {
|
|
return;
|
|
}
|
|
console.error(err);
|
|
}));
|
|
} else if ((!teamData.hash || !teamEdPrivate) && s.role === "OWNER") {
|
|
Feedback.send("TEAM_RIGHTS_OWNER");
|
|
}
|
|
})).nThen((function() {
|
|
if (!cache) {
|
|
ctx.progress += 30 / ctx.numberOfTeams;
|
|
ctx.updateProgress({
|
|
progress: ctx.progress
|
|
});
|
|
}
|
|
onReady(ctx, id, lm, roster, keys, null, cb);
|
|
}));
|
|
};
|
|
var createTeam = function(ctx, data, cId, _cb) {
|
|
var cb = Util.once(_cb);
|
|
var password = Hash.createChannelId();
|
|
var hash = Hash.createRandomHash("team", password);
|
|
var secret = Hash.getSecrets("team", hash, password);
|
|
var roHash = Hash.getViewHashFromKeys(secret);
|
|
var keyPair = Nacl.sign.keyPair();
|
|
var curvePair = Nacl.box.keyPair();
|
|
var rosterSeed = Crypto.Team.createSeed();
|
|
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterSeed, {
|
|
curvePublic: ctx.store.proxy.curvePublic,
|
|
curvePrivate: ctx.store.proxy.curvePrivate
|
|
});
|
|
var roster;
|
|
var chatSecret = Hash.getSecrets("chat");
|
|
var chatHashes = Hash.getHashes(chatSecret);
|
|
var config = {
|
|
network: ctx.store.network,
|
|
channel: secret.channel,
|
|
data: {},
|
|
validateKey: secret.keys.validateKey,
|
|
crypto: Crypto.createEncryptor(secret.keys),
|
|
logLevel: 1,
|
|
classic: true,
|
|
ChainPad,
|
|
Cache,
|
|
owners: [ ctx.store.proxy.edPublic ]
|
|
};
|
|
nThen((function(waitFor) {
|
|
Roster.create({
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
channel: rosterKeys.channel,
|
|
owners: [ ctx.store.proxy.edPublic ],
|
|
keys: rosterKeys,
|
|
store: ctx.store,
|
|
lastKnownHash: void 0,
|
|
newTeam: true,
|
|
Cache
|
|
}, waitFor((function(err, _roster) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
console.error(err);
|
|
return void cb({
|
|
error: "ROSTER_ERROR"
|
|
});
|
|
}
|
|
roster = _roster;
|
|
var myData = Messaging.createData(ctx.store.proxy);
|
|
delete myData.channel;
|
|
roster.init(myData, waitFor((function(err) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "ROSTER_INIT_ERROR"
|
|
});
|
|
}
|
|
})));
|
|
})));
|
|
var crypto = Crypto.createEncryptor(chatSecret.keys);
|
|
var chatCfg = {
|
|
network: ctx.store.network,
|
|
channel: chatSecret.channel,
|
|
noChainPad: true,
|
|
crypto,
|
|
metadata: {
|
|
validateKey: chatSecret.keys.validateKey,
|
|
owners: [ ctx.store.proxy.edPublic ]
|
|
}
|
|
};
|
|
var chatReady = waitFor();
|
|
var cpNf2;
|
|
chatCfg.onReady = function() {
|
|
if (cpNf2) {
|
|
cpNf2.stop();
|
|
}
|
|
chatReady();
|
|
};
|
|
chatCfg.onError = function() {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "CHAT_INIT_ERROR"
|
|
});
|
|
};
|
|
cpNf2 = CpNetflux.start(chatCfg);
|
|
})).nThen((function(waitFor) {
|
|
roster.metadata({
|
|
name: data.name
|
|
}, waitFor((function(err) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "ROSTER_INIT_ERROR"
|
|
});
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
var id = Util.createRandomInteger();
|
|
config.onMetadataUpdate = function() {
|
|
var team = ctx.teams[id];
|
|
if (!team) {
|
|
return;
|
|
}
|
|
ctx.emit("ROSTER_CHANGE", id, team.clients);
|
|
};
|
|
var lm = Listmap.create(config);
|
|
var proxy = lm.proxy;
|
|
proxy.version = 2;
|
|
proxy.on("ready", (function() {
|
|
var keys = {
|
|
mailbox: {
|
|
channel: Hash.createChannelId(),
|
|
viewed: [],
|
|
keys: {
|
|
curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
|
|
curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
|
|
}
|
|
},
|
|
drive: {
|
|
edPrivate: Nacl.util.encodeBase64(keyPair.secretKey),
|
|
edPublic: Nacl.util.encodeBase64(keyPair.publicKey)
|
|
},
|
|
chat: {
|
|
edit: chatHashes.editHash,
|
|
view: chatHashes.viewHash,
|
|
validateKey: chatSecret.keys.validateKey,
|
|
channel: chatSecret.channel
|
|
},
|
|
roster: {
|
|
channel: rosterKeys.channel,
|
|
edit: rosterSeed,
|
|
view: rosterKeys.viewKeyStr
|
|
}
|
|
};
|
|
var t = ctx.store.proxy.teams[id] = {
|
|
owner: true,
|
|
channel: secret.channel,
|
|
hash,
|
|
roHash,
|
|
password,
|
|
keys,
|
|
metadata: {
|
|
name: data.name
|
|
}
|
|
};
|
|
proxy.drive = {};
|
|
onReady(ctx, id, lm, roster, keys, cId, (function() {
|
|
Feedback.send("TEAM_CREATION");
|
|
ctx.store.mailbox.open("team-" + id, t.keys.mailbox, (function() {}), true, {
|
|
owners: t.keys.drive.edPublic
|
|
});
|
|
ctx.updateMetadata();
|
|
cb();
|
|
}));
|
|
})).on("error", (function(info) {
|
|
if (info && typeof info.loaded !== "undefined" && !info.loaded) {
|
|
cb({
|
|
error: "ECONNECT"
|
|
});
|
|
}
|
|
if (info && info.error) {
|
|
if (info.error === "EDELETED") {
|
|
closeTeam(ctx, id);
|
|
}
|
|
}
|
|
}));
|
|
}));
|
|
};
|
|
var deleteTeam = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!team || !teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var state = team.roster.getState();
|
|
var curvePublic = Util.find(ctx, [ "store", "proxy", "curvePublic" ]);
|
|
var me = state.members[curvePublic];
|
|
if (!me || me.role !== "OWNER") {
|
|
return cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
var edPublic = Util.find(ctx, [ "store", "proxy", "edPublic" ]);
|
|
var teamEdPublic = Util.find(teamData, [ "keys", "drive", "edPublic" ]);
|
|
nThen((function(waitFor) {
|
|
ctx.Store.anonRpcMsg(null, {
|
|
msg: "GET_METADATA",
|
|
data: teamData.channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return cb({
|
|
error: obj.error
|
|
});
|
|
}
|
|
var metadata = obj[0];
|
|
if (metadata && Array.isArray(metadata.owners) && metadata.owners.indexOf(edPublic) !== -1) {
|
|
return;
|
|
}
|
|
waitFor.abort();
|
|
cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
team.proxy.delete = true;
|
|
var ownedPads = team.manager.getChannelsList("owned");
|
|
var sem = Util.Saferphore.create(10);
|
|
ownedPads.forEach((function(c) {
|
|
var w = waitFor();
|
|
sem.take((function(give) {
|
|
var otherOwners = false;
|
|
nThen((function(_w) {
|
|
if (c.length !== 32) {
|
|
return;
|
|
}
|
|
ctx.Store.anonRpcMsg(null, {
|
|
msg: "GET_METADATA",
|
|
data: c
|
|
}, _w((function(obj) {
|
|
if (obj && obj.error) {
|
|
give();
|
|
return void _w.abort();
|
|
}
|
|
var md = obj[0];
|
|
var isOwner = md && Array.isArray(md.owners) && md.owners.indexOf(teamEdPublic) !== -1;
|
|
if (!isOwner) {
|
|
give();
|
|
return void _w.abort();
|
|
}
|
|
otherOwners = md.owners.some((function(ed) {
|
|
return ed !== teamEdPublic;
|
|
}));
|
|
})));
|
|
})).nThen((function(_w) {
|
|
if (otherOwners) {
|
|
ctx.Store.setPadMetadata(null, {
|
|
channel: c,
|
|
command: "RM_OWNERS",
|
|
value: [ teamEdPublic ]
|
|
}, _w());
|
|
return;
|
|
}
|
|
team.rpc.removeOwnedChannel(c, _w((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
give();
|
|
w();
|
|
}));
|
|
}));
|
|
}));
|
|
})).nThen((function(waitFor) {
|
|
team.rpc.removePins(waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
var mailboxChan = Util.find(teamData, [ "keys", "mailbox", "channel" ]);
|
|
team.rpc.removeOwnedChannel(mailboxChan, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
var rosterChan = Util.find(teamData, [ "keys", "roster", "channel" ]);
|
|
ctx.store.rpc.removeOwnedChannel(rosterChan, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
var chatChan = Util.find(teamData, [ "keys", "chat", "channel" ]);
|
|
ctx.store.rpc.removeOwnedChannel(chatChan, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
ctx.store.rpc.removeOwnedChannel(teamData.channel, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
Feedback.send("TEAM_DELETION");
|
|
closeTeam(ctx, teamId);
|
|
cb();
|
|
}));
|
|
};
|
|
var joinTeam = function(ctx, data, cId, cb) {
|
|
var team = data.team;
|
|
if (!(team.hash || team.roHash) || !team.channel || !team.password || !team.keys || !team.metadata) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
let myTeams = ctx.store.proxy.teams;
|
|
if (Object.values(myTeams).some((obj => obj.channel === team.channel))) {
|
|
return void cb({
|
|
error: "EEXISTS"
|
|
});
|
|
}
|
|
var id = Util.createRandomInteger();
|
|
ctx.store.proxy.teams[id] = team;
|
|
ctx.onReadyHandlers[id] = [];
|
|
openChannel(ctx, team, id, (function(obj) {
|
|
if (!(obj && obj.error)) {
|
|
console.debug("Team joined:" + id);
|
|
}
|
|
var t = ctx.store.proxy.teams[id];
|
|
ctx.store.mailbox.open("team-" + id, t.keys.mailbox, (function() {}), true, {
|
|
owners: t.keys.drive.edPublic
|
|
});
|
|
ctx.updateMetadata();
|
|
cb(obj);
|
|
}));
|
|
};
|
|
var leaveTeam = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
var curvePublic = ctx.store.proxy.curvePublic;
|
|
team.roster.remove([ curvePublic ], (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
closeTeam(ctx, teamId);
|
|
cb();
|
|
}));
|
|
};
|
|
var getTeamRoster = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
var state = team.roster.getState() || {};
|
|
var members = state.members || {};
|
|
var md;
|
|
nThen((function(waitFor) {
|
|
ctx.Store.getPadMetadata(null, {
|
|
channel: teamData.channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
md = team.listmap.metadata || {};
|
|
return;
|
|
}
|
|
md = obj;
|
|
})));
|
|
})).nThen((function() {
|
|
ctx.pending_owners = md.pending_owners;
|
|
if (Array.isArray(md.pending_owners)) {
|
|
md.pending_owners.forEach((function(ed) {
|
|
var member;
|
|
Object.keys(members).some((function(curve) {
|
|
if (members[curve].edPublic === ed) {
|
|
member = members[curve];
|
|
return true;
|
|
}
|
|
}));
|
|
if (!member && teamData.owner) {
|
|
var removeOwnership = function(chan) {
|
|
ctx.Store.setPadMetadata(null, {
|
|
channel: chan,
|
|
command: "RM_PENDING_OWNERS",
|
|
value: [ ed ]
|
|
}, (function() {}));
|
|
};
|
|
removeOwnership(teamData.channel);
|
|
removeOwnership(Util.find(teamData, [ "keys", "roster", "channel" ]));
|
|
removeOwnership(Util.find(teamData, [ "keys", "chat", "channel" ]));
|
|
return;
|
|
}
|
|
member.pendingOwner = true;
|
|
}));
|
|
}
|
|
if (ctx.store.messenger) {
|
|
var chatData = team.getChatData();
|
|
var online = ctx.store.messenger.getOnlineList(chatData.channel) || [];
|
|
online.forEach((function(curve) {
|
|
if (members[curve]) {
|
|
members[curve].online = true;
|
|
}
|
|
}));
|
|
}
|
|
Object.keys(members).forEach((function(curve) {
|
|
var member = members[curve];
|
|
if (!member.inviteChannel) {
|
|
return;
|
|
}
|
|
if (!member.hash) {
|
|
return;
|
|
}
|
|
if (!teamData.hash) {
|
|
delete member.hash;
|
|
return;
|
|
}
|
|
try {
|
|
member.hash = Invite.decryptHash(member.hash, teamData.hash);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
cb(members);
|
|
}));
|
|
};
|
|
var getEditableFolders = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var folders = team.manager.folders || {};
|
|
var ids = Object.keys(folders).filter((function(id) {
|
|
return !folders[id].proxy.version;
|
|
}));
|
|
cb(ids.map((function(id) {
|
|
var uo = Util.find(team, [ "user", "userObject" ]);
|
|
return {
|
|
name: Util.find(folders, [ id, "proxy", "metadata", "title" ]),
|
|
path: uo ? uo.findFile(id)[0] : []
|
|
};
|
|
})));
|
|
};
|
|
var getTeamMetadata = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
var state = team.roster.getState() || {};
|
|
var md = state.metadata || {};
|
|
md.offline = team.offline;
|
|
cb(md);
|
|
};
|
|
var setTeamMetadata = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (team.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
if (data.metadata) {
|
|
delete data.metadata.offline;
|
|
}
|
|
team.roster.metadata(data.metadata, (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
var localTeam = ctx.store.proxy.teams[teamId];
|
|
if (localTeam) {
|
|
localTeam.metadata = data.metadata;
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var offerOwnership = function(ctx, data, cId, _cb) {
|
|
var cb = Util.once(_cb);
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
if (!data.curvePublic) {
|
|
return void cb({
|
|
error: "MISSING_DATA"
|
|
});
|
|
}
|
|
var state = team.roster.getState();
|
|
var user = state.members[data.curvePublic];
|
|
nThen((function(waitFor) {
|
|
var onError = function(res) {
|
|
var err = res && res.error;
|
|
if (err) {
|
|
console.error(err);
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
};
|
|
var addPendingOwner = function(chan) {
|
|
ctx.Store.setPadMetadata(null, {
|
|
channel: chan,
|
|
command: "ADD_PENDING_OWNERS",
|
|
value: [ user.edPublic ]
|
|
}, waitFor(onError));
|
|
};
|
|
addPendingOwner(teamData.channel);
|
|
addPendingOwner(Util.find(teamData, [ "keys", "roster", "channel" ]));
|
|
addPendingOwner(Util.find(teamData, [ "keys", "chat", "channel" ]));
|
|
})).nThen((function(waitFor) {
|
|
var obj = {};
|
|
obj[user.curvePublic] = {
|
|
role: "OWNER"
|
|
};
|
|
team.roster.describe(obj, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
ctx.store.mailbox.sendTo("ADD_OWNER", {
|
|
teamChannel: teamData.channel,
|
|
chatChannel: Util.find(teamData, [ "keys", "chat", "channel" ]),
|
|
rosterChannel: Util.find(teamData, [ "keys", "roster", "channel" ]),
|
|
title: teamData.metadata.name
|
|
}, {
|
|
channel: user.notifications,
|
|
curvePublic: user.curvePublic
|
|
}, waitFor());
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var revokeOwnership = function(ctx, teamId, user, _cb) {
|
|
var cb = Util.once(_cb);
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var isPendingOwner = user.pendingOwner;
|
|
nThen((function(waitFor) {
|
|
var cmd = isPendingOwner ? "RM_PENDING_OWNERS" : "RM_OWNERS";
|
|
var onError = function(res) {
|
|
var err = res && res.error;
|
|
if (err) {
|
|
console.error(err);
|
|
waitFor.abort();
|
|
return void cb(err);
|
|
}
|
|
};
|
|
var removeOwnership = function(chan) {
|
|
ctx.Store.setPadMetadata(null, {
|
|
channel: chan,
|
|
command: cmd,
|
|
value: [ user.edPublic ]
|
|
}, waitFor(onError));
|
|
};
|
|
removeOwnership(teamData.channel);
|
|
removeOwnership(Util.find(teamData, [ "keys", "roster", "channel" ]));
|
|
removeOwnership(Util.find(teamData, [ "keys", "chat", "channel" ]));
|
|
})).nThen((function(waitFor) {
|
|
var obj = {};
|
|
obj[user.curvePublic] = {
|
|
role: "ADMIN",
|
|
pendingOwner: false
|
|
};
|
|
team.roster.describe(obj, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
ctx.store.mailbox.sendTo("RM_OWNER", {
|
|
teamChannel: teamData.channel,
|
|
title: teamData.metadata.name,
|
|
pending: isPendingOwner
|
|
}, {
|
|
channel: user.notifications,
|
|
curvePublic: user.curvePublic
|
|
}, waitFor());
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var answerOwnership = function(ctx, data, cId, cb) {
|
|
var myTeams = ctx.store.proxy.teams;
|
|
var teamId;
|
|
Object.keys(myTeams).forEach((function(id) {
|
|
if (myTeams[id].channel === data.teamChannel) {
|
|
teamId = id;
|
|
return true;
|
|
}
|
|
}));
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
var obj = {};
|
|
if (data.answer) {
|
|
teamData.owner = true;
|
|
return;
|
|
}
|
|
obj[ctx.store.proxy.curvePublic] = {
|
|
role: "ADMIN"
|
|
};
|
|
team.roster.describe(obj, (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var getInviteData = function(ctx, teamId, edit) {
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return {};
|
|
}
|
|
var data = Util.clone(teamData);
|
|
if (!edit) {
|
|
delete data.hash;
|
|
delete data.keys.drive.edPrivate;
|
|
delete data.keys.chat.edit;
|
|
}
|
|
delete data.owner;
|
|
return data;
|
|
};
|
|
var updateMyRights = function(ctx, teamId, hash) {
|
|
if (!teamId) {
|
|
return true;
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return true;
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return true;
|
|
}
|
|
var secret = Hash.getSecrets("team", hash || teamData.roHash, teamData.password);
|
|
SF.upgrade(teamData.channel, secret);
|
|
if (team.userObject) {
|
|
team.userObject.setReadOnly(!secret.keys.secondaryKey, secret.keys.secondaryKey);
|
|
}
|
|
if (secret.keys.secondaryKey) {
|
|
try {
|
|
ctx.store.modules.calendar.upgradeTeam(teamId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
if (!secret.keys.secondaryKey && team.rpc) {
|
|
team.rpc.destroy();
|
|
}
|
|
var folders = Util.find(team, [ "proxy", "drive", "sharedFolders" ]);
|
|
Object.keys(folders || {}).forEach((function(sfId) {
|
|
var data = team.manager.getSharedFolderData(sfId);
|
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
|
SF.upgrade(secret.channel, secret);
|
|
var uo = Util.find(team, [ "manager", "folders", sfId, "userObject" ]);
|
|
if (uo) {
|
|
uo.setReadOnly(!secret.keys.secondaryKey, secret.keys.secondaryKey);
|
|
}
|
|
}));
|
|
ctx.updateMetadata();
|
|
ctx.emit("ROSTER_CHANGE_RIGHTS", teamId, team.clients);
|
|
};
|
|
var changeMyRights = function(ctx, teamId, state, data, cb) {
|
|
if (!teamId) {
|
|
return void cb(false);
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb(false);
|
|
}
|
|
var onReady = ctx.onReadyHandlers[teamId];
|
|
var team = ctx.teams[teamId];
|
|
if (teamData.channel !== data.channel || teamData.password !== data.password) {
|
|
return void cb(false);
|
|
}
|
|
if (state) {
|
|
teamData.hash = data.hash;
|
|
teamData.keys.drive.edPrivate = data.keys.drive.edPrivate;
|
|
teamData.keys.chat.edit = data.keys.chat.edit;
|
|
} else {
|
|
delete teamData.hash;
|
|
delete teamData.keys.drive.edPrivate;
|
|
delete teamData.keys.chat.edit;
|
|
}
|
|
if (!team && Array.isArray(onReady)) {
|
|
onReady.push({
|
|
cb: function() {
|
|
changeMyRights(ctx, teamId, state, data, cb);
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
if (!team) {
|
|
return void cb(false);
|
|
}
|
|
if (state) {
|
|
initRpc(ctx, team, teamData.keys.drive, (function() {
|
|
team.manager.addPin(team.pin, team.unpin);
|
|
}));
|
|
var secret = Hash.getSecrets("team", data.hash, teamData.password);
|
|
team.secondaryKey = secret && secret.keys.secondaryKey;
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
team.listmap.setReadOnly(false, crypto);
|
|
} else {
|
|
delete team.secondaryKey;
|
|
if (team.rpc && team.rpc.destroy) {
|
|
team.rpc.destroy();
|
|
}
|
|
team.manager.removePin();
|
|
team.listmap.setReadOnly(true);
|
|
}
|
|
updateMyRights(ctx, teamId, data.hash);
|
|
cb(true);
|
|
};
|
|
var changeEditRights = function(ctx, teamId, user, state, cb) {
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
if (!teamData) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
ctx.store.mailbox.sendTo("TEAM_EDIT_RIGHTS", {
|
|
state,
|
|
teamData: getInviteData(ctx, teamId, state)
|
|
}, {
|
|
channel: user.notifications,
|
|
curvePublic: user.curvePublic
|
|
}, cb);
|
|
};
|
|
var describeUser = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
var team = ctx.teams[teamId];
|
|
if (!teamData || !team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
if (!data.curvePublic || !data.data) {
|
|
return void cb({
|
|
error: "MISSING_DATA"
|
|
});
|
|
}
|
|
var state = team.roster.getState();
|
|
var user = state.members[data.curvePublic];
|
|
var md;
|
|
nThen((function(waitFor) {
|
|
ctx.Store.getPadMetadata(null, {
|
|
channel: teamData.channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
md = team.listmap.metadata || {};
|
|
return;
|
|
}
|
|
md = obj;
|
|
})));
|
|
})).nThen((function() {
|
|
user.pendingOwner = Array.isArray(md.pending_owners) && md.pending_owners.indexOf(user.edPublic) !== -1;
|
|
if (user.role === "OWNER" && data.data.role !== "OWNER") {
|
|
revokeOwnership(ctx, teamId, user, (function(err) {
|
|
if (!err) {
|
|
return void cb();
|
|
}
|
|
console.error(err);
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}));
|
|
return;
|
|
}
|
|
if (user.role === "VIEWER" && data.data.role !== "VIEWER") {
|
|
changeEditRights(ctx, teamId, user, true, (function(obj) {
|
|
return void cb(obj);
|
|
}));
|
|
}
|
|
if (user.role !== "VIEWER" && data.data.role === "VIEWER") {
|
|
changeEditRights(ctx, teamId, user, false, (function(obj) {
|
|
return void cb(obj);
|
|
}));
|
|
}
|
|
var obj = {};
|
|
obj[data.curvePublic] = data.data;
|
|
team.roster.describe(obj, (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb();
|
|
}));
|
|
}));
|
|
};
|
|
var inviteToTeam = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
var user = data.user;
|
|
if (!user || !user.curvePublic || !user.notifications) {
|
|
return void cb({
|
|
error: "MISSING_DATA"
|
|
});
|
|
}
|
|
delete user.channel;
|
|
delete user.lastKnownHash;
|
|
user.pending = true;
|
|
var obj = {};
|
|
obj[user.curvePublic] = user;
|
|
obj[user.curvePublic].role = "VIEWER";
|
|
team.roster.add(obj, (function(err) {
|
|
if (err && err !== "NO_CHANGE") {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
ctx.store.mailbox.sendTo("INVITE_TO_TEAM", {
|
|
team: getInviteData(ctx, teamId)
|
|
}, {
|
|
channel: user.notifications,
|
|
curvePublic: user.curvePublic
|
|
}, (function(obj) {
|
|
cb(obj);
|
|
}));
|
|
}));
|
|
};
|
|
var removeUser = function(ctx, data, cId, cb) {
|
|
var teamId = data.teamId;
|
|
if (!teamId) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!team.roster) {
|
|
return void cb({
|
|
error: "NO_ROSTER"
|
|
});
|
|
}
|
|
if (!data.curvePublic) {
|
|
return void cb({
|
|
error: "MISSING_DATA"
|
|
});
|
|
}
|
|
var state = team.roster.getState();
|
|
var userData = state.members[data.curvePublic];
|
|
team.roster.remove([ data.curvePublic ], (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (!userData || !userData.notifications) {
|
|
return cb();
|
|
}
|
|
ctx.store.mailbox.sendTo("KICKED_FROM_TEAM", {
|
|
pending: data.pending,
|
|
teamChannel: getInviteData(ctx, teamId).channel,
|
|
teamName: getInviteData(ctx, teamId).metadata.name
|
|
}, {
|
|
channel: userData.notifications,
|
|
curvePublic: userData.curvePublic
|
|
}, (function(obj) {
|
|
cb(obj);
|
|
}));
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, cId) {
|
|
Object.keys(ctx.onReadyHandlers).forEach((function(teamId) {
|
|
var idx = -1;
|
|
ctx.onReadyHandlers[teamId].some((function(obj, _idx) {
|
|
if (obj.cId === cId) {
|
|
idx = _idx;
|
|
return true;
|
|
}
|
|
}));
|
|
if (idx !== -1) {
|
|
ctx.onReadyHandlers[teamId].splice(idx, 1);
|
|
}
|
|
}));
|
|
Object.keys(ctx.teams).forEach((function(id) {
|
|
var clients = ctx.teams[id].clients;
|
|
var idx = clients.indexOf(cId);
|
|
if (idx !== -1) {
|
|
clients.splice(idx, 1);
|
|
}
|
|
}));
|
|
};
|
|
var subscribe = function(ctx, id, cId, cb) {
|
|
removeClient(ctx, cId);
|
|
try {
|
|
ctx.store.messenger.removeClient(cId);
|
|
} catch (e) {}
|
|
openCachedTeamChat = function() {};
|
|
if (!id) {
|
|
return void cb();
|
|
}
|
|
if (ctx.onReadyHandlers[id] && !ctx.teams[id]) {
|
|
var _idx = ctx.onReadyHandlers[id].indexOf(cId);
|
|
if (_idx === -1) {
|
|
ctx.onReadyHandlers[id].push({
|
|
cId,
|
|
cb
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (!ctx.teams[id]) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var clients = ctx.teams[id].clients;
|
|
var idx = clients.indexOf(cId);
|
|
if (idx === -1) {
|
|
clients.push(cId);
|
|
}
|
|
cb();
|
|
};
|
|
var openTeamChat = function(ctx, data, cId, cb) {
|
|
var team = ctx.teams[data.teamId];
|
|
if (!team) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var onUpdate = function() {
|
|
ctx.emit("ROSTER_CHANGE", data.teamId, team.clients);
|
|
};
|
|
if (ctx.store.messenger) {
|
|
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
|
|
} else {
|
|
openCachedTeamChat = function() {
|
|
ctx.store.messenger.openTeamChat(team.getChatData(), onUpdate, cId, cb);
|
|
};
|
|
}
|
|
};
|
|
var createInviteLink = function(ctx, data, cId, _cb) {
|
|
var cb = Util.mkAsync(Util.once(_cb));
|
|
var teamId = data.teamId;
|
|
var team = ctx.teams[data.teamId];
|
|
var seeds = data.seeds;
|
|
var bytes64 = data.bytes64;
|
|
if (!teamId || !team) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var roster = team.roster;
|
|
var teamName;
|
|
try {
|
|
teamName = roster.getState().metadata.name;
|
|
} catch (err) {
|
|
return void cb({
|
|
error: "TEAM_NAME_ERR"
|
|
});
|
|
}
|
|
var message = data.message;
|
|
var name = data.name;
|
|
var hash = data.hash;
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
try {
|
|
var encryptedHash = Invite.encryptHash(hash, teamData.hash);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
var previewKeys = Invite.derivePreviewKeys(seeds.preview);
|
|
var inviteKeys = Invite.deriveInviteKeys(bytes64);
|
|
var ephemeralKeys = Invite.generateKeys();
|
|
var role = data.role || "VIEWER";
|
|
var uses = data.uses || 1;
|
|
nThen((function(w) {
|
|
(function() {
|
|
var sign = Invite.generateSignPair();
|
|
var putOpts = {
|
|
initialState: "{}",
|
|
network: ctx.store.network,
|
|
metadata: {
|
|
owners: [ ctx.store.proxy.edPublic, ephemeralKeys.edPublic ]
|
|
}
|
|
};
|
|
putOpts.metadata.validateKey = sign.validateKey;
|
|
var previewContent = {
|
|
teamName,
|
|
message,
|
|
author: Messaging.createData(ctx.store.proxy, false),
|
|
displayName: name
|
|
};
|
|
var cryptput_config = {
|
|
channel: previewKeys.channel,
|
|
type: "pad",
|
|
version: 2,
|
|
keys: {
|
|
cryptKey: previewKeys.cryptKey,
|
|
validateKey: sign.validateKey,
|
|
signKey: sign.signKey
|
|
}
|
|
};
|
|
Crypt.put(cryptput_config, JSON.stringify(previewContent), w((function(err) {
|
|
if (err) {
|
|
console.error("CRYPTPUT_ERR", err);
|
|
w.abort();
|
|
return void cb({
|
|
error: "SET_PREVIEW_CONTENT"
|
|
});
|
|
}
|
|
})), putOpts);
|
|
})();
|
|
(function() {
|
|
var sign = Invite.generateSignPair();
|
|
var putOpts = {
|
|
initialState: "{}",
|
|
network: ctx.store.network,
|
|
metadata: {
|
|
owners: [ ctx.store.proxy.edPublic, ephemeralKeys.edPublic ]
|
|
}
|
|
};
|
|
putOpts.metadata.validateKey = sign.validateKey;
|
|
var inviteContent = {
|
|
teamData: getInviteData(ctx, teamId, role === "MEMBER"),
|
|
ephemeral: {
|
|
edPublic: ephemeralKeys.edPublic,
|
|
edPrivate: ephemeralKeys.edPrivate,
|
|
curvePublic: ephemeralKeys.curvePublic,
|
|
curvePrivate: ephemeralKeys.curvePrivate
|
|
}
|
|
};
|
|
var cryptput_config = {
|
|
channel: inviteKeys.channel,
|
|
type: "pad",
|
|
version: 2,
|
|
keys: {
|
|
cryptKey: inviteKeys.cryptKey,
|
|
validateKey: sign.validateKey,
|
|
signKey: sign.signKey
|
|
}
|
|
};
|
|
Crypt.put(cryptput_config, JSON.stringify(inviteContent), w((function(err) {
|
|
if (err) {
|
|
console.error("CRYPTPUT_ERR", err);
|
|
w.abort();
|
|
return void cb({
|
|
error: "SET_PREVIEW_CONTENT"
|
|
});
|
|
}
|
|
})), putOpts);
|
|
})();
|
|
})).nThen((function(w) {
|
|
team.pin([ inviteKeys.channel, previewKeys.channel ], (function(obj) {
|
|
if (obj && obj.error) {
|
|
console.error(obj.error);
|
|
}
|
|
}));
|
|
Invite.createRosterEntry(team.roster, {
|
|
curvePublic: ephemeralKeys.curvePublic,
|
|
content: {
|
|
curvePublic: ephemeralKeys.curvePublic,
|
|
displayName: data.name,
|
|
pending: true,
|
|
remaining: uses,
|
|
totalUses: uses,
|
|
role,
|
|
hash: encryptedHash,
|
|
inviteChannel: inviteKeys.channel,
|
|
previewChannel: previewKeys.channel
|
|
}
|
|
}, w((function(err) {
|
|
if (err) {
|
|
w.abort();
|
|
cb(err);
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
var getPreviewContent = function(ctx, data, cId, cb) {
|
|
var seeds = data.seeds;
|
|
var previewKeys;
|
|
try {
|
|
previewKeys = Invite.derivePreviewKeys(seeds.preview);
|
|
} catch (err) {
|
|
return void cb({
|
|
error: "INVALID_SEEDS"
|
|
});
|
|
}
|
|
Crypt.get({
|
|
channel: previewKeys.channel,
|
|
type: "pad",
|
|
version: 2,
|
|
keys: {
|
|
cryptKey: previewKeys.cryptKey
|
|
}
|
|
}, (function(err, val) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (!val) {
|
|
return void cb({
|
|
error: "DELETED"
|
|
});
|
|
}
|
|
var json = Util.tryParse(val);
|
|
if (!json) {
|
|
return void cb({
|
|
error: "parseError"
|
|
});
|
|
}
|
|
cb(json);
|
|
}), {
|
|
network: ctx.store.network,
|
|
initialState: "{}"
|
|
});
|
|
};
|
|
var getInviteContent = function(ctx, data, cId, cb) {
|
|
var bytes64 = data.bytes64;
|
|
var previewKeys;
|
|
try {
|
|
previewKeys = Invite.deriveInviteKeys(bytes64);
|
|
} catch (err) {
|
|
return void cb({
|
|
error: "INVALID_SEEDS"
|
|
});
|
|
}
|
|
Crypt.get({
|
|
channel: previewKeys.channel,
|
|
type: "pad",
|
|
version: 2,
|
|
keys: {
|
|
cryptKey: previewKeys.cryptKey
|
|
}
|
|
}, (function(err, val) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (!val) {
|
|
return void cb({
|
|
error: "DELETED"
|
|
});
|
|
}
|
|
var json = Util.tryParse(val);
|
|
if (!json) {
|
|
return void cb({
|
|
error: "parseError"
|
|
});
|
|
}
|
|
cb(json);
|
|
}), {
|
|
network: ctx.store.network,
|
|
initialState: "{}"
|
|
});
|
|
};
|
|
var acceptLinkInvitation = function(ctx, data, cId, cb) {
|
|
var inviteContent;
|
|
var rosterState;
|
|
nThen((function(waitFor) {
|
|
getInviteContent(ctx, data, cId, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
inviteContent = obj;
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
var chan = Util.find(inviteContent, [ "teamData", "channel" ]);
|
|
var myTeams = ctx.store.proxy.teams || {};
|
|
var isMember = Object.keys(myTeams).some((function(k) {
|
|
var t = myTeams[k];
|
|
return t.channel === chan;
|
|
}));
|
|
if (isMember) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "ALREADY_MEMBER"
|
|
});
|
|
}
|
|
var rosterData = Util.find(inviteContent, [ "teamData", "keys", "roster" ]);
|
|
var myKeys = inviteContent.ephemeral;
|
|
if (!rosterData || !myKeys) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "INVALID_INVITE_CONTENT"
|
|
});
|
|
}
|
|
var rosterKeys = Crypto.Team.deriveMemberKeys(rosterData.edit, myKeys);
|
|
Roster.create({
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
channel: rosterData.channel,
|
|
keys: rosterKeys,
|
|
store: ctx.store,
|
|
Cache
|
|
}, waitFor((function(err, roster) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
console.error(err);
|
|
return void cb({
|
|
error: "ROSTER_ERROR"
|
|
});
|
|
}
|
|
var myData = Messaging.createData(ctx.store.proxy, false);
|
|
var state = roster.getState();
|
|
rosterState = state.members[myKeys.curvePublic];
|
|
roster.accept(myData.curvePublic, waitFor((function(err) {
|
|
roster.stop();
|
|
if (err) {
|
|
waitFor.abort();
|
|
console.error(err);
|
|
return void cb({
|
|
error: "ACCEPT_ERROR"
|
|
});
|
|
}
|
|
})));
|
|
})));
|
|
})).nThen((function() {
|
|
var tempRpc = {};
|
|
if (!rosterState.remaining || rosterState.remaining === 1) {
|
|
initRpc(ctx, tempRpc, inviteContent.ephemeral, (function(err) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
var rpc = tempRpc.rpc;
|
|
if (rosterState.inviteChannel) {
|
|
rpc.removeOwnedChannel(rosterState.inviteChannel, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
}
|
|
if (rosterState.previewChannel) {
|
|
rpc.removeOwnedChannel(rosterState.previewChannel, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
}
|
|
}));
|
|
}
|
|
joinTeam(ctx, {
|
|
team: inviteContent.teamData
|
|
}, cId, cb);
|
|
}));
|
|
};
|
|
var deriveMailbox = function(team) {
|
|
if (!team) {
|
|
return;
|
|
}
|
|
if (team.keys && team.keys.mailbox) {
|
|
return team.keys.mailbox;
|
|
}
|
|
var strSeed = Util.find(team, [ "keys", "roster", "edit" ]);
|
|
if (!strSeed) {
|
|
return;
|
|
}
|
|
var hash = Nacl.hash(Nacl.util.decodeUTF8(strSeed));
|
|
var seed = hash.slice(0, 32);
|
|
var mailboxChannel = Util.uint8ArrayToHex(hash.slice(32, 48));
|
|
var curvePair = Nacl.box.keyPair.fromSecretKey(seed);
|
|
return {
|
|
channel: mailboxChannel,
|
|
viewed: [],
|
|
keys: {
|
|
curvePrivate: Nacl.util.encodeBase64(curvePair.secretKey),
|
|
curvePublic: Nacl.util.encodeBase64(curvePair.publicKey)
|
|
}
|
|
};
|
|
};
|
|
Team.init = function(cfg, waitFor, emit) {
|
|
var team = {};
|
|
var store = cfg.store;
|
|
if (!store.loggedIn || !store.proxy.edPublic) {
|
|
return;
|
|
}
|
|
if (store.modules?.["team"]) {
|
|
return;
|
|
}
|
|
var ctx = {
|
|
store,
|
|
Store: cfg.Store,
|
|
pinPads: cfg.pinPads,
|
|
emit,
|
|
onReadyHandlers: {},
|
|
teams: {},
|
|
cache: {},
|
|
nocache: {},
|
|
updateMetadata: cfg.updateMetadata,
|
|
updateProgress: cfg.updateLoadingProgress,
|
|
progress: 0
|
|
};
|
|
if (!store.proxy.teams) {
|
|
store.proxy.teams = {};
|
|
}
|
|
var teams = store.proxy.teams;
|
|
ctx.numberOfTeams = Object.keys(teams).length;
|
|
ctx.store.proxy.on("change", [ "teams" ], (function(o, n, p) {
|
|
if (p[2] !== "hash") {
|
|
return;
|
|
}
|
|
updateMyRights(ctx, p[1], n);
|
|
}));
|
|
ctx.store.proxy.on("remove", [ "teams" ], (function(o, p) {
|
|
if (p[2] !== "hash") {
|
|
return;
|
|
}
|
|
updateMyRights(ctx, p[1]);
|
|
}));
|
|
var checkKeyPair = function(edPrivate, edPublic) {
|
|
if (!edPrivate || !edPublic) {
|
|
return true;
|
|
}
|
|
try {
|
|
var secretKey = Nacl.util.decodeBase64(edPrivate);
|
|
var pair = Nacl.sign.keyPair.fromSecretKey(secretKey);
|
|
return Nacl.util.encodeBase64(pair.publicKey) === edPublic;
|
|
} catch (e) {
|
|
return false;
|
|
}
|
|
};
|
|
var removeDuplicates = function() {
|
|
var _teams = {};
|
|
Object.keys(teams).forEach((function(id) {
|
|
try {
|
|
var t = teams[id];
|
|
var _t = _teams[t.channel];
|
|
var edPrivate = Util.find(t, [ "keys", "drive", "edPrivate" ]);
|
|
var edPublic = Util.find(t, [ "keys", "drive", "edPublic" ]);
|
|
if (!edPublic) {
|
|
Feedback.send("TEAM_CORRUPTED_EDPUBLIC");
|
|
} else if (edPrivate && edPublic && !checkKeyPair(edPrivate, edPublic)) {
|
|
Feedback.send("TEAM_CORRUPTED_EDPRIVATE");
|
|
delete teams[id].keys.drive.edPrivate;
|
|
edPrivate = undefined;
|
|
}
|
|
if (t.hash) {
|
|
var parsed = Hash.parseTypeHash("drive", t.hash);
|
|
if (parsed.version === 2 && t.hash.length !== 40) {
|
|
Feedback.send("TEAM_CORRUPTED_HASH");
|
|
}
|
|
}
|
|
if (!_t) {
|
|
_teams[t.channel] = id;
|
|
return;
|
|
}
|
|
var best = teams[_t];
|
|
var bestPrivate = Util.find(best, [ "keys", "drive", "edPrivate" ]);
|
|
var bestChat = Util.find(best, [ "keys", "chat", "edit" ]);
|
|
var chat = Util.find(t, [ "keys", "chat", "edit" ]);
|
|
if (!best.hash && t.hash) {
|
|
best.hash = t.hash;
|
|
}
|
|
if (!bestPrivate && edPrivate) {
|
|
best.keys.drive.edPrivate = edPrivate;
|
|
}
|
|
if (!bestChat && chat) {
|
|
best.keys.chat.edit = chat;
|
|
}
|
|
ctx.store.proxy.duplicateTeams = ctx.store.proxy.duplicateTeams || {};
|
|
ctx.store.proxy.duplicateTeams[id] = teams[id];
|
|
delete teams[id];
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
};
|
|
Object.keys(teams).forEach((function(id) {
|
|
ctx.onReadyHandlers[id] = [];
|
|
if (!Util.find(teams, [ id, "keys", "mailbox" ])) {
|
|
teams[id].keys.mailbox = deriveMailbox(teams[id]);
|
|
}
|
|
openChannel(ctx, teams[id], id, waitFor((function(err) {
|
|
if (err) {
|
|
delete ctx.onReadyHandlers[id];
|
|
delete ctx.cache[id];
|
|
if (err?.error === "NOCACHE") {
|
|
ctx.nocache[id] = true;
|
|
}
|
|
var txt = typeof err === "string" ? err : err.type || err.message;
|
|
Feedback.send("TEAM_LOADING_ERROR=" + txt);
|
|
return void console.error(err);
|
|
}
|
|
console.debug("Team " + id + " cache ready");
|
|
})), Cache.isEnabled());
|
|
}));
|
|
team.onReady = function(waitFor) {
|
|
removeDuplicates();
|
|
var checkTeam = function(id) {
|
|
if (!teams[id]) {
|
|
closeTeam(ctx, id);
|
|
delete ctx.onReadyHandlers[id];
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
Object.keys(ctx.teams).forEach(checkTeam);
|
|
Object.keys(ctx.onReadyHandlers).forEach((function(id) {
|
|
var closed = checkTeam(id);
|
|
if (closed) {
|
|
return;
|
|
}
|
|
var team = ctx.store.proxy.teams[id];
|
|
var rosterChan = Util.find(team, [ "keys", "roster", "channel" ]);
|
|
var _cb = Util.once(Util.mkAsync(waitFor()));
|
|
nThen((function(w) {
|
|
checkTeamChannels(ctx, id, team.channel, rosterChan, w, _cb);
|
|
}));
|
|
ctx.onReadyHandlers[id].push({
|
|
cb: _cb
|
|
});
|
|
}));
|
|
Object.keys(teams).forEach((function(id) {
|
|
if (ctx.onReadyHandlers[id] || ctx.teams[id]) {
|
|
return;
|
|
}
|
|
ctx.onReadyHandlers[id] = [];
|
|
if (!Util.find(teams, [ id, "keys", "mailbox" ])) {
|
|
teams[id].keys.mailbox = deriveMailbox(teams[id]);
|
|
}
|
|
openChannel(ctx, teams[id], id, waitFor((function(err) {
|
|
if (err) {
|
|
var txt = typeof err === "string" ? err : err.type || err.message;
|
|
Feedback.send("TEAM_LOADING_ERROR=" + txt);
|
|
return void console.error(err);
|
|
}
|
|
console.debug("Team " + id + " ready");
|
|
})));
|
|
}));
|
|
openCachedTeamChat();
|
|
onStoreReady.fire();
|
|
};
|
|
team.getTeam = function(id) {
|
|
return ctx.teams[id];
|
|
};
|
|
team.getTeamsData = function(app) {
|
|
var t = {};
|
|
var safe = false;
|
|
if ([ "drive", "teams", "settings" ].indexOf(app) !== -1) {
|
|
safe = true;
|
|
}
|
|
Object.keys(teams).forEach((function(id) {
|
|
if (!ctx.teams[id]) {
|
|
return;
|
|
}
|
|
var proxy = ctx.teams[id].proxy || {};
|
|
var nPads = proxy.drive && Object.keys(proxy.drive.filesData || {}).length;
|
|
var nSf = proxy.drive && Object.keys(proxy.drive.sharedFolders || {}).length;
|
|
t[id] = {
|
|
owner: teams[id].owner,
|
|
name: teams[id].metadata.name,
|
|
channel: teams[id].channel,
|
|
numberPads: nPads,
|
|
numberSf: nSf,
|
|
roster: Util.find(teams[id], [ "keys", "roster", "channel" ]),
|
|
edPublic: Util.find(teams[id], [ "keys", "drive", "edPublic" ]),
|
|
avatar: Util.find(teams[id], [ "metadata", "avatar" ]),
|
|
viewer: !Util.find(teams[id], [ "keys", "drive", "edPrivate" ]),
|
|
notifications: Util.find(teams[id], [ "keys", "mailbox", "channel" ]),
|
|
curvePublic: Util.find(teams[id], [ "keys", "mailbox", "keys", "curvePublic" ]),
|
|
validKeys: checkKeyPair(Util.find(teams[id], [ "keys", "drive", "edPrivate" ]), Util.find(teams[id], [ "keys", "drive", "edPublic" ]))
|
|
};
|
|
if (safe && ctx.teams[id]) {
|
|
t[id].secondaryKey = ctx.teams[id].secondaryKey;
|
|
}
|
|
if (ctx.teams[id]) {
|
|
t[id].hasSecondaryKey = Boolean(ctx.teams[id].secondaryKey);
|
|
}
|
|
}));
|
|
return t;
|
|
};
|
|
team.getTeams = function() {
|
|
return Object.keys(ctx.teams);
|
|
};
|
|
var isPending = function(teamId, curve) {
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return;
|
|
}
|
|
var state = team.roster && team.roster.getState();
|
|
if (!state.members) {
|
|
return;
|
|
}
|
|
var m = state.members[curve] || {};
|
|
return m.pending;
|
|
};
|
|
team.removeFromTeam = function(teamId, curve, pendingOnly) {
|
|
if (!teams[teamId]) {
|
|
return;
|
|
}
|
|
if (pendingOnly && !isPending(teamId, curve)) {
|
|
return;
|
|
}
|
|
if (ctx.onReadyHandlers[teamId]) {
|
|
ctx.onReadyHandlers[teamId].push({
|
|
cb: function() {
|
|
ctx.teams[teamId].roster.remove([ curve ], (function(err) {
|
|
if (err && err !== "NO_CHANGE") {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
var team = ctx.teams[teamId];
|
|
if (!team) {
|
|
return void console.error("TEAM MODULE ERROR");
|
|
}
|
|
team.roster.remove([ curve ], (function(err) {
|
|
if (err && err !== "NO_CHANGE") {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
};
|
|
team.changeMyRights = function(id, edit, teamData, cb) {
|
|
changeMyRights(ctx, id, edit, teamData, cb);
|
|
};
|
|
team.updateMyData = function(data) {
|
|
Object.keys(ctx.teams).forEach((function(id) {
|
|
var team = ctx.teams[id];
|
|
if (!team.roster) {
|
|
return;
|
|
}
|
|
var obj = {};
|
|
obj[data.curvePublic] = data;
|
|
team.roster.describe(obj, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
}));
|
|
}));
|
|
};
|
|
team.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
var listTeams = function(cb) {
|
|
var t = Util.clone(teams);
|
|
Object.keys(t).forEach((function(id) {
|
|
if (ctx.teams[id]) {
|
|
t[id].offline = ctx.teams[id].offline;
|
|
return;
|
|
}
|
|
t[id].error = true;
|
|
if (ctx.nocache[id]) {
|
|
t[id].offline = true;
|
|
}
|
|
}));
|
|
cb(t);
|
|
};
|
|
team.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "SUBSCRIBE") {
|
|
return void subscribe(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "LIST_TEAMS") {
|
|
return void listTeams(cb);
|
|
}
|
|
if (cmd === "OPEN_TEAM_CHAT") {
|
|
return void openTeamChat(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_TEAM_ROSTER") {
|
|
return void getTeamRoster(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_TEAM_METADATA") {
|
|
return void getTeamMetadata(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "SET_TEAM_METADATA") {
|
|
return void setTeamMetadata(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "OFFER_OWNERSHIP") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void offerOwnership(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "ANSWER_OWNERSHIP") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void answerOwnership(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DESCRIBE_USER") {
|
|
return void describeUser(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "INVITE_TO_TEAM") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void inviteToTeam(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "LEAVE_TEAM") {
|
|
return void leaveTeam(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "JOIN_TEAM") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void joinTeam(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "REMOVE_USER") {
|
|
return void removeUser(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DELETE_TEAM") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void deleteTeam(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CREATE_TEAM") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void createTeam(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_EDITABLE_FOLDERS") {
|
|
return void getEditableFolders(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CREATE_INVITE_LINK") {
|
|
return void createInviteLink(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "GET_PREVIEW_CONTENT") {
|
|
return void getPreviewContent(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "ACCEPT_LINK_INVITATION") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void acceptLinkInvitation(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return team;
|
|
};
|
|
Team.anonGetPreviewContent = function(cfg, data, cb) {
|
|
getPreviewContent(cfg, data, null, cb);
|
|
};
|
|
return Team;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireCommonConstants(), requireCommonRealtime(), requireProxyManager(), requireUserObject(), requireSharedfolder(), requireRoster(), requireCommonMessaging(), requireCommonFeedback(), requireInvitation(), requireCryptget(), requireCacheStore(), requirePinpad(), requireChainpadListmap(), requireCrypto(), requireChainpadNetflux(), requireChainpad_dist(), requireNthen(), requireNaclFast());
|
|
}
|
|
})();
|
|
})(team);
|
|
return team.exports;
|
|
}
|
|
var messenger = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredMessenger;
|
|
function requireMessenger() {
|
|
if (hasRequiredMessenger) return messenger.exports;
|
|
hasRequiredMessenger = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Crypto, Hash, Util, Realtime, Messaging, Constants, Messages = {}, PadTypes, nThen) => {
|
|
var Curve = Crypto.Curve;
|
|
var Msg = {};
|
|
Msg.setCustomize = data => {
|
|
Messages = data.Messages;
|
|
};
|
|
var Types = {
|
|
message: "MSG",
|
|
unfriend: "UNFRIEND",
|
|
mapId: "MAP_ID",
|
|
mapIdAck: "MAP_ID_ACK"
|
|
};
|
|
var clone = function(o) {
|
|
return JSON.parse(JSON.stringify(o));
|
|
};
|
|
var convertToUint8 = function(obj) {
|
|
var l = Object.keys(obj).length;
|
|
var u = new Uint8Array(l);
|
|
for (var i = 0; i < l; i++) {
|
|
u[i] = obj[i];
|
|
}
|
|
return u;
|
|
};
|
|
var createData = Messaging.createData;
|
|
var getFriend = function(proxy, pubkey) {
|
|
if (pubkey === proxy.curvePublic) {
|
|
var data = createData(proxy);
|
|
delete data.channel;
|
|
return data;
|
|
}
|
|
return proxy.friends ? proxy.friends[pubkey] : undefined;
|
|
};
|
|
var getFriendList = Msg.getFriendList = function(proxy) {
|
|
if (!proxy.friends) {
|
|
proxy.friends = {};
|
|
}
|
|
return proxy.friends;
|
|
};
|
|
var msgAlreadyKnown = function(channel, sig) {
|
|
return channel.messages.some((function(message) {
|
|
return message.sig === sig;
|
|
}));
|
|
};
|
|
var getFriendFromChannel = function(ctx, id) {
|
|
var proxy = ctx.store.proxy;
|
|
var friends = getFriendList(proxy);
|
|
var friend;
|
|
for (var k in friends) {
|
|
if (friends[k].channel === id) {
|
|
friend = friends[k];
|
|
break;
|
|
}
|
|
}
|
|
return friend;
|
|
};
|
|
var initRangeRequest = function(ctx, txid, chanId, cb) {
|
|
if (!ctx.range_requests) {
|
|
ctx.range_requests = {};
|
|
}
|
|
ctx.range_requests[txid] = {
|
|
messages: [],
|
|
cb,
|
|
chanId
|
|
};
|
|
};
|
|
var getRangeRequest = function(ctx, txid) {
|
|
return ctx.range_requests[txid];
|
|
};
|
|
var deleteRangeRequest = function(ctx, txid) {
|
|
delete ctx.range_requests[txid];
|
|
};
|
|
var getMoreHistory = function(ctx, chanId, hash, count, cb) {
|
|
if (typeof cb !== "function") {
|
|
return;
|
|
}
|
|
if (typeof hash !== "string") {
|
|
return void cb([]);
|
|
}
|
|
var chan = ctx.channels[chanId];
|
|
if (typeof chan === "undefined") {
|
|
console.error("chan is undefined. we're going to have a problem here");
|
|
return;
|
|
}
|
|
var txid = Util.uid();
|
|
initRangeRequest(ctx, txid, chanId, cb);
|
|
var msg = [ "GET_HISTORY_RANGE", chan.id, {
|
|
from: hash,
|
|
count,
|
|
txid
|
|
} ];
|
|
var network = ctx.store.network;
|
|
network.sendto(network.historyKeeper, JSON.stringify(msg)).then((function() {}), (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
var getChannelMessagesSince = function(ctx, channel, data, keys) {
|
|
var network = ctx.store.network;
|
|
console.log("Fetching [%s] messages since [%s]", channel.id, data.lastKnownHash || "");
|
|
if (channel.isPadChat || channel.isTeamChat) {
|
|
var txid = Util.uid();
|
|
initRangeRequest(ctx, txid, channel.id, undefined);
|
|
var msg0 = [ "GET_HISTORY_RANGE", channel.id, {
|
|
count: 10,
|
|
txid
|
|
} ];
|
|
network.sendto(network.historyKeeper, JSON.stringify(msg0)).then((function() {}), (function(err) {
|
|
console.error(err);
|
|
}));
|
|
return;
|
|
}
|
|
var proxy = ctx.store.proxy;
|
|
var friend = getFriendFromChannel(ctx, channel.id) || {};
|
|
var cfg = {
|
|
metadata: {
|
|
validateKey: keys ? keys.validateKey : undefined,
|
|
owners: [ proxy.edPublic, friend.edPublic ]
|
|
},
|
|
lastKnownHash: data.lastKnownHash
|
|
};
|
|
var msg = [ "GET_HISTORY", channel.id, cfg ];
|
|
network.sendto(network.historyKeeper, JSON.stringify(msg)).then((function() {}), (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
var setChannelHead = function(ctx, id, hash, cb) {
|
|
var channel = ctx.channels[id];
|
|
if (channel.isFriendChat) {
|
|
var friend = getFriendFromChannel(ctx, id);
|
|
if (!friend) {
|
|
return void cb({
|
|
error: "NO_SUCH_FRIEND"
|
|
});
|
|
}
|
|
friend.lastKnownHash = hash;
|
|
} else if (channel.isPadChat) ; else if (channel.isTeamChat) ; else {
|
|
return void cb({
|
|
error: "NOT_IMPLEMENTED"
|
|
});
|
|
}
|
|
cb();
|
|
};
|
|
var onChannelReady = function(ctx, chanId) {
|
|
var channel = ctx.channels[chanId];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
channel.ready = true;
|
|
channel.onReady.fire();
|
|
};
|
|
var onIdMessage = function(ctx, msg, sender) {
|
|
var channel, parsed0;
|
|
try {
|
|
parsed0 = JSON.parse(msg);
|
|
channel = ctx.channels[parsed0.channel];
|
|
if (!channel || channel.wc.members.indexOf(sender) === -1) {
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
return void console.error(e, msg);
|
|
}
|
|
var decryptedMsg = channel.decrypt(parsed0.msg);
|
|
if (!decryptedMsg) {
|
|
return void console.error("Failed to decrypt message");
|
|
}
|
|
var parsed = Util.tryParse(decryptedMsg);
|
|
if (!parsed) {
|
|
return void console.error(decryptedMsg);
|
|
}
|
|
if (parsed[0] !== Types.mapId && parsed[0] !== Types.mapIdAck) {
|
|
return;
|
|
}
|
|
if (parsed[2] !== sender || !parsed[1]) {
|
|
return;
|
|
}
|
|
channel.mapId[sender] = parsed[1];
|
|
ctx.emit("JOIN", {
|
|
info: parsed[1],
|
|
id: channel.id
|
|
}, channel.clients);
|
|
if (channel.readOnly) {
|
|
return;
|
|
}
|
|
if (parsed[0] !== Types.mapId) {
|
|
return;
|
|
}
|
|
var proxy = ctx.store.proxy || {};
|
|
var myData = createData(proxy);
|
|
delete myData.channel;
|
|
var rMsg = [ Types.mapIdAck, myData, channel.wc.myID ];
|
|
var rMsgStr = JSON.stringify(rMsg);
|
|
var cryptMsg = channel.encrypt(rMsgStr);
|
|
var data = {
|
|
channel: channel.id,
|
|
msg: cryptMsg
|
|
};
|
|
var network = ctx.store.network;
|
|
network.sendto(sender, JSON.stringify(data));
|
|
};
|
|
var orderMessages = function(channel, new_messages) {
|
|
var messages = channel.messages;
|
|
new_messages.reverse().forEach((function(msg) {
|
|
messages.unshift(msg);
|
|
}));
|
|
};
|
|
var removeFromFriendList = function(ctx, curvePublic, cb) {
|
|
var proxy = ctx.store.proxy;
|
|
var friends = proxy.friends;
|
|
if (!friends) {
|
|
return;
|
|
}
|
|
delete friends[curvePublic];
|
|
Realtime.whenRealtimeSyncs(ctx.store.realtime, (function() {
|
|
ctx.updateMetadata();
|
|
cb();
|
|
}));
|
|
};
|
|
var pushMsg = function(ctx, channel, cryptMsg) {
|
|
var sig = cryptMsg.slice(0, 64);
|
|
if (msgAlreadyKnown(channel, sig)) {
|
|
return;
|
|
}
|
|
var msg = channel.decrypt(cryptMsg);
|
|
var parsedMsg = JSON.parse(msg);
|
|
var curvePublic;
|
|
if (parsedMsg[0] === Types.message) {
|
|
var res = {
|
|
type: parsedMsg[0],
|
|
sig,
|
|
author: parsedMsg[1],
|
|
time: parsedMsg[2],
|
|
text: parsedMsg[3],
|
|
channel: channel.id,
|
|
name: parsedMsg[4]
|
|
};
|
|
channel.messages.push(res);
|
|
if (channel.ready) {
|
|
ctx.emit("MESSAGE", res, channel.clients);
|
|
}
|
|
return true;
|
|
}
|
|
var proxy = ctx.store.proxy;
|
|
if (parsedMsg[0] === Types.unfriend) {
|
|
curvePublic = parsedMsg[1];
|
|
if (curvePublic === proxy.curvePublic) {
|
|
return;
|
|
}
|
|
removeFromFriendList(ctx, curvePublic, (function() {
|
|
channel.wc.leave(Types.unfriend);
|
|
var network = ctx.store.network;
|
|
if (channel.onReconnect) {
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
delete ctx.channels[channel.id];
|
|
ctx.emit("UNFRIEND", {
|
|
curvePublic,
|
|
fromMe: false
|
|
}, channel.clients);
|
|
}));
|
|
return;
|
|
}
|
|
};
|
|
var onDirectMessage = function(ctx, msg, sender) {
|
|
var hk = ctx.store.network.historyKeeper;
|
|
if (sender !== hk) {
|
|
return void onIdMessage(ctx, msg, sender);
|
|
}
|
|
var parsed = JSON.parse(msg);
|
|
if ((parsed.validateKey || parsed.owners) && parsed.channel) {
|
|
return;
|
|
}
|
|
var channel;
|
|
if (/HISTORY_RANGE/.test(parsed[0])) {
|
|
var txid = parsed[1];
|
|
var req = getRangeRequest(ctx, txid);
|
|
var type = parsed[0];
|
|
if (!req) {
|
|
return;
|
|
}
|
|
channel = ctx.channels[req.chanId];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
if (!req.cb) {
|
|
if (type === "HISTORY_RANGE") {
|
|
if (!Array.isArray(parsed[2])) {
|
|
return;
|
|
}
|
|
pushMsg(ctx, channel, parsed[2][4]);
|
|
} else if (type === "HISTORY_RANGE_END") {
|
|
onChannelReady(ctx, req.chanId);
|
|
return deleteRangeRequest(ctx, txid);
|
|
}
|
|
return;
|
|
}
|
|
if (type === "HISTORY_RANGE") {
|
|
req.messages.push(parsed[2]);
|
|
} else if (type === "HISTORY_RANGE_END") {
|
|
var decrypted = req.messages.map((function(msg) {
|
|
if (msg[2] !== "MSG") {
|
|
return;
|
|
}
|
|
try {
|
|
return {
|
|
d: JSON.parse(channel.decrypt(msg[4])),
|
|
sig: msg[4].slice(0, 64)
|
|
};
|
|
} catch (e) {
|
|
return void console.log("failed to decrypt");
|
|
}
|
|
})).filter((function(decrypted) {
|
|
if (!decrypted || !decrypted.d || decrypted.d[0] !== Types.message) {
|
|
return;
|
|
}
|
|
if (msgAlreadyKnown(channel, decrypted.sig)) {
|
|
return;
|
|
}
|
|
return decrypted;
|
|
})).map((function(O) {
|
|
return {
|
|
type: O.d[0],
|
|
sig: O.sig,
|
|
author: O.d[1],
|
|
time: O.d[2],
|
|
text: O.d[3],
|
|
channel: req.chanId,
|
|
name: O.d[4]
|
|
};
|
|
}));
|
|
orderMessages(channel, decrypted);
|
|
req.cb(decrypted);
|
|
return deleteRangeRequest(ctx, txid);
|
|
} else {
|
|
console.log(parsed);
|
|
}
|
|
return;
|
|
}
|
|
if (parsed.channel && ctx.channels[parsed.channel]) {
|
|
channel = ctx.channels[parsed.channel];
|
|
if (parsed.error === "ECLEARED") {
|
|
setChannelHead(ctx, parsed.channel, "", (function() {}));
|
|
channel.messages = [];
|
|
ctx.emit("CLEAR_CHANNEL", parsed.channel, channel.clients);
|
|
return;
|
|
}
|
|
if (parsed.error && (parsed.error === "EINVAL" || parsed.error === "EUNKNOWN")) {
|
|
setChannelHead(ctx, parsed.channel, "", (function() {
|
|
getChannelMessagesSince(ctx, channel, {}, {});
|
|
}));
|
|
return;
|
|
}
|
|
if (parsed.state && parsed.state === 1 && parsed.channel) {
|
|
return void onChannelReady(ctx, parsed.channel);
|
|
}
|
|
}
|
|
channel = ctx.channels[parsed[3]];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
pushMsg(ctx, channel, parsed[4]);
|
|
};
|
|
var onMessage = function(ctx, msg, sender, chan) {
|
|
var channel = ctx.channels[chan.id];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
pushMsg(ctx, channel, msg);
|
|
};
|
|
var onFriendRemoved = function(ctx, curvePublic, chanId) {
|
|
var channel = ctx.channels[chanId];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
if (channel.wc) {
|
|
channel.wc.leave(Types.unfriend);
|
|
}
|
|
var network = ctx.store.network;
|
|
if (channel.onReconnect) {
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
delete ctx.channels[channel.id];
|
|
ctx.emit("UNFRIEND", {
|
|
curvePublic,
|
|
fromMe: true
|
|
}, ctx.friendsClients);
|
|
};
|
|
var removeFriend = function(ctx, curvePublic, _cb) {
|
|
var cb = Util.once(_cb);
|
|
if (typeof cb !== "function") {
|
|
return void console.error("NO_CALLBACK");
|
|
}
|
|
var proxy = ctx.store.proxy;
|
|
var data = getFriend(proxy, curvePublic);
|
|
if (!data) {
|
|
console.error("friend is not valid");
|
|
return void cb({
|
|
error: "INVALID_FRIEND"
|
|
});
|
|
}
|
|
var channel = ctx.channels[data.channel];
|
|
if (ctx.store.mailbox && data.curvePublic && data.notifications) {
|
|
Messaging.removeFriend(ctx.store, curvePublic, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb({
|
|
error: obj.error
|
|
});
|
|
}
|
|
ctx.updateMetadata();
|
|
cb(obj);
|
|
}));
|
|
return;
|
|
}
|
|
if (!channel) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
try {
|
|
var msg = [ Types.unfriend, proxy.curvePublic, +new Date ];
|
|
var msgStr = JSON.stringify(msg);
|
|
var cryptMsg = channel.encrypt(msgStr);
|
|
channel.wc.bcast(cryptMsg).then((function() {
|
|
onFriendRemoved(ctx, curvePublic, data.channel);
|
|
removeFromFriendList(ctx, curvePublic, (function() {
|
|
cb();
|
|
}));
|
|
}), (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
onFriendRemoved(ctx, curvePublic, data.channel);
|
|
removeFromFriendList(ctx, curvePublic, (function() {
|
|
cb();
|
|
}));
|
|
}));
|
|
} catch (e) {
|
|
cb({
|
|
error: e
|
|
});
|
|
}
|
|
};
|
|
var cancelFriend = function(ctx, data, _cb) {
|
|
var cb = Util.once(_cb);
|
|
if (typeof cb !== "function") {
|
|
return void console.error("NO_CALLBACK");
|
|
}
|
|
ctx.Store.cancelFriendRequest(data, cb);
|
|
};
|
|
var getAllClients = function(ctx) {
|
|
var all = [];
|
|
Array.prototype.push.apply(all, ctx.friendsClients);
|
|
Object.keys(ctx.channels).forEach((function(id) {
|
|
Array.prototype.push.apply(all, ctx.channels[id].clients);
|
|
}));
|
|
return Util.deduplicateString(all);
|
|
};
|
|
var muteUser = function(ctx, data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var proxy = ctx.store.proxy;
|
|
var muted = proxy.mutedUsers = proxy.mutedUsers || {};
|
|
if (muted[data.curvePublic]) {
|
|
return void cb();
|
|
}
|
|
muted[data.curvePublic] = data;
|
|
ctx.emit("UPDATE_MUTED", null, getAllClients(ctx));
|
|
cb();
|
|
};
|
|
var unmuteUser = function(ctx, curvePublic, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var proxy = ctx.store.proxy;
|
|
var muted = proxy.mutedUsers = proxy.mutedUsers || {};
|
|
delete muted[curvePublic];
|
|
ctx.emit("UPDATE_MUTED", null, getAllClients(ctx));
|
|
cb(Object.keys(muted).length);
|
|
};
|
|
var getMutedUsers = function(ctx, cb) {
|
|
var proxy = ctx.store.proxy;
|
|
if (cb) {
|
|
return void cb(proxy.mutedUsers || {});
|
|
}
|
|
return proxy.mutedUsers || {};
|
|
};
|
|
var openChannel = function(ctx, data) {
|
|
var proxy = ctx.store.proxy;
|
|
var network = ctx.store.network;
|
|
var hk = network.historyKeeper;
|
|
var keys = data.keys;
|
|
var encryptor = data.encryptor || Curve.createEncryptor(keys);
|
|
var channel = {
|
|
id: data.channel,
|
|
isFriendChat: data.isFriendChat,
|
|
isPadChat: data.isPadChat,
|
|
isTeamChat: data.isTeamChat,
|
|
padChan: data.padChan,
|
|
readOnly: data.readOnly,
|
|
ready: false,
|
|
onReady: Util.mkEvent(true),
|
|
sending: false,
|
|
messages: [],
|
|
clients: data.clients || [],
|
|
onUserlistUpdate: data.onUserlistUpdate || function() {},
|
|
mapId: {}
|
|
};
|
|
if (data.onReady) {
|
|
channel.onReady.reg(data.onReady);
|
|
}
|
|
channel.encrypt = function(msg) {
|
|
if (channel.readOnly) {
|
|
return;
|
|
}
|
|
return encryptor.encrypt(msg);
|
|
};
|
|
channel.decrypt = data.decrypt || function(msg) {
|
|
return encryptor.decrypt(msg);
|
|
};
|
|
var onJoining = function(peer) {
|
|
if (peer === hk) {
|
|
return;
|
|
}
|
|
if (channel.readOnly) {
|
|
return;
|
|
}
|
|
if (channel.isPadChat) {
|
|
return;
|
|
}
|
|
var myData = createData(proxy);
|
|
delete myData.channel;
|
|
var msg = [ Types.mapId, myData, channel.wc.myID ];
|
|
var msgStr = JSON.stringify(msg);
|
|
var cryptMsg = channel.encrypt(msgStr);
|
|
var data = {
|
|
channel: channel.id,
|
|
msg: cryptMsg
|
|
};
|
|
network.sendto(peer, JSON.stringify(data));
|
|
};
|
|
var onLeaving = function(peer) {
|
|
if (peer === hk) {
|
|
return;
|
|
}
|
|
var otherData = channel.mapId[peer];
|
|
if (!otherData) {
|
|
return;
|
|
}
|
|
if (channel.wc.members.some((function(nId) {
|
|
return channel.mapId[nId] && channel.mapId[nId].curvePublic === otherData.curvePublic;
|
|
}))) {
|
|
return;
|
|
}
|
|
ctx.emit("LEAVE", {
|
|
info: otherData,
|
|
id: channel.id
|
|
}, channel.clients);
|
|
};
|
|
var onOpen = function(chan) {
|
|
channel.wc = chan;
|
|
ctx.channels[data.channel] = channel;
|
|
chan.on("message", (function(msg, sender) {
|
|
onMessage(ctx, msg, sender, chan);
|
|
}));
|
|
chan.on("join", onJoining);
|
|
chan.on("leave", onLeaving);
|
|
getChannelMessagesSince(ctx, channel, data, keys);
|
|
};
|
|
network.join(data.channel).then(onOpen, (function(err) {
|
|
console.error(err);
|
|
}));
|
|
channel.onReconnect = function() {
|
|
if (channel && channel.stopped) {
|
|
return;
|
|
}
|
|
if (!ctx.channels[data.channel]) {
|
|
return;
|
|
}
|
|
network.join(data.channel).then(onOpen, (function(err) {
|
|
console.error(err);
|
|
}));
|
|
};
|
|
network.on("reconnect", channel.onReconnect);
|
|
};
|
|
var sendMessage = function(ctx, id, payload, cb) {
|
|
var channel = ctx.channels[id];
|
|
if (!channel) {
|
|
return void cb({
|
|
error: "NO_CHANNEL"
|
|
});
|
|
}
|
|
if (channel.readOnly) {
|
|
return void cb({
|
|
error: "FORBIDDEN"
|
|
});
|
|
}
|
|
var network = ctx.store.network;
|
|
if (!network.webChannels.some((function(wc) {
|
|
if (wc.id === channel.wc.id) {
|
|
return true;
|
|
}
|
|
}))) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
var proxy = ctx.store.proxy || {};
|
|
var msg = [ Types.message, proxy.curvePublic, +new Date, payload ];
|
|
if (!channel.isFriendChat) {
|
|
var name = proxy[Constants.displayNameKey] || Messages.anonymous + "#" + (proxy.uid || ctx.store.noDriveUid).slice(0, 5);
|
|
msg.push(name);
|
|
}
|
|
var msgStr = JSON.stringify(msg);
|
|
var cryptMsg = channel.encrypt(msgStr);
|
|
channel.wc.bcast(cryptMsg).then((function() {
|
|
pushMsg(ctx, channel, cryptMsg);
|
|
cb();
|
|
}), (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
}));
|
|
};
|
|
var getOnlineList = function(ctx, chanId) {
|
|
var channel = ctx.channels[chanId];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
var online = [];
|
|
var myData = createData(ctx.store.proxy, false);
|
|
online.push(myData.curvePublic);
|
|
channel.wc.members.forEach((function(nId) {
|
|
if (nId === ctx.store.network.historyKeeper) {
|
|
return;
|
|
}
|
|
var data = channel.mapId[nId] || {};
|
|
if (!data.curvePublic) {
|
|
return;
|
|
}
|
|
if (online.indexOf(data.curvePublic) !== -1) {
|
|
return;
|
|
}
|
|
online.push(data.curvePublic);
|
|
}));
|
|
return online;
|
|
};
|
|
var getStatus = function(ctx, chanId, cb) {
|
|
var channel = ctx.channels[chanId];
|
|
if (!channel) {
|
|
return void cb("NO_SUCH_CHANNEL");
|
|
}
|
|
if (channel.onUserlistUpdate) {
|
|
channel.onUserlistUpdate();
|
|
}
|
|
var proxy = ctx.store.proxy || {};
|
|
var online = channel.wc.members.some((function(nId) {
|
|
if (nId === ctx.store.network.historyKeeper) {
|
|
return;
|
|
}
|
|
var data = channel.mapId[nId] || undefined;
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
return data.curvePublic !== proxy.curvePublic;
|
|
}));
|
|
cb(online);
|
|
};
|
|
var getMyInfo = function(ctx, cb) {
|
|
var proxy = ctx.store.proxy || {};
|
|
cb({
|
|
curvePublic: proxy.curvePublic,
|
|
displayName: proxy[Constants.displayNameKey]
|
|
});
|
|
};
|
|
var loadFriend = function(ctx, clientId, friend, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var chanId = friend.channel;
|
|
var channel = ctx.channels[chanId];
|
|
if (channel) {
|
|
return void channel.onReady.reg((function() {
|
|
if (channel.clients.indexOf(clientId) === -1) {
|
|
channel.clients.push(clientId);
|
|
}
|
|
cb();
|
|
}));
|
|
}
|
|
var proxy = ctx.store.proxy;
|
|
var keys = Curve.deriveKeys(friend.curvePublic, proxy.curvePrivate);
|
|
var data = {
|
|
keys,
|
|
channel: friend.channel,
|
|
lastKnownHash: friend.lastKnownHash,
|
|
owners: [ proxy.edPublic, friend.edPublic ],
|
|
isFriendChat: true,
|
|
clients: clientId ? [ clientId ] : ctx.friendsClients,
|
|
onReady: cb
|
|
};
|
|
openChannel(ctx, data);
|
|
};
|
|
var initFriends = function(ctx, clientId, cb) {
|
|
var friends = getFriendList(ctx.store.proxy);
|
|
nThen((function(waitFor) {
|
|
Object.keys(friends).forEach((function(key) {
|
|
if (key === "me") {
|
|
delete friends.me.channel;
|
|
return;
|
|
}
|
|
var friend = clone(friends[key]);
|
|
if (typeof friend !== "object") {
|
|
return;
|
|
}
|
|
if (!friend.channel) {
|
|
return;
|
|
}
|
|
loadFriend(ctx, clientId, friend, waitFor());
|
|
}));
|
|
})).nThen((function() {
|
|
if (ctx.friendsClients.indexOf(clientId) === -1) {
|
|
ctx.friendsClients.push(clientId);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var getRooms = function(ctx, data, cb) {
|
|
var proxy = ctx.store.proxy;
|
|
if (data && data.curvePublic) {
|
|
var curvePublic = data.curvePublic;
|
|
var friend = getFriend(proxy, curvePublic);
|
|
if (!friend) {
|
|
return void cb({
|
|
error: "NO_SUCH_FRIEND"
|
|
});
|
|
}
|
|
var channel = ctx.channels[friend.channel];
|
|
if (!channel) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
return void cb([ {
|
|
id: channel.id,
|
|
isFriendChat: true,
|
|
name: friend.displayName,
|
|
lastKnownHash: friend.lastKnownHash,
|
|
curvePublic: friend.curvePublic,
|
|
messages: channel.messages
|
|
} ]);
|
|
}
|
|
if (data && data.padChat) {
|
|
var pCChannel = ctx.channels[data.padChat];
|
|
if (!pCChannel) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
return void cb([ {
|
|
id: pCChannel.id,
|
|
isPadChat: true,
|
|
messages: pCChannel.messages
|
|
} ]);
|
|
}
|
|
if (data && data.teamChat) {
|
|
var tCChannel = ctx.channels[data.teamChat];
|
|
if (!tCChannel) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
return void cb([ {
|
|
id: tCChannel.id,
|
|
isTeamChat: true,
|
|
messages: tCChannel.messages
|
|
} ]);
|
|
}
|
|
var rooms = Object.keys(ctx.channels).map((function(id) {
|
|
var r = ctx.channels[id];
|
|
var name, lastKnownHash, curvePublic;
|
|
if (r.isFriendChat) {
|
|
var friend = getFriendFromChannel(ctx, id);
|
|
if (!friend) {
|
|
return null;
|
|
}
|
|
name = friend.displayName;
|
|
lastKnownHash = friend.lastKnownHash;
|
|
curvePublic = friend.curvePublic;
|
|
} else if (r.isPadChat) {
|
|
return;
|
|
} else if (r.isTeamChat) {
|
|
return;
|
|
} else ;
|
|
return {
|
|
id: r.id,
|
|
isFriendChat: r.isFriendChat,
|
|
name,
|
|
lastKnownHash,
|
|
curvePublic,
|
|
messages: r.messages
|
|
};
|
|
})).filter((function(x) {
|
|
return x;
|
|
}));
|
|
cb(rooms);
|
|
};
|
|
var getUserList = function(ctx, data, cb) {
|
|
var room = ctx.channels[data.id];
|
|
if (!room) {
|
|
return void cb({
|
|
error: "NO_SUCH_CHANNEL"
|
|
});
|
|
}
|
|
if (room.isFriendChat) {
|
|
var friend = getFriendFromChannel(ctx, data.id);
|
|
if (!friend) {
|
|
return void cb({
|
|
error: "NO_SUCH_FRIEND"
|
|
});
|
|
}
|
|
cb([ friend ]);
|
|
} else {
|
|
cb([]);
|
|
}
|
|
};
|
|
var openPadChat = function(ctx, clientId, data, _cb) {
|
|
var chanId = data.channel;
|
|
var cb = Util.once(Util.mkAsync((function() {
|
|
ctx.emit("PADCHAT_READY", chanId, [ clientId ]);
|
|
_cb();
|
|
})));
|
|
var channel = ctx.channels[chanId];
|
|
if (channel) {
|
|
return void channel.onReady.reg((function() {
|
|
if (channel.clients.indexOf(clientId) === -1) {
|
|
channel.clients.push(clientId);
|
|
}
|
|
cb();
|
|
}));
|
|
}
|
|
var secret = data.secret;
|
|
if (secret.keys.cryptKey) {
|
|
secret.keys.cryptKey = convertToUint8(secret.keys.cryptKey);
|
|
}
|
|
var encryptor = Crypto.createEncryptor(secret.keys);
|
|
var vKey = secret.keys && secret.keys.validateKey || ctx.validateKeys[secret.channel];
|
|
var chanData = {
|
|
padChan: data.secret && data.secret.channel,
|
|
readOnly: typeof secret.keys === "object" && !secret.keys.validateKey,
|
|
encryptor,
|
|
channel: data.channel,
|
|
isPadChat: true,
|
|
decrypt: function(msg) {
|
|
return encryptor.decrypt(msg, vKey);
|
|
},
|
|
clients: [ clientId ],
|
|
onReady: cb
|
|
};
|
|
openChannel(ctx, chanData);
|
|
};
|
|
var openTeamChat = function(ctx, clientId, data, onUpdate, _cb) {
|
|
var chatData = data;
|
|
var chanId = chatData.channel;
|
|
var secret = chatData.secret;
|
|
if (!chanId || !secret) {
|
|
return void _cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var cb = Util.once(Util.mkAsync((function() {
|
|
ctx.emit("TEAMCHAT_READY", chanId, [ clientId ]);
|
|
_cb({
|
|
readOnly: typeof secret.keys === "object" && !secret.keys.validateKey,
|
|
channel: chanId
|
|
});
|
|
})));
|
|
var channel = ctx.channels[chanId];
|
|
if (channel) {
|
|
return void channel.onReady.reg((function() {
|
|
if (channel.clients.indexOf(clientId) === -1) {
|
|
channel.clients.push(clientId);
|
|
}
|
|
cb();
|
|
}));
|
|
}
|
|
if (secret.keys.cryptKey) {
|
|
secret.keys.cryptKey = convertToUint8(secret.keys.cryptKey);
|
|
}
|
|
var encryptor = Crypto.createEncryptor(secret.keys);
|
|
var vKey = secret.keys && secret.keys.validateKey || chatData.validateKey;
|
|
var chanData = {
|
|
teamId: data.teamId,
|
|
readOnly: typeof secret.keys === "object" && !secret.keys.validateKey,
|
|
encryptor,
|
|
channel: chanId,
|
|
isTeamChat: true,
|
|
decrypt: function(msg) {
|
|
return encryptor.decrypt(msg, vKey);
|
|
},
|
|
clients: [ clientId ],
|
|
onUserlistUpdate: onUpdate,
|
|
onReady: cb
|
|
};
|
|
openChannel(ctx, chanData);
|
|
};
|
|
var clearOwnedChannel = function(ctx, id, cb) {
|
|
var channel = ctx.channels[id];
|
|
if (!channel) {
|
|
return void cb({
|
|
error: "NO_CHANNEL"
|
|
});
|
|
}
|
|
if (!ctx.store.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
ctx.store.rpc.clearOwnedChannel(id, (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
if (!err) {
|
|
channel.messages = [];
|
|
ctx.emit("CLEAR_CHANNEL", id, channel.clients);
|
|
}
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, cId) {
|
|
var friendsIdx = ctx.friendsClients.indexOf(cId);
|
|
if (friendsIdx !== -1) {
|
|
ctx.friendsClients.splice(friendsIdx, 1);
|
|
}
|
|
Object.keys(ctx.channels).forEach((function(id) {
|
|
var channel = ctx.channels[id];
|
|
var clients = channel.clients;
|
|
var idx = clients.indexOf(cId);
|
|
if (idx !== -1) {
|
|
clients.splice(idx, 1);
|
|
}
|
|
if (clients.length === 0) {
|
|
if (channel.wc) {
|
|
channel.wc.leave();
|
|
}
|
|
var network = ctx.store.network;
|
|
if (channel.onReconnect) {
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
channel.stopped = true;
|
|
delete ctx.channels[id];
|
|
return true;
|
|
}
|
|
}));
|
|
};
|
|
Msg.init = function(cfg, waitFor, emit) {
|
|
var messenger = {};
|
|
var store = cfg.store;
|
|
if (store.messenger) {
|
|
store.messenger.addListener();
|
|
return store.messenger;
|
|
}
|
|
if (!PadTypes.isAvailable("contacts")) {
|
|
return;
|
|
}
|
|
var ctx = {
|
|
store,
|
|
Store: cfg.Store,
|
|
updateMetadata: cfg.updateMetadata,
|
|
pinPads: cfg.pinPads,
|
|
emit,
|
|
friendsClients: [],
|
|
channels: {},
|
|
validateKeys: {},
|
|
range_requests: {}
|
|
};
|
|
messenger.addListener = function() {
|
|
if (store.proxy) {
|
|
store.proxy.on("change", [ "mutedUsers" ], (function() {
|
|
ctx.emit("UPDATE_MUTED", null, getAllClients(ctx));
|
|
}));
|
|
}
|
|
};
|
|
messenger.addListener();
|
|
let onNetwork = Util.once((_network => {
|
|
const network = ctx.store.network || _network;
|
|
network.on("message", (function(msg, sender) {
|
|
onDirectMessage(ctx, msg, sender);
|
|
}));
|
|
network.on("disconnect", (function() {
|
|
ctx.emit("DISCONNECT", null, getAllClients(ctx));
|
|
}));
|
|
network.on("reconnect", (function() {
|
|
ctx.emit("RECONNECT", null, getAllClients(ctx));
|
|
}));
|
|
}));
|
|
store.networkPromise?.then(onNetwork);
|
|
if (ctx.store.network) {
|
|
onNetwork();
|
|
}
|
|
messenger.onFriendUpdate = function(curve) {
|
|
var friend = getFriend(store.proxy, curve);
|
|
if (!friend || !friend.channel) {
|
|
return;
|
|
}
|
|
var chan = ctx.channels[friend.channel];
|
|
if (chan) {
|
|
ctx.emit("UPDATE_DATA", {
|
|
info: clone(friend),
|
|
channel: friend.channel
|
|
}, chan.clients);
|
|
}
|
|
};
|
|
messenger.onFriendAdded = function(friendData) {
|
|
if (!ctx.friendsClients.length) {
|
|
return;
|
|
}
|
|
var friend = getFriend(ctx.store.proxy, friendData.curvePublic);
|
|
if (typeof friend !== "object") {
|
|
return;
|
|
}
|
|
var channel = friend.channel;
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
var chanId = friend.channel;
|
|
var chan = ctx.channels[chanId];
|
|
if (chan) {
|
|
return;
|
|
}
|
|
loadFriend(ctx, null, friend, (function() {
|
|
emit("FRIEND", {
|
|
curvePublic: friend.curvePublic
|
|
}, ctx.friendsClients);
|
|
}));
|
|
};
|
|
messenger.onFriendRemoved = function(curvePublic, chanId) {
|
|
onFriendRemoved(ctx, curvePublic, chanId);
|
|
};
|
|
messenger.getOnlineList = function(chanId) {
|
|
return getOnlineList(ctx, chanId);
|
|
};
|
|
messenger.storeValidateKey = function(chan, key) {
|
|
ctx.validateKeys[chan] = key;
|
|
};
|
|
messenger.leavePad = function(padChan) {
|
|
delete ctx.validateKeys[padChan];
|
|
Object.keys(ctx.channels).some((function(chatChan) {
|
|
var channel = ctx.channels[chatChan];
|
|
if (channel.padChan !== padChan) {
|
|
return;
|
|
}
|
|
if (channel.wc) {
|
|
channel.wc.leave();
|
|
}
|
|
var network = ctx.store.network;
|
|
if (channel.onReconnect) {
|
|
network.off("reconnect", channel.onReconnect);
|
|
}
|
|
channel.stopped = true;
|
|
delete ctx.channels[chatChan];
|
|
return true;
|
|
}));
|
|
};
|
|
messenger.openTeamChat = function(data, onUpdate, cId, cb) {
|
|
openTeamChat(ctx, cId, data, onUpdate, cb);
|
|
};
|
|
messenger.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
messenger.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "INIT_FRIENDS") {
|
|
return void initFriends(ctx, clientId, cb);
|
|
}
|
|
if (cmd === "GET_ROOMS") {
|
|
return void getRooms(ctx, data, cb);
|
|
}
|
|
if (cmd === "GET_MUTED_USERS") {
|
|
return void getMutedUsers(ctx, cb);
|
|
}
|
|
if (cmd === "GET_USERLIST") {
|
|
return void getUserList(ctx, data, cb);
|
|
}
|
|
if (cmd === "OPEN_PAD_CHAT") {
|
|
return void openPadChat(ctx, clientId, data, cb);
|
|
}
|
|
if (cmd === "GET_MY_INFO") {
|
|
return void getMyInfo(ctx, cb);
|
|
}
|
|
if (cmd === "REMOVE_FRIEND") {
|
|
return void removeFriend(ctx, data, cb);
|
|
}
|
|
if (cmd === "CANCEL_FRIEND") {
|
|
return void cancelFriend(ctx, data, cb);
|
|
}
|
|
if (cmd === "MUTE_USER") {
|
|
return void muteUser(ctx, data, cb);
|
|
}
|
|
if (cmd === "UNMUTE_USER") {
|
|
return void unmuteUser(ctx, data, cb);
|
|
}
|
|
if (cmd === "GET_STATUS") {
|
|
return void getStatus(ctx, data, cb);
|
|
}
|
|
if (cmd === "GET_MORE_HISTORY") {
|
|
return void getMoreHistory(ctx, data.id, data.sig, data.count, cb);
|
|
}
|
|
if (cmd === "SEND_MESSAGE") {
|
|
return void sendMessage(ctx, data.id, data.content, cb);
|
|
}
|
|
if (cmd === "SET_CHANNEL_HEAD") {
|
|
return void setChannelHead(ctx, data.id, data.sig, cb);
|
|
}
|
|
if (cmd === "CLEAR_OWNED_CHANNEL") {
|
|
return void clearOwnedChannel(ctx, data, cb);
|
|
}
|
|
};
|
|
return messenger;
|
|
};
|
|
return Msg;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCrypto(), requireCommonHash(), requireCommonUtil(), requireCommonRealtime(), requireCommonMessaging(), requireCommonConstants(), undefined, requirePadTypes(), requireNthen());
|
|
}
|
|
})();
|
|
})(messenger);
|
|
return messenger.exports;
|
|
}
|
|
var history = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredHistory;
|
|
function requireHistory() {
|
|
if (hasRequiredHistory) return history.exports;
|
|
hasRequiredHistory = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, UserObject, nThen) => {
|
|
var History = {};
|
|
var commands = {};
|
|
var getAccountChannels = function(ctx) {
|
|
var channels = [];
|
|
var edPublic = Util.find(ctx.store, [ "proxy", "edPublic" ]);
|
|
var driveOwned = (Util.find(ctx.store, [ "driveMetadata", "owners" ]) || []).indexOf(edPublic) !== -1;
|
|
if (driveOwned) {
|
|
channels.push(ctx.store.driveChannel);
|
|
}
|
|
var profile = ctx.store.proxy.profile;
|
|
if (profile) {
|
|
var profileChan = profile.edit ? Hash.hrefToHexChannelId("/profile/#" + profile.edit, null) : null;
|
|
if (profileChan) {
|
|
channels.push(profileChan);
|
|
}
|
|
}
|
|
if (ctx.store.proxy.todo) {
|
|
channels.push(Hash.hrefToHexChannelId("/todo/#" + ctx.store.proxy.todo, null));
|
|
}
|
|
var mailboxes = ctx.store.proxy.mailboxes;
|
|
if (mailboxes) {
|
|
var mList = Object.keys(mailboxes).map((function(m) {
|
|
return {
|
|
lastKnownHash: mailboxes[m].lastKnownHash,
|
|
channel: mailboxes[m].channel
|
|
};
|
|
}));
|
|
Array.prototype.push.apply(channels, mList);
|
|
}
|
|
var sf = ctx.store.proxy[UserObject.SHARED_FOLDERS];
|
|
if (sf) {
|
|
var sfChannels = Object.keys(sf).map((function(fId) {
|
|
var data = sf[fId];
|
|
if (!data || !data.owners) {
|
|
return;
|
|
}
|
|
var isOwner = Array.isArray(data.owners) && data.owners.indexOf(edPublic) !== -1;
|
|
if (!isOwner) {
|
|
return;
|
|
}
|
|
return data.channel;
|
|
})).filter(Boolean);
|
|
Array.prototype.push.apply(channels, sfChannels);
|
|
}
|
|
return channels;
|
|
};
|
|
let getTeamChannels = function(ctx, teamId) {
|
|
let team = Util.find(ctx.store, [ "proxy", "teams", teamId ]);
|
|
if (!team) {
|
|
return [];
|
|
}
|
|
let channels = [ team.channel ];
|
|
let roster = team.keys.roster;
|
|
channels.push({
|
|
channel: roster.channel,
|
|
lastKnownHash: roster.lastKnownHash
|
|
});
|
|
return channels;
|
|
};
|
|
var getEdPublic = function(ctx, teamId) {
|
|
if (!teamId) {
|
|
return Util.find(ctx.store, [ "proxy", "edPublic" ]);
|
|
}
|
|
var teamData = Util.find(ctx, [ "store", "proxy", "teams", teamId ]);
|
|
return Util.find(teamData, [ "keys", "drive", "edPublic" ]);
|
|
};
|
|
var getRpc = function(ctx, teamId) {
|
|
if (!teamId) {
|
|
return ctx.store.rpc;
|
|
}
|
|
var teams = ctx.store.modules["team"];
|
|
if (!teams) {
|
|
return;
|
|
}
|
|
var team = teams.getTeam(teamId);
|
|
if (!team) {
|
|
return;
|
|
}
|
|
return team.rpc;
|
|
};
|
|
var getHistoryData = function(ctx, channel, lastKnownHash, teamId, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var edPublic = getEdPublic(ctx, teamId);
|
|
var Store = ctx.Store;
|
|
var total = 0;
|
|
var history = 0;
|
|
var metadata = 0;
|
|
var hash;
|
|
nThen((function(waitFor) {
|
|
Store.getFileSize(null, {
|
|
channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
if (typeof obj.size === "undefined") {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
total = obj.size;
|
|
})));
|
|
Store.getHistory(null, {
|
|
channel,
|
|
lastKnownHash
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
waitFor.abort();
|
|
return void cb(obj);
|
|
}
|
|
if (!Array.isArray(obj)) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
if (!obj.length) {
|
|
return;
|
|
}
|
|
hash = obj[0].hash;
|
|
var messages = obj.map((function(data) {
|
|
return data.msg;
|
|
}));
|
|
history = messages.join("\n").length;
|
|
})), true);
|
|
Store.getPadMetadata(null, {
|
|
channel
|
|
}, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
return;
|
|
}
|
|
if (!obj || typeof obj !== "object") {
|
|
return;
|
|
}
|
|
metadata = JSON.stringify(obj).length;
|
|
if (!obj || !Array.isArray(obj.owners) || obj.owners.indexOf(edPublic) === -1) {
|
|
waitFor.abort();
|
|
return void cb({
|
|
error: "INSUFFICIENT_PERMISSIONS"
|
|
});
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
cb({
|
|
size: total - metadata - history,
|
|
hash
|
|
});
|
|
}));
|
|
};
|
|
commands.GET_HISTORY_SIZE = function(ctx, data, cId, cb) {
|
|
if (!ctx.store.loggedIn || !ctx.store.rpc) {
|
|
return void cb({
|
|
error: "INSUFFICIENT_PERMISSIONS"
|
|
});
|
|
}
|
|
var channels = data.channels;
|
|
if (!Array.isArray(channels)) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var warning = [];
|
|
if (data.account) {
|
|
channels = getAccountChannels(ctx);
|
|
} else if (data.team) {
|
|
channels = getTeamChannels(ctx, data.team);
|
|
}
|
|
var size = 0;
|
|
var res = [];
|
|
nThen((function(waitFor) {
|
|
channels.forEach((function(chan) {
|
|
var channel = chan;
|
|
var lastKnownHash;
|
|
if (typeof chan === "object" && chan.channel) {
|
|
channel = chan.channel;
|
|
lastKnownHash = chan.lastKnownHash;
|
|
}
|
|
getHistoryData(ctx, channel, lastKnownHash, data.teamId, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
warning.push(obj.error);
|
|
return;
|
|
}
|
|
size += obj.size;
|
|
if (!obj.hash) {
|
|
return;
|
|
}
|
|
res.push({
|
|
channel,
|
|
hash: obj.hash
|
|
});
|
|
})));
|
|
}));
|
|
})).nThen((function() {
|
|
cb({
|
|
warning: warning.length ? warning : undefined,
|
|
channels: res,
|
|
size
|
|
});
|
|
}));
|
|
};
|
|
commands.TRIM_HISTORY = function(ctx, data, cId, cb) {
|
|
if (!ctx.store.loggedIn || !ctx.store.rpc) {
|
|
return void cb({
|
|
error: "INSUFFICIENT_PERMISSIONS"
|
|
});
|
|
}
|
|
var channels = data.channels;
|
|
if (!Array.isArray(channels)) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var rpc = getRpc(ctx, data.teamId);
|
|
if (!rpc) {
|
|
return void cb({
|
|
error: "ENORPC"
|
|
});
|
|
}
|
|
var warning = [];
|
|
nThen((function(waitFor) {
|
|
channels.forEach((function(obj) {
|
|
rpc.trimHistory(obj, waitFor((function(err) {
|
|
if (err) {
|
|
warning.push(err);
|
|
return;
|
|
}
|
|
})));
|
|
}));
|
|
})).nThen((function() {
|
|
if (channels.length === 1 && warning.length) {
|
|
return void cb({
|
|
error: warning[0]
|
|
});
|
|
}
|
|
cb({
|
|
warning: warning.length ? warning : undefined
|
|
});
|
|
}));
|
|
};
|
|
History.init = function(cfg, waitFor, emit) {
|
|
var history = {};
|
|
if (!cfg.store) {
|
|
return;
|
|
}
|
|
var ctx = {
|
|
store: cfg.store,
|
|
Store: cfg.Store,
|
|
pinPads: cfg.pinPads,
|
|
updateMetadata: cfg.updateMetadata,
|
|
emit
|
|
};
|
|
history.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
try {
|
|
commands[cmd](ctx, data, clientId, cb);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
return history;
|
|
};
|
|
return History;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireUserObject(), requireNthen());
|
|
}
|
|
})();
|
|
})(history);
|
|
return history.exports;
|
|
}
|
|
var calendar$1 = {
|
|
exports: {}
|
|
};
|
|
var recurrence = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredRecurrence;
|
|
function requireRecurrence() {
|
|
if (hasRequiredRecurrence) return recurrence.exports;
|
|
hasRequiredRecurrence = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = Util => {
|
|
var Rec = {};
|
|
const window = globalThis;
|
|
var debug = function() {};
|
|
var getWeekNo = Rec.getWeekNo = function(date, wkst) {
|
|
if (typeof wkst !== "number") {
|
|
wkst = 1;
|
|
}
|
|
var newYear = new Date(date.getFullYear(), 0, 1);
|
|
var day = newYear.getDay() - wkst;
|
|
day = day >= 0 ? day : day + 7;
|
|
var daynum = Math.floor((date.getTime() - newYear.getTime()) / 864e5) + 1;
|
|
var weeknum;
|
|
if (day < 4) {
|
|
weeknum = Math.floor((daynum + day - 1) / 7) + 1;
|
|
if (weeknum > 52) {
|
|
var nYear = new Date(date.getFullYear() + 1, 0, 1);
|
|
var nday = nYear.getDay() - wkst;
|
|
nday = nday >= 0 ? nday : nday + 7;
|
|
weeknum = nday < 4 ? 1 : 53;
|
|
}
|
|
} else {
|
|
weeknum = Math.floor((daynum + day - 1) / 7);
|
|
}
|
|
return weeknum;
|
|
};
|
|
var getYearDay = function(date) {
|
|
var start = new Date(date.getFullYear(), 0, 0);
|
|
var diff = date - start + (start.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1e3;
|
|
var oneDay = 1e3 * 60 * 60 * 24;
|
|
return Math.floor(diff / oneDay);
|
|
};
|
|
var setYearDay = function(date, day) {
|
|
if (typeof day !== "number" || Math.abs(day) < 1 || Math.abs(day) > 366) {
|
|
return;
|
|
}
|
|
if (day < 0) {
|
|
var max = getYearDay(new Date(date.getFullYear(), 11, 31));
|
|
day = max + day + 1;
|
|
}
|
|
date.setMonth(0);
|
|
date.setDate(day);
|
|
return true;
|
|
};
|
|
var getEndData = function(s, e) {
|
|
if (s > e) {
|
|
return void console.error("Wrong data");
|
|
}
|
|
var days;
|
|
if (e.getFullYear() === s.getFullYear()) {
|
|
days = getYearDay(e) - getYearDay(s);
|
|
} else {
|
|
var tmp = new Date(s.getFullYear(), 11, 31);
|
|
var d1 = getYearDay(tmp) - getYearDay(s);
|
|
var de = getYearDay(e);
|
|
days = d1 + de;
|
|
while (tmp.getFullYear() + 1 < e.getFullYear()) {
|
|
tmp.setFullYear(tmp.getFullYear() + 1);
|
|
days += getYearDay(tmp);
|
|
}
|
|
}
|
|
return {
|
|
h: e.getHours(),
|
|
m: e.getMinutes(),
|
|
days
|
|
};
|
|
};
|
|
var setEndData = function(s, e, data) {
|
|
e.setTime(+s);
|
|
if (!data) {
|
|
return;
|
|
}
|
|
e.setHours(data.h);
|
|
e.setMinutes(data.m);
|
|
e.setSeconds(0);
|
|
e.setDate(s.getDate() + data.days);
|
|
};
|
|
var DAYORDER = Rec.DAYORDER = [ "SU", "MO", "TU", "WE", "TH", "FR", "SA" ];
|
|
var getDayData = function(str) {
|
|
var pos = Number(str.slice(0, -2));
|
|
var day = DAYORDER.indexOf(str.slice(-2));
|
|
return pos ? [ pos, day ] : day;
|
|
};
|
|
var goToFirstWeekDay = function(date, wkst) {
|
|
var d = date.getDay();
|
|
wkst = typeof wkst === "number" ? wkst : 1;
|
|
if (d >= wkst) {
|
|
date.setDate(date.getDate() - (d - wkst));
|
|
} else {
|
|
date.setDate(date.getDate() - (7 + d - wkst));
|
|
}
|
|
};
|
|
var getDateStr = function(date) {
|
|
return date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
|
};
|
|
var FREQ = {};
|
|
FREQ["daily"] = function(s, i) {
|
|
s.setDate(s.getDate() + i);
|
|
};
|
|
FREQ["weekly"] = function(s, i) {
|
|
s.setDate(s.getDate() + i * 7);
|
|
};
|
|
FREQ["monthly"] = function(s, i) {
|
|
s.setMonth(s.getMonth() + i);
|
|
};
|
|
FREQ["yearly"] = function(s, i) {
|
|
s.setFullYear(s.getFullYear() + i);
|
|
};
|
|
var EXPAND = {};
|
|
EXPAND["month"] = function(dateS, origin, b) {
|
|
var oS = new Date(origin.start);
|
|
var a = dateS.getMonth() + 1;
|
|
var toAdd = (b - a + 12) % 12;
|
|
var m = dateS.getMonth() + toAdd;
|
|
dateS.setMonth(m);
|
|
dateS.setDate(oS.getDate());
|
|
if (dateS.getMonth() !== m) {
|
|
return;
|
|
}
|
|
return true;
|
|
};
|
|
EXPAND["weekno"] = function(dateS, origin, week, rule) {
|
|
var wkst = rule && rule.wkst;
|
|
if (typeof wkst !== "number") {
|
|
wkst = 1;
|
|
}
|
|
var oS = new Date(origin.start);
|
|
var lastD = new Date(dateS.getFullYear(), 11, 31);
|
|
var lastW = getWeekNo(lastD, wkst);
|
|
var doubleOne = lastW === 1;
|
|
if (lastW === 1) {
|
|
lastW = 52;
|
|
}
|
|
var a = getWeekNo(dateS, wkst);
|
|
if (!week || week > lastW) {
|
|
return false;
|
|
}
|
|
if (week < 0) {
|
|
week = lastW + week + 1;
|
|
}
|
|
var toAdd = week - a;
|
|
var weekS = new Date(+dateS);
|
|
weekS.setDate(weekS.getDate() + toAdd * 7);
|
|
goToFirstWeekDay(weekS, wkst);
|
|
var all = "aaaaaaa".split("").map((function(o, i) {
|
|
var date = new Date(+weekS);
|
|
date.setDate(date.getDate() + i);
|
|
if (date.getFullYear() !== dateS.getFullYear()) {
|
|
return;
|
|
}
|
|
return date.toLocaleDateString() !== oS.toLocaleDateString() && date;
|
|
})).filter(Boolean);
|
|
if (week === 1 && doubleOne) {
|
|
goToFirstWeekDay(lastD, wkst);
|
|
"aaaaaaa".split("").some((function(o, i) {
|
|
var date = new Date(+lastD);
|
|
date.setDate(date.getDate() + i);
|
|
if (date.toLocaleDateString() === oS.toLocaleDateString()) {
|
|
return;
|
|
}
|
|
if (date.getFullYear() > dateS.getFullYear()) {
|
|
return true;
|
|
}
|
|
all.push(date);
|
|
}));
|
|
}
|
|
return all.length ? all : undefined;
|
|
};
|
|
EXPAND["yearday"] = function(dateS, origin, b) {
|
|
var y = dateS.getFullYear();
|
|
var state = setYearDay(dateS, b);
|
|
if (!state) {
|
|
return;
|
|
}
|
|
if (dateS.getFullYear() !== y) {
|
|
return;
|
|
}
|
|
return true;
|
|
};
|
|
EXPAND["monthday"] = function(dateS, origin, b, rule) {
|
|
if (typeof b !== "number" || Math.abs(b) < 1 || Math.abs(b) > 31) {
|
|
return false;
|
|
}
|
|
var setMonthDay = function(date, day) {
|
|
var m = date.getMonth();
|
|
if (day < 0) {
|
|
var tmp = new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
day = tmp.getDate() + day + 1;
|
|
}
|
|
date.setDate(day);
|
|
return date.getMonth() === m;
|
|
};
|
|
if (rule.freq === "monthly") {
|
|
return setMonthDay(dateS, b);
|
|
}
|
|
var all = "aaaaaaaaaaaa".split("").map((function(o, i) {
|
|
var date = new Date(dateS.getFullYear(), i, 1);
|
|
var ok = setMonthDay(date, b);
|
|
return ok ? date : undefined;
|
|
})).filter(Boolean);
|
|
return all.length ? all : undefined;
|
|
};
|
|
EXPAND["day"] = function(dateS, origin, b, rule) {
|
|
var day = getDayData(b);
|
|
var pos;
|
|
if (Array.isArray(day)) {
|
|
pos = day[0];
|
|
day = day[1];
|
|
}
|
|
var all = [];
|
|
if (![ 0, 1, 2, 3, 4, 5, 6 ].includes(day)) {
|
|
return false;
|
|
}
|
|
var filterPos = function(m) {
|
|
if (!pos) {
|
|
return;
|
|
}
|
|
var _all = [];
|
|
"aaaaaaaaaaaa".split("").some((function(a, i) {
|
|
if (typeof m !== "undefined" && i !== m) {
|
|
return;
|
|
}
|
|
var _pos;
|
|
var tmp = all.filter((function(d) {
|
|
return d.getMonth() === i;
|
|
}));
|
|
if (pos < 0) {
|
|
_pos = tmp.length + pos;
|
|
} else {
|
|
_pos = pos - 1;
|
|
}
|
|
_all.push(tmp[_pos]);
|
|
return typeof m !== "undefined" && i === m;
|
|
}));
|
|
all = _all.filter(Boolean);
|
|
};
|
|
var tmp;
|
|
if (rule.freq === "yearly") {
|
|
tmp = new Date(+dateS);
|
|
var y = dateS.getFullYear();
|
|
while (tmp.getDay() !== day) {
|
|
tmp.setDate(tmp.getDate() + 1);
|
|
}
|
|
while (tmp.getFullYear() === y) {
|
|
all.push(new Date(+tmp));
|
|
tmp.setDate(tmp.getDate() + 7);
|
|
}
|
|
filterPos();
|
|
return all;
|
|
}
|
|
if (rule.freq === "monthly") {
|
|
tmp = new Date(+dateS);
|
|
var m = dateS.getMonth();
|
|
while (tmp.getDay() !== day) {
|
|
tmp.setDate(tmp.getDate() + 1);
|
|
}
|
|
while (tmp.getMonth() === m) {
|
|
all.push(new Date(+tmp));
|
|
tmp.setDate(tmp.getDate() + 7);
|
|
}
|
|
filterPos(m);
|
|
return all;
|
|
}
|
|
if (rule.freq === "weekly") {
|
|
while (dateS.getDay() !== day) {
|
|
dateS.setDate(dateS.getDate() + 1);
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var LIMIT = {};
|
|
LIMIT["month"] = function(events, rule) {
|
|
return events.filter((function(s) {
|
|
return rule.includes(s.getMonth() + 1);
|
|
}));
|
|
};
|
|
LIMIT["weekno"] = function(events, weeks, rules) {
|
|
return events.filter((function(s) {
|
|
var wkst = rules && rules.wkst;
|
|
if (typeof wkst !== "number") {
|
|
wkst = 1;
|
|
}
|
|
var lastD = new Date(s.getFullYear(), 11, 31);
|
|
var lastW = getWeekNo(lastD, wkst);
|
|
if (lastW === 1) {
|
|
lastW = 52;
|
|
}
|
|
var w = getWeekNo(s, wkst);
|
|
return weeks.some((function(week) {
|
|
if (week > 0) {
|
|
return week === w;
|
|
}
|
|
return w === lastW + week + 1;
|
|
}));
|
|
}));
|
|
};
|
|
LIMIT["yearday"] = function(events, days) {
|
|
return events.filter((function(s) {
|
|
var d = getYearDay(s);
|
|
var max = getYearDay(new Date(s.getFullYear(), 11, 31));
|
|
return days.some((function(day) {
|
|
if (day > 0) {
|
|
return day === d;
|
|
}
|
|
return d === max + day + 1;
|
|
}));
|
|
}));
|
|
};
|
|
LIMIT["monthday"] = function(events, rule) {
|
|
return events.filter((function(s) {
|
|
var r = Util.clone(rule);
|
|
r = r.map((function(b) {
|
|
if (b < 0) {
|
|
var tmp = new Date(s.getFullYear(), s.getMonth() + 1, 0);
|
|
b = tmp.getDate() + b + 1;
|
|
}
|
|
return b;
|
|
}));
|
|
return r.includes(s.getDate());
|
|
}));
|
|
};
|
|
LIMIT["day"] = function(events, days, rules) {
|
|
return events.filter((function(s) {
|
|
var dayStr = s.toLocaleDateString();
|
|
var type = "yearly";
|
|
if (rules.freq === "monthly" || rules.freq === "yearly" && rules.by && rules.by.month) {
|
|
type = "monthly";
|
|
}
|
|
return days.some((function(r) {
|
|
var day = getDayData(r);
|
|
var pos;
|
|
if (Array.isArray(day)) {
|
|
pos = day[0];
|
|
day = day[1];
|
|
}
|
|
if (!pos) {
|
|
return s.getDay() === day;
|
|
}
|
|
var d = new Date(s.getFullYear(), s.getMonth(), 1);
|
|
if (type === "yearly") {
|
|
d.setMonth(0);
|
|
}
|
|
var res = EXPAND["day"](d, {}, r, {
|
|
freq: type
|
|
});
|
|
return res.some((function(date) {
|
|
return date.toLocaleDateString() === dayStr;
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
LIMIT["setpos"] = function(events, rule) {
|
|
var init = events.slice();
|
|
var rules = Util.deduplicateString(rule.slice().map((function(n) {
|
|
if (n > 0) {
|
|
return n - 1;
|
|
}
|
|
if (n === 0) {
|
|
return;
|
|
}
|
|
return init.length + n;
|
|
})));
|
|
return events.filter((function(ev) {
|
|
var idx = init.indexOf(ev);
|
|
return rules.includes(idx);
|
|
}));
|
|
};
|
|
var BYORDER = [ "month", "weekno", "yearday", "monthday", "day" ];
|
|
var BYDAYORDER = [ "month", "monthday", "day" ];
|
|
Rec.getMonthId = function(d) {
|
|
return d.getFullYear() + "-" + d.getMonth();
|
|
};
|
|
var cache = window.CP_calendar_cache = {};
|
|
var recurringAcross = {};
|
|
Rec.resetCache = function() {
|
|
cache = window.CP_calendar_cache = {};
|
|
recurringAcross = {};
|
|
};
|
|
var iterate = function(rule, _origin, s) {
|
|
var origin = Util.clone(_origin);
|
|
var oS = new Date(origin.start);
|
|
var id = origin.id.split("|")[0];
|
|
var uid = s.toLocaleDateString();
|
|
cache[id] = cache[id] || {};
|
|
var inter = rule.interval || 1;
|
|
var freq = rule.freq;
|
|
var all = [];
|
|
var limit = function(byrule, n) {
|
|
all = LIMIT[byrule](all, n, rule);
|
|
};
|
|
var expand = function(byrule) {
|
|
return function(n) {
|
|
var _s = new Date(+s);
|
|
if (rule.freq === "yearly") {
|
|
_s.setMonth(0);
|
|
_s.setDate(1);
|
|
} else if (rule.freq === "monthly") {
|
|
_s.setDate(1);
|
|
} else if (rule.freq === "weekly") {
|
|
goToFirstWeekDay(_s, rule.wkst);
|
|
} else if (rule.freq === "daily") ;
|
|
var add = EXPAND[byrule](_s, origin, n, rule);
|
|
if (!add) {
|
|
return;
|
|
}
|
|
if (Array.isArray(add)) {
|
|
add = add.filter((function(dateS) {
|
|
return dateS.toLocaleDateString() !== oS.toLocaleDateString();
|
|
}));
|
|
Array.prototype.push.apply(all, add);
|
|
} else {
|
|
if (_s.toLocaleDateString() === oS.toLocaleDateString()) {
|
|
return;
|
|
}
|
|
all.push(_s);
|
|
}
|
|
};
|
|
};
|
|
var it = Util.once((function() {
|
|
FREQ[freq](s, inter);
|
|
}));
|
|
var addDefault = function() {
|
|
if (freq === "monthly") {
|
|
s.setDate(15);
|
|
} else if (freq === "yearly" && oS.getMonth() === 1 && oS.getDate() === 29) {
|
|
s.setDate(28);
|
|
}
|
|
it();
|
|
var _s = new Date(+s);
|
|
if (freq === "monthly" || freq === "yearly") {
|
|
_s.setDate(oS.getDate());
|
|
if (_s.getDate() !== oS.getDate()) {
|
|
return;
|
|
}
|
|
if (freq === "yearly" && _s.getMonth() !== oS.getMonth()) {
|
|
return;
|
|
}
|
|
}
|
|
all.push(_s);
|
|
};
|
|
if (Array.isArray(cache[id][uid])) {
|
|
debug("Get cache", id, uid);
|
|
if (freq === "monthly") {
|
|
s.setDate(15);
|
|
} else if (freq === "yearly" && oS.getMonth() === 1 && oS.getDate() === 29) {
|
|
s.setDate(28);
|
|
}
|
|
it();
|
|
return cache[id][uid];
|
|
}
|
|
if (rule.by && freq === "yearly") {
|
|
var order = BYORDER.slice();
|
|
var monthLimit = false;
|
|
if (rule.by.weekno || rule.by.yearday || rule.by.monthday || rule.by.day) {
|
|
order.shift();
|
|
monthLimit = true;
|
|
}
|
|
var first = true;
|
|
order.forEach((function(_order) {
|
|
var r = rule.by[_order];
|
|
if (!r) {
|
|
return;
|
|
}
|
|
if (first) {
|
|
r.forEach(expand(_order));
|
|
first = false;
|
|
} else if (_order === "day") {
|
|
if (rule.by.yearday || rule.by.monthday || rule.by.weekno) {
|
|
limit("day", rule.by.day);
|
|
} else {
|
|
rule.by.day.forEach(expand("day"));
|
|
}
|
|
} else {
|
|
limit(_order, r);
|
|
}
|
|
}));
|
|
if (rule.by.month && monthLimit) {
|
|
limit("month", rule.by.month);
|
|
}
|
|
}
|
|
if (rule.by && freq === "monthly") {
|
|
if (!rule.by.monthday && !rule.by.day) {
|
|
addDefault();
|
|
} else if (rule.by.monthday) {
|
|
rule.by.monthday.forEach(expand("monthday"));
|
|
} else if (rule.by.day) {
|
|
rule.by.day.forEach(expand("day"));
|
|
}
|
|
if (rule.by.month) {
|
|
limit("month", rule.by.month);
|
|
}
|
|
if (rule.by.day && rule.by.monthday) {
|
|
limit("day", rule.by.day);
|
|
}
|
|
}
|
|
if (rule.by && freq === "weekly") {
|
|
if (!rule.by.day) {
|
|
addDefault();
|
|
} else {
|
|
rule.by.day.forEach(expand("day"));
|
|
}
|
|
if (rule.by.month) {
|
|
limit("month", rule.by.month);
|
|
}
|
|
}
|
|
if (rule.by && freq === "daily") {
|
|
addDefault();
|
|
BYDAYORDER.forEach((function(_order) {
|
|
var r = rule.by[_order];
|
|
if (!r) {
|
|
return;
|
|
}
|
|
limit(_order, r);
|
|
}));
|
|
}
|
|
all.sort((function(a, b) {
|
|
return a - b;
|
|
}));
|
|
if (rule.by && rule.by.setpos) {
|
|
limit("setpos", rule.by.setpos);
|
|
}
|
|
if (!rule.by || !Object.keys(rule.by).length) {
|
|
addDefault();
|
|
} else {
|
|
it();
|
|
}
|
|
var done = [];
|
|
all = all.filter((function(newS) {
|
|
var start = new Date(+newS).toLocaleDateString();
|
|
if (done.includes(start)) {
|
|
return false;
|
|
}
|
|
done.push(start);
|
|
return true;
|
|
}));
|
|
debug("Set cache", id, uid);
|
|
cache[id][uid] = all;
|
|
return all;
|
|
};
|
|
var getNextRules = function(obj) {
|
|
if (!obj.recUpdate) {
|
|
return [];
|
|
}
|
|
var _allRules = {};
|
|
var _obj = obj.recUpdate.from;
|
|
Object.keys(_obj || {}).forEach((function(d) {
|
|
var u = _obj[d];
|
|
if (u.recurrenceRule) {
|
|
_allRules[d] = u.recurrenceRule;
|
|
}
|
|
}));
|
|
return Object.keys(_allRules).sort((function(a, b) {
|
|
return Number(a) - Number(b);
|
|
})).map((function(k) {
|
|
var r = Util.clone(_allRules[k]);
|
|
if (!FREQ[r.freq]) {
|
|
return;
|
|
}
|
|
if (r.interval && r.interval < 1) {
|
|
return;
|
|
}
|
|
r._start = Number(k);
|
|
return r;
|
|
})).filter(Boolean);
|
|
};
|
|
var fixTimeZone = function(evTimeZone, origin, target) {
|
|
var getOffset = function(date, tz) {
|
|
let iso = date.toLocaleString("en-CA", {
|
|
timeZone: tz,
|
|
hour12: false
|
|
}).replace(", ", "T");
|
|
iso += "." + date.getMilliseconds().toString().padStart(3, "0");
|
|
let utcDate = new Date(iso + "Z");
|
|
return -(utcDate - date);
|
|
};
|
|
var myTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
var offset = getOffset(origin, evTimeZone) - getOffset(target, evTimeZone);
|
|
var myOffset = getOffset(origin, myTimeZone) - getOffset(target, myTimeZone);
|
|
return myOffset - offset;
|
|
};
|
|
Rec.getRecurring = function(months, events) {
|
|
if (window.CP_DEV_MODE) {
|
|
debug = console.warn;
|
|
}
|
|
var toAdd = [];
|
|
months.forEach((function(monthId) {
|
|
var ms = monthId.split("-");
|
|
var _startMonth = new Date(ms[0], ms[1]);
|
|
var _endMonth = new Date(+_startMonth);
|
|
_endMonth.setMonth(_endMonth.getMonth() + 1);
|
|
_endMonth.setMilliseconds(-1);
|
|
debug("Compute month", _startMonth.toLocaleDateString());
|
|
var rec = events || [];
|
|
rec.forEach((function(obj) {
|
|
var _start = new Date(obj.start);
|
|
var _end = new Date(obj.end);
|
|
var _origin = obj;
|
|
var rule = obj.recurrenceRule;
|
|
if (!rule) {
|
|
return;
|
|
}
|
|
var nextRules = getNextRules(obj);
|
|
var nextRule = nextRules.shift();
|
|
if (_start >= _endMonth) {
|
|
return;
|
|
}
|
|
var until = rule.until;
|
|
var _nextRules = nextRules.slice();
|
|
var _nextRule = nextRule;
|
|
while (_nextRule && _nextRule._start && _nextRule._start < _startMonth) {
|
|
until = nextRule.until;
|
|
_nextRule = _nextRules.shift();
|
|
}
|
|
if (until < _startMonth) {
|
|
return;
|
|
}
|
|
var endData = getEndData(_start, _end);
|
|
if (rule.interval && rule.interval < 1) {
|
|
return;
|
|
}
|
|
if (!FREQ[rule.freq]) {
|
|
return;
|
|
}
|
|
debug("Iterate over", obj.title, obj);
|
|
debug("Use rule", rule);
|
|
var count = rule.count;
|
|
var c = 1;
|
|
var next = function(start) {
|
|
var evS = new Date(+start);
|
|
if (count && c >= count) {
|
|
return;
|
|
}
|
|
debug("Start iteration", evS.toLocaleDateString());
|
|
var _toAdd = iterate(rule, obj, evS);
|
|
debug("Iteration results", JSON.stringify(_toAdd.map((function(o) {
|
|
return new Date(o).toLocaleDateString();
|
|
}))));
|
|
if (!_toAdd.length) {
|
|
if (evS.getFullYear() < _startMonth.getFullYear() || evS < _endMonth) {
|
|
return void next(evS);
|
|
}
|
|
return;
|
|
}
|
|
var stop = false;
|
|
var newrule = false;
|
|
_toAdd.some((function(_newS) {
|
|
var _ev = Util.clone(obj);
|
|
_ev.id = _origin.id + "|" + +_newS;
|
|
var _evS = new Date(+_newS);
|
|
var _evE = new Date(+_newS);
|
|
setEndData(_evS, _evE, endData);
|
|
_ev.start = +_evS;
|
|
_ev.end = +_evE;
|
|
_ev._count = c;
|
|
if (_ev.isAllDay && _ev.startDay) {
|
|
_ev.startDay = getDateStr(_evS);
|
|
}
|
|
if (_ev.isAllDay && _ev.endDay) {
|
|
_ev.endDay = getDateStr(_evE);
|
|
}
|
|
if (nextRule && _ev.start === nextRule._start) {
|
|
newrule = true;
|
|
}
|
|
var useNewRule = function() {
|
|
if (!newrule) {
|
|
return;
|
|
}
|
|
debug("Use new rule", nextRule);
|
|
_ev._count = c;
|
|
count = nextRule.count;
|
|
c = 1;
|
|
evS = +_evS;
|
|
obj = _ev;
|
|
rule = nextRule;
|
|
nextRule = nextRules.shift();
|
|
};
|
|
if (c >= count) {
|
|
debug(_evS.toLocaleDateString(), "count");
|
|
stop = true;
|
|
return true;
|
|
}
|
|
if (_evS >= _endMonth) {
|
|
debug(_evS.toLocaleDateString(), "endMonth");
|
|
stop = true;
|
|
return true;
|
|
}
|
|
if (rule.until && _evS > rule.until) {
|
|
debug(_evS.toLocaleDateString(), "until");
|
|
stop = true;
|
|
return true;
|
|
}
|
|
if (_evS < _start) {
|
|
debug(_evS.toLocaleDateString(), "start");
|
|
return;
|
|
}
|
|
c++;
|
|
if (_evE < _startMonth) {
|
|
debug(_evS.toLocaleDateString(), "startMonth");
|
|
if (newrule) {
|
|
useNewRule();
|
|
}
|
|
return;
|
|
}
|
|
if (_evS < _endMonth && _evE >= _endMonth || _evS < _startMonth && _evE >= _startMonth) {
|
|
if (recurringAcross[_ev.id] && recurringAcross[_ev.id].includes(_ev.start)) {
|
|
return;
|
|
} else {
|
|
recurringAcross[_ev.id] = recurringAcross[_ev.id] || [];
|
|
recurringAcross[_ev.id].push(_ev.start);
|
|
}
|
|
}
|
|
if (_origin.timeZone && !_ev.isAllDay) {
|
|
var offset = fixTimeZone(_origin.timeZone, _start, _evS);
|
|
_ev.start += offset;
|
|
_ev.end += offset;
|
|
}
|
|
toAdd.push(_ev);
|
|
if (newrule) {
|
|
useNewRule();
|
|
return true;
|
|
}
|
|
}));
|
|
if (!stop) {
|
|
next(evS);
|
|
}
|
|
};
|
|
next(_start);
|
|
debug("Added this month (all events)", toAdd.map((function(ev) {
|
|
return new Date(ev.start).toLocaleDateString();
|
|
})));
|
|
}));
|
|
}));
|
|
return toAdd;
|
|
};
|
|
Rec.getAllOccurrences = function(ev) {
|
|
if (!ev.recurrenceRule) {
|
|
return [ ev.start ];
|
|
}
|
|
var r = ev.recurrenceRule;
|
|
if (!r.until && !r.count) {
|
|
return false;
|
|
}
|
|
var all = [ ev.start ];
|
|
var d = new Date(ev.start);
|
|
d.setDate(15);
|
|
var toAdd = [];
|
|
var i = 0;
|
|
var check = function() {
|
|
return r.count ? all.length < r.count : +d <= r.until;
|
|
};
|
|
while ((toAdd = Rec.getRecurring([ Rec.getMonthId(d) ], [ ev ])) && check() && i < r.count * 12) {
|
|
Array.prototype.push.apply(all, toAdd.map((function(_ev) {
|
|
return _ev.start;
|
|
})));
|
|
d.setMonth(d.getMonth() + 1);
|
|
i++;
|
|
}
|
|
return all;
|
|
};
|
|
Rec.diffDate = function(oldTime, newTime) {
|
|
var n = new Date(newTime);
|
|
var o = new Date(oldTime);
|
|
var d = 0;
|
|
var mult = n < o ? -1 : 1;
|
|
while (n.toLocaleDateString() !== o.toLocaleDateString() || mult >= 1e4) {
|
|
n.setDate(n.getDate() - mult);
|
|
d++;
|
|
}
|
|
d = mult * d;
|
|
n = new Date(newTime);
|
|
var h = n.getHours() - o.getHours();
|
|
var m = n.getMinutes() - o.getMinutes();
|
|
return {
|
|
d,
|
|
h,
|
|
m
|
|
};
|
|
};
|
|
var sortUpdate = function(obj) {
|
|
return Object.keys(obj).sort((function(d1, d2) {
|
|
return Number(d1) - Number(d2);
|
|
}));
|
|
};
|
|
Rec.applyUpdates = function(events) {
|
|
events.forEach((function(ev) {
|
|
ev.raw = {
|
|
start: ev.start,
|
|
end: ev.end
|
|
};
|
|
if (!ev.recUpdate) {
|
|
return;
|
|
}
|
|
var from = ev.recUpdate.from || {};
|
|
var one = ev.recUpdate.one || {};
|
|
var s = ev.start;
|
|
var nextRules = getNextRules(ev).filter((function(r) {
|
|
return r._start > s;
|
|
}));
|
|
var nextRule = nextRules.shift();
|
|
var applyDiff = function(obj, k) {
|
|
var diff = obj[k];
|
|
var d = new Date(ev.raw[k]);
|
|
d.setDate(d.getDate() + diff.d);
|
|
d.setHours(d.getHours() + diff.h);
|
|
d.setMinutes(d.getMinutes() + diff.m);
|
|
ev[k] = +d;
|
|
};
|
|
sortUpdate(from).forEach((function(d) {
|
|
if (s < Number(d)) {
|
|
return;
|
|
}
|
|
Object.keys(from[d]).forEach((function(k) {
|
|
if (k === "start" || k === "end") {
|
|
return void applyDiff(from[d], k);
|
|
}
|
|
if (k === "recurrenceRule" && !from[d][k]) {
|
|
return;
|
|
}
|
|
ev[k] = from[d][k];
|
|
}));
|
|
}));
|
|
Object.keys(one[s] || {}).forEach((function(k) {
|
|
if (k === "start" || k === "end") {
|
|
return void applyDiff(one[s], k);
|
|
}
|
|
if (k === "recurrenceRule" && !one[s][k]) {
|
|
return;
|
|
}
|
|
ev[k] = one[s][k];
|
|
}));
|
|
if (ev.deleted) {
|
|
Object.keys(ev).forEach((function(k) {
|
|
delete ev[k];
|
|
}));
|
|
}
|
|
if (nextRule && ev.recurrenceRule) {
|
|
ev.recurrenceRule._next = nextRule._start - 1;
|
|
}
|
|
if (ev.reminders) {
|
|
ev.raw.reminders = ev.reminders;
|
|
}
|
|
}));
|
|
return events;
|
|
};
|
|
return Rec;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil());
|
|
}
|
|
})();
|
|
})(recurrence);
|
|
return recurrence.exports;
|
|
}
|
|
var flatpickr$1 = {
|
|
exports: {}
|
|
};
|
|
/* flatpickr v4.6.9,, @license MIT */ var flatpickr = flatpickr$1.exports;
|
|
var hasRequiredFlatpickr;
|
|
function requireFlatpickr() {
|
|
if (hasRequiredFlatpickr) return flatpickr$1.exports;
|
|
hasRequiredFlatpickr = 1;
|
|
(function(module, exports) {
|
|
!function(e, t) {
|
|
module.exports = t();
|
|
}(flatpickr, (function() {
|
|
var e = function() {
|
|
return (e = Object.assign || function(e) {
|
|
for (var t, n = 1, a = arguments.length; n < a; n++) for (var i in t = arguments[n]) Object.prototype.hasOwnProperty.call(t, i) && (e[i] = t[i]);
|
|
return e;
|
|
}).apply(this, arguments);
|
|
};
|
|
function t() {
|
|
for (var e = 0, t = 0, n = arguments.length; t < n; t++) e += arguments[t].length;
|
|
var a = Array(e), i = 0;
|
|
for (t = 0; t < n; t++) for (var o = arguments[t], r = 0, l = o.length; r < l; r++,
|
|
i++) a[i] = o[r];
|
|
return a;
|
|
}
|
|
var n = [ "onChange", "onClose", "onDayCreate", "onDestroy", "onKeyDown", "onMonthChange", "onOpen", "onParseConfig", "onReady", "onValueUpdate", "onYearChange", "onPreCalendarPosition" ], a = {
|
|
_disable: [],
|
|
allowInput: !1,
|
|
allowInvalidPreload: !1,
|
|
altFormat: "F j, Y",
|
|
altInput: !1,
|
|
altInputClass: "form-control input",
|
|
animate: "object" == typeof window && -1 === window.navigator.userAgent.indexOf("MSIE"),
|
|
ariaDateFormat: "F j, Y",
|
|
autoFillDefaultTime: !0,
|
|
clickOpens: !0,
|
|
closeOnSelect: !0,
|
|
conjunction: ", ",
|
|
dateFormat: "Y-m-d",
|
|
defaultHour: 12,
|
|
defaultMinute: 0,
|
|
defaultSeconds: 0,
|
|
disable: [],
|
|
disableMobile: !1,
|
|
enableSeconds: !1,
|
|
enableTime: !1,
|
|
errorHandler: function(e) {
|
|
return "undefined" != typeof console && console.warn(e);
|
|
},
|
|
getWeek: function(e) {
|
|
var t = new Date(e.getTime());
|
|
t.setHours(0, 0, 0, 0), t.setDate(t.getDate() + 3 - (t.getDay() + 6) % 7);
|
|
var n = new Date(t.getFullYear(), 0, 4);
|
|
return 1 + Math.round(((t.getTime() - n.getTime()) / 864e5 - 3 + (n.getDay() + 6) % 7) / 7);
|
|
},
|
|
hourIncrement: 1,
|
|
ignoredFocusElements: [],
|
|
inline: !1,
|
|
locale: "default",
|
|
minuteIncrement: 5,
|
|
mode: "single",
|
|
monthSelectorType: "dropdown",
|
|
nextArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M13.207 8.472l-7.854 7.854-0.707-0.707 7.146-7.146-7.146-7.148 0.707-0.707 7.854 7.854z' /></svg>",
|
|
noCalendar: !1,
|
|
now: new Date,
|
|
onChange: [],
|
|
onClose: [],
|
|
onDayCreate: [],
|
|
onDestroy: [],
|
|
onKeyDown: [],
|
|
onMonthChange: [],
|
|
onOpen: [],
|
|
onParseConfig: [],
|
|
onReady: [],
|
|
onValueUpdate: [],
|
|
onYearChange: [],
|
|
onPreCalendarPosition: [],
|
|
plugins: [],
|
|
position: "auto",
|
|
positionElement: void 0,
|
|
prevArrow: "<svg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' viewBox='0 0 17 17'><g></g><path d='M5.207 8.471l7.146 7.147-0.707 0.707-7.853-7.854 7.854-7.853 0.707 0.707-7.147 7.146z' /></svg>",
|
|
shorthandCurrentMonth: !1,
|
|
showMonths: 1,
|
|
static: !1,
|
|
time_24hr: !1,
|
|
weekNumbers: !1,
|
|
wrap: !1
|
|
}, i = {
|
|
weekdays: {
|
|
shorthand: [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ],
|
|
longhand: [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ]
|
|
},
|
|
months: {
|
|
shorthand: [ "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ],
|
|
longhand: [ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" ]
|
|
},
|
|
daysInMonth: [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ],
|
|
firstDayOfWeek: 0,
|
|
ordinal: function(e) {
|
|
var t = e % 100;
|
|
if (t > 3 && t < 21) return "th";
|
|
switch (t % 10) {
|
|
case 1:
|
|
return "st";
|
|
|
|
case 2:
|
|
return "nd";
|
|
|
|
case 3:
|
|
return "rd";
|
|
|
|
default:
|
|
return "th";
|
|
}
|
|
},
|
|
rangeSeparator: " to ",
|
|
weekAbbreviation: "Wk",
|
|
scrollTitle: "Scroll to increment",
|
|
toggleTitle: "Click to toggle",
|
|
amPM: [ "AM", "PM" ],
|
|
yearAriaLabel: "Year",
|
|
monthAriaLabel: "Month",
|
|
hourAriaLabel: "Hour",
|
|
minuteAriaLabel: "Minute",
|
|
time_24hr: !1
|
|
}, o = function(e, t) {
|
|
return void 0 === t && (t = 2), ("000" + e).slice(-1 * t);
|
|
}, r = function(e) {
|
|
return !0 === e ? 1 : 0;
|
|
};
|
|
function l(e, t) {
|
|
var n;
|
|
return function() {
|
|
var a = this;
|
|
clearTimeout(n), n = setTimeout((function() {
|
|
return e.apply(a, arguments);
|
|
}), t);
|
|
};
|
|
}
|
|
var c = function(e) {
|
|
return e instanceof Array ? e : [ e ];
|
|
};
|
|
function d(e, t, n) {
|
|
if (!0 === n) return e.classList.add(t);
|
|
e.classList.remove(t);
|
|
}
|
|
function s(e, t, n) {
|
|
var a = window.document.createElement(e);
|
|
return t = t || "", n = n || "", a.className = t, void 0 !== n && (a.textContent = n),
|
|
a;
|
|
}
|
|
function u(e) {
|
|
for (;e.firstChild; ) e.removeChild(e.firstChild);
|
|
}
|
|
function f(e, t) {
|
|
return t(e) ? e : e.parentNode ? f(e.parentNode, t) : void 0;
|
|
}
|
|
function m(e, t) {
|
|
var n = s("div", "numInputWrapper"), a = s("input", "numInput " + e), i = s("span", "arrowUp"), o = s("span", "arrowDown");
|
|
if (-1 === navigator.userAgent.indexOf("MSIE 9.0") ? a.type = "number" : (a.type = "text",
|
|
a.pattern = "\\d*"), void 0 !== t) for (var r in t) a.setAttribute(r, t[r]);
|
|
return n.appendChild(a), n.appendChild(i), n.appendChild(o), n;
|
|
}
|
|
function g(e) {
|
|
try {
|
|
return "function" == typeof e.composedPath ? e.composedPath()[0] : e.target;
|
|
} catch (t) {
|
|
return e.target;
|
|
}
|
|
}
|
|
var p = function() {}, h = function(e, t, n) {
|
|
return n.months[t ? "shorthand" : "longhand"][e];
|
|
}, v = {
|
|
D: p,
|
|
F: function(e, t, n) {
|
|
e.setMonth(n.months.longhand.indexOf(t));
|
|
},
|
|
G: function(e, t) {
|
|
e.setHours(parseFloat(t));
|
|
},
|
|
H: function(e, t) {
|
|
e.setHours(parseFloat(t));
|
|
},
|
|
J: function(e, t) {
|
|
e.setDate(parseFloat(t));
|
|
},
|
|
K: function(e, t, n) {
|
|
e.setHours(e.getHours() % 12 + 12 * r(new RegExp(n.amPM[1], "i").test(t)));
|
|
},
|
|
M: function(e, t, n) {
|
|
e.setMonth(n.months.shorthand.indexOf(t));
|
|
},
|
|
S: function(e, t) {
|
|
e.setSeconds(parseFloat(t));
|
|
},
|
|
U: function(e, t) {
|
|
return new Date(1e3 * parseFloat(t));
|
|
},
|
|
W: function(e, t, n) {
|
|
var a = parseInt(t), i = new Date(e.getFullYear(), 0, 2 + 7 * (a - 1), 0, 0, 0, 0);
|
|
return i.setDate(i.getDate() - i.getDay() + n.firstDayOfWeek), i;
|
|
},
|
|
Y: function(e, t) {
|
|
e.setFullYear(parseFloat(t));
|
|
},
|
|
Z: function(e, t) {
|
|
return new Date(t);
|
|
},
|
|
d: function(e, t) {
|
|
e.setDate(parseFloat(t));
|
|
},
|
|
h: function(e, t) {
|
|
e.setHours(parseFloat(t));
|
|
},
|
|
i: function(e, t) {
|
|
e.setMinutes(parseFloat(t));
|
|
},
|
|
j: function(e, t) {
|
|
e.setDate(parseFloat(t));
|
|
},
|
|
l: p,
|
|
m: function(e, t) {
|
|
e.setMonth(parseFloat(t) - 1);
|
|
},
|
|
n: function(e, t) {
|
|
e.setMonth(parseFloat(t) - 1);
|
|
},
|
|
s: function(e, t) {
|
|
e.setSeconds(parseFloat(t));
|
|
},
|
|
u: function(e, t) {
|
|
return new Date(parseFloat(t));
|
|
},
|
|
w: p,
|
|
y: function(e, t) {
|
|
e.setFullYear(2e3 + parseFloat(t));
|
|
}
|
|
}, D = {
|
|
D: "(\\w+)",
|
|
F: "(\\w+)",
|
|
G: "(\\d\\d|\\d)",
|
|
H: "(\\d\\d|\\d)",
|
|
J: "(\\d\\d|\\d)\\w+",
|
|
K: "",
|
|
M: "(\\w+)",
|
|
S: "(\\d\\d|\\d)",
|
|
U: "(.+)",
|
|
W: "(\\d\\d|\\d)",
|
|
Y: "(\\d{4})",
|
|
Z: "(.+)",
|
|
d: "(\\d\\d|\\d)",
|
|
h: "(\\d\\d|\\d)",
|
|
i: "(\\d\\d|\\d)",
|
|
j: "(\\d\\d|\\d)",
|
|
l: "(\\w+)",
|
|
m: "(\\d\\d|\\d)",
|
|
n: "(\\d\\d|\\d)",
|
|
s: "(\\d\\d|\\d)",
|
|
u: "(.+)",
|
|
w: "(\\d\\d|\\d)",
|
|
y: "(\\d{2})"
|
|
}, w = {
|
|
Z: function(e) {
|
|
return e.toISOString();
|
|
},
|
|
D: function(e, t, n) {
|
|
return t.weekdays.shorthand[w.w(e, t, n)];
|
|
},
|
|
F: function(e, t, n) {
|
|
return h(w.n(e, t, n) - 1, !1, t);
|
|
},
|
|
G: function(e, t, n) {
|
|
return o(w.h(e, t, n));
|
|
},
|
|
H: function(e) {
|
|
return o(e.getHours());
|
|
},
|
|
J: function(e, t) {
|
|
return void 0 !== t.ordinal ? e.getDate() + t.ordinal(e.getDate()) : e.getDate();
|
|
},
|
|
K: function(e, t) {
|
|
return t.amPM[r(e.getHours() > 11)];
|
|
},
|
|
M: function(e, t) {
|
|
return h(e.getMonth(), !0, t);
|
|
},
|
|
S: function(e) {
|
|
return o(e.getSeconds());
|
|
},
|
|
U: function(e) {
|
|
return e.getTime() / 1e3;
|
|
},
|
|
W: function(e, t, n) {
|
|
return n.getWeek(e);
|
|
},
|
|
Y: function(e) {
|
|
return o(e.getFullYear(), 4);
|
|
},
|
|
d: function(e) {
|
|
return o(e.getDate());
|
|
},
|
|
h: function(e) {
|
|
return e.getHours() % 12 ? e.getHours() % 12 : 12;
|
|
},
|
|
i: function(e) {
|
|
return o(e.getMinutes());
|
|
},
|
|
j: function(e) {
|
|
return e.getDate();
|
|
},
|
|
l: function(e, t) {
|
|
return t.weekdays.longhand[e.getDay()];
|
|
},
|
|
m: function(e) {
|
|
return o(e.getMonth() + 1);
|
|
},
|
|
n: function(e) {
|
|
return e.getMonth() + 1;
|
|
},
|
|
s: function(e) {
|
|
return e.getSeconds();
|
|
},
|
|
u: function(e) {
|
|
return e.getTime();
|
|
},
|
|
w: function(e) {
|
|
return e.getDay();
|
|
},
|
|
y: function(e) {
|
|
return String(e.getFullYear()).substring(2);
|
|
}
|
|
}, b = function(e) {
|
|
var t = e.config, n = void 0 === t ? a : t, o = e.l10n, r = void 0 === o ? i : o, l = e.isMobile, c = void 0 !== l && l;
|
|
return function(e, t, a) {
|
|
var i = a || r;
|
|
return void 0 === n.formatDate || c ? t.split("").map((function(t, a, o) {
|
|
return w[t] && "\\" !== o[a - 1] ? w[t](e, i, n) : "\\" !== t ? t : "";
|
|
})).join("") : n.formatDate(e, t, i);
|
|
};
|
|
}, C = function(e) {
|
|
var t = e.config, n = void 0 === t ? a : t, o = e.l10n, r = void 0 === o ? i : o;
|
|
return function(e, t, i, o) {
|
|
if (0 === e || e) {
|
|
var l, c = o || r, d = e;
|
|
if (e instanceof Date) l = new Date(e.getTime()); else if ("string" != typeof e && void 0 !== e.toFixed) l = new Date(e); else if ("string" == typeof e) {
|
|
var s = t || (n || a).dateFormat, u = String(e).trim();
|
|
if ("today" === u) l = new Date, i = !0; else if (/Z$/.test(u) || /GMT$/.test(u)) l = new Date(e); else if (n && n.parseDate) l = n.parseDate(e, s); else {
|
|
l = n && n.noCalendar ? new Date((new Date).setHours(0, 0, 0, 0)) : new Date((new Date).getFullYear(), 0, 1, 0, 0, 0, 0);
|
|
for (var f = void 0, m = [], g = 0, p = 0, h = ""; g < s.length; g++) {
|
|
var w = s[g], b = "\\" === w, C = "\\" === s[g - 1] || b;
|
|
if (D[w] && !C) {
|
|
h += D[w];
|
|
var M = new RegExp(h).exec(e);
|
|
M && (f = !0) && m["Y" !== w ? "push" : "unshift"]({
|
|
fn: v[w],
|
|
val: M[++p]
|
|
});
|
|
} else b || (h += ".");
|
|
m.forEach((function(e) {
|
|
var t = e.fn, n = e.val;
|
|
return l = t(l, n, c) || l;
|
|
}));
|
|
}
|
|
l = f ? l : void 0;
|
|
}
|
|
}
|
|
if (l instanceof Date && !isNaN(l.getTime())) return !0 === i && l.setHours(0, 0, 0, 0),
|
|
l;
|
|
n.errorHandler(new Error("Invalid date provided: " + d));
|
|
}
|
|
};
|
|
};
|
|
function M(e, t, n) {
|
|
return void 0 === n && (n = !0), !1 !== n ? new Date(e.getTime()).setHours(0, 0, 0, 0) - new Date(t.getTime()).setHours(0, 0, 0, 0) : e.getTime() - t.getTime();
|
|
}
|
|
var y = 864e5;
|
|
function x(e) {
|
|
var t = e.defaultHour, n = e.defaultMinute, a = e.defaultSeconds;
|
|
if (void 0 !== e.minDate) {
|
|
var i = e.minDate.getHours(), o = e.minDate.getMinutes(), r = e.minDate.getSeconds();
|
|
t < i && (t = i), t === i && n < o && (n = o), t === i && n === o && a < r && (a = e.minDate.getSeconds());
|
|
}
|
|
if (void 0 !== e.maxDate) {
|
|
var l = e.maxDate.getHours(), c = e.maxDate.getMinutes();
|
|
(t = Math.min(t, l)) === l && (n = Math.min(c, n)), t === l && n === c && (a = e.maxDate.getSeconds());
|
|
}
|
|
return {
|
|
hours: t,
|
|
minutes: n,
|
|
seconds: a
|
|
};
|
|
}
|
|
"function" != typeof Object.assign && (Object.assign = function(e) {
|
|
for (var t = [], n = 1; n < arguments.length; n++) t[n - 1] = arguments[n];
|
|
if (!e) throw TypeError("Cannot convert undefined or null to object");
|
|
for (var a = function(t) {
|
|
t && Object.keys(t).forEach((function(n) {
|
|
return e[n] = t[n];
|
|
}));
|
|
}, i = 0, o = t; i < o.length; i++) {
|
|
var r = o[i];
|
|
a(r);
|
|
}
|
|
return e;
|
|
});
|
|
function E(p, v) {
|
|
var w = {
|
|
config: e(e({}, a), T.defaultConfig),
|
|
l10n: i
|
|
};
|
|
function E(e) {
|
|
return e.bind(w);
|
|
}
|
|
function k() {
|
|
var e = w.config;
|
|
!1 === e.weekNumbers && 1 === e.showMonths || !0 !== e.noCalendar && window.requestAnimationFrame((function() {
|
|
if (void 0 !== w.calendarContainer && (w.calendarContainer.style.visibility = "hidden",
|
|
w.calendarContainer.style.display = "block"), void 0 !== w.daysContainer) {
|
|
var t = (w.days.offsetWidth + 1) * e.showMonths;
|
|
w.daysContainer.style.width = t + "px", w.calendarContainer.style.width = t + (void 0 !== w.weekWrapper ? w.weekWrapper.offsetWidth : 0) + "px",
|
|
w.calendarContainer.style.removeProperty("visibility"), w.calendarContainer.style.removeProperty("display");
|
|
}
|
|
}));
|
|
}
|
|
function I(e) {
|
|
if (0 === w.selectedDates.length) {
|
|
var t = void 0 === w.config.minDate || M(new Date, w.config.minDate) >= 0 ? new Date : new Date(w.config.minDate.getTime()), n = x(w.config);
|
|
t.setHours(n.hours, n.minutes, n.seconds, t.getMilliseconds()), w.selectedDates = [ t ],
|
|
w.latestSelectedDateObj = t;
|
|
}
|
|
void 0 !== e && "blur" !== e.type && function(e) {
|
|
e.preventDefault();
|
|
var t = "keydown" === e.type, n = g(e), a = n;
|
|
void 0 !== w.amPM && n === w.amPM && (w.amPM.textContent = w.l10n.amPM[r(w.amPM.textContent === w.l10n.amPM[0])]);
|
|
var i = parseFloat(a.getAttribute("min")), l = parseFloat(a.getAttribute("max")), c = parseFloat(a.getAttribute("step")), d = parseInt(a.value, 10), s = e.delta || (t ? 38 === e.which ? 1 : -1 : 0), u = d + c * s;
|
|
if (void 0 !== a.value && 2 === a.value.length) {
|
|
var f = a === w.hourElement, m = a === w.minuteElement;
|
|
u < i ? (u = l + u + r(!f) + (r(f) && r(!w.amPM)), m && j(void 0, -1, w.hourElement)) : u > l && (u = a === w.hourElement ? u - l - r(!w.amPM) : i,
|
|
m && j(void 0, 1, w.hourElement)), w.amPM && f && (1 === c ? u + d === 23 : Math.abs(u - d) > c) && (w.amPM.textContent = w.l10n.amPM[r(w.amPM.textContent === w.l10n.amPM[0])]),
|
|
a.value = o(u);
|
|
}
|
|
}(e);
|
|
var a = w._input.value;
|
|
S(), be(), w._input.value !== a && w._debouncedChange();
|
|
}
|
|
function S() {
|
|
if (void 0 !== w.hourElement && void 0 !== w.minuteElement) {
|
|
var e, t, n = (parseInt(w.hourElement.value.slice(-2), 10) || 0) % 24, a = (parseInt(w.minuteElement.value, 10) || 0) % 60, i = void 0 !== w.secondElement ? (parseInt(w.secondElement.value, 10) || 0) % 60 : 0;
|
|
void 0 !== w.amPM && (e = n, t = w.amPM.textContent, n = e % 12 + 12 * r(t === w.l10n.amPM[1]));
|
|
var o = void 0 !== w.config.minTime || w.config.minDate && w.minDateHasTime && w.latestSelectedDateObj && 0 === M(w.latestSelectedDateObj, w.config.minDate, !0);
|
|
if (void 0 !== w.config.maxTime || w.config.maxDate && w.maxDateHasTime && w.latestSelectedDateObj && 0 === M(w.latestSelectedDateObj, w.config.maxDate, !0)) {
|
|
var l = void 0 !== w.config.maxTime ? w.config.maxTime : w.config.maxDate;
|
|
(n = Math.min(n, l.getHours())) === l.getHours() && (a = Math.min(a, l.getMinutes())),
|
|
a === l.getMinutes() && (i = Math.min(i, l.getSeconds()));
|
|
}
|
|
if (o) {
|
|
var c = void 0 !== w.config.minTime ? w.config.minTime : w.config.minDate;
|
|
(n = Math.max(n, c.getHours())) === c.getHours() && a < c.getMinutes() && (a = c.getMinutes()),
|
|
a === c.getMinutes() && (i = Math.max(i, c.getSeconds()));
|
|
}
|
|
O(n, a, i);
|
|
}
|
|
}
|
|
function _(e) {
|
|
var t = e || w.latestSelectedDateObj;
|
|
t && O(t.getHours(), t.getMinutes(), t.getSeconds());
|
|
}
|
|
function O(e, t, n) {
|
|
void 0 !== w.latestSelectedDateObj && w.latestSelectedDateObj.setHours(e % 24, t, n || 0, 0),
|
|
w.hourElement && w.minuteElement && !w.isMobile && (w.hourElement.value = o(w.config.time_24hr ? e : (12 + e) % 12 + 12 * r(e % 12 == 0)),
|
|
w.minuteElement.value = o(t), void 0 !== w.amPM && (w.amPM.textContent = w.l10n.amPM[r(e >= 12)]),
|
|
void 0 !== w.secondElement && (w.secondElement.value = o(n)));
|
|
}
|
|
function F(e) {
|
|
var t = g(e), n = parseInt(t.value) + (e.delta || 0);
|
|
(n / 1e3 > 1 || "Enter" === e.key && !/[^\d]/.test(n.toString())) && Q(n);
|
|
}
|
|
function A(e, t, n, a) {
|
|
return t instanceof Array ? t.forEach((function(t) {
|
|
return A(e, t, n, a);
|
|
})) : e instanceof Array ? e.forEach((function(e) {
|
|
return A(e, t, n, a);
|
|
})) : (e.addEventListener(t, n, a), void w._handlers.push({
|
|
remove: function() {
|
|
return e.removeEventListener(t, n);
|
|
}
|
|
}));
|
|
}
|
|
function N() {
|
|
pe("onChange");
|
|
}
|
|
function P(e, t) {
|
|
var n = void 0 !== e ? w.parseDate(e) : w.latestSelectedDateObj || (w.config.minDate && w.config.minDate > w.now ? w.config.minDate : w.config.maxDate && w.config.maxDate < w.now ? w.config.maxDate : w.now), a = w.currentYear, i = w.currentMonth;
|
|
try {
|
|
void 0 !== n && (w.currentYear = n.getFullYear(), w.currentMonth = n.getMonth());
|
|
} catch (e) {
|
|
e.message = "Invalid date supplied: " + n, w.config.errorHandler(e);
|
|
}
|
|
t && w.currentYear !== a && (pe("onYearChange"), K()), !t || w.currentYear === a && w.currentMonth === i || pe("onMonthChange"),
|
|
w.redraw();
|
|
}
|
|
function Y(e) {
|
|
var t = g(e);
|
|
~t.className.indexOf("arrow") && j(e, t.classList.contains("arrowUp") ? 1 : -1);
|
|
}
|
|
function j(e, t, n) {
|
|
var a = e && g(e), i = n || a && a.parentNode && a.parentNode.firstChild, o = he("increment");
|
|
o.delta = t, i && i.dispatchEvent(o);
|
|
}
|
|
function H(e, t, n, a) {
|
|
var i = X(t, !0), o = s("span", "flatpickr-day " + e, t.getDate().toString());
|
|
return o.dateObj = t, o.$i = a, o.setAttribute("aria-label", w.formatDate(t, w.config.ariaDateFormat)),
|
|
-1 === e.indexOf("hidden") && 0 === M(t, w.now) && (w.todayDateElem = o, o.classList.add("today"),
|
|
o.setAttribute("aria-current", "date")), i ? (o.tabIndex = -1, ve(t) && (o.classList.add("selected"),
|
|
w.selectedDateElem = o, "range" === w.config.mode && (d(o, "startRange", w.selectedDates[0] && 0 === M(t, w.selectedDates[0], !0)),
|
|
d(o, "endRange", w.selectedDates[1] && 0 === M(t, w.selectedDates[1], !0)), "nextMonthDay" === e && o.classList.add("inRange")))) : o.classList.add("flatpickr-disabled"),
|
|
"range" === w.config.mode && function(e) {
|
|
return !("range" !== w.config.mode || w.selectedDates.length < 2) && (M(e, w.selectedDates[0]) >= 0 && M(e, w.selectedDates[1]) <= 0);
|
|
}(t) && !ve(t) && o.classList.add("inRange"), w.weekNumbers && 1 === w.config.showMonths && "prevMonthDay" !== e && n % 7 == 1 && w.weekNumbers.insertAdjacentHTML("beforeend", "<span class='flatpickr-day'>" + w.config.getWeek(t) + "</span>"),
|
|
pe("onDayCreate", o), o;
|
|
}
|
|
function L(e) {
|
|
e.focus(), "range" === w.config.mode && ae(e);
|
|
}
|
|
function W(e) {
|
|
for (var t = e > 0 ? 0 : w.config.showMonths - 1, n = e > 0 ? w.config.showMonths : -1, a = t; a != n; a += e) for (var i = w.daysContainer.children[a], o = e > 0 ? 0 : i.children.length - 1, r = e > 0 ? i.children.length : -1, l = o; l != r; l += e) {
|
|
var c = i.children[l];
|
|
if (-1 === c.className.indexOf("hidden") && X(c.dateObj)) return c;
|
|
}
|
|
}
|
|
function R(e, t) {
|
|
var n = ee(document.activeElement || document.body), a = void 0 !== e ? e : n ? document.activeElement : void 0 !== w.selectedDateElem && ee(w.selectedDateElem) ? w.selectedDateElem : void 0 !== w.todayDateElem && ee(w.todayDateElem) ? w.todayDateElem : W(t > 0 ? 1 : -1);
|
|
void 0 === a ? w._input.focus() : n ? function(e, t) {
|
|
for (var n = -1 === e.className.indexOf("Month") ? e.dateObj.getMonth() : w.currentMonth, a = t > 0 ? w.config.showMonths : -1, i = t > 0 ? 1 : -1, o = n - w.currentMonth; o != a; o += i) for (var r = w.daysContainer.children[o], l = n - w.currentMonth === o ? e.$i + t : t < 0 ? r.children.length - 1 : 0, c = r.children.length, d = l; d >= 0 && d < c && d != (t > 0 ? c : -1); d += i) {
|
|
var s = r.children[d];
|
|
if (-1 === s.className.indexOf("hidden") && X(s.dateObj) && Math.abs(e.$i - d) >= Math.abs(t)) return L(s);
|
|
}
|
|
w.changeMonth(i), R(W(i), 0);
|
|
}(a, t) : L(a);
|
|
}
|
|
function B(e, t) {
|
|
for (var n = (new Date(e, t, 1).getDay() - w.l10n.firstDayOfWeek + 7) % 7, a = w.utils.getDaysInMonth((t - 1 + 12) % 12, e), i = w.utils.getDaysInMonth(t, e), o = window.document.createDocumentFragment(), r = w.config.showMonths > 1, l = r ? "prevMonthDay hidden" : "prevMonthDay", c = r ? "nextMonthDay hidden" : "nextMonthDay", d = a + 1 - n, u = 0; d <= a; d++,
|
|
u++) o.appendChild(H(l, new Date(e, t - 1, d), d, u));
|
|
for (d = 1; d <= i; d++, u++) o.appendChild(H("", new Date(e, t, d), d, u));
|
|
for (var f = i + 1; f <= 42 - n && (1 === w.config.showMonths || u % 7 != 0); f++,
|
|
u++) o.appendChild(H(c, new Date(e, t + 1, f % i), f, u));
|
|
var m = s("div", "dayContainer");
|
|
return m.appendChild(o), m;
|
|
}
|
|
function J() {
|
|
if (void 0 !== w.daysContainer) {
|
|
u(w.daysContainer), w.weekNumbers && u(w.weekNumbers);
|
|
for (var e = document.createDocumentFragment(), t = 0; t < w.config.showMonths; t++) {
|
|
var n = new Date(w.currentYear, w.currentMonth, 1);
|
|
n.setMonth(w.currentMonth + t), e.appendChild(B(n.getFullYear(), n.getMonth()));
|
|
}
|
|
w.daysContainer.appendChild(e), w.days = w.daysContainer.firstChild, "range" === w.config.mode && 1 === w.selectedDates.length && ae();
|
|
}
|
|
}
|
|
function K() {
|
|
if (!(w.config.showMonths > 1 || "dropdown" !== w.config.monthSelectorType)) {
|
|
var e = function(e) {
|
|
return !(void 0 !== w.config.minDate && w.currentYear === w.config.minDate.getFullYear() && e < w.config.minDate.getMonth()) && !(void 0 !== w.config.maxDate && w.currentYear === w.config.maxDate.getFullYear() && e > w.config.maxDate.getMonth());
|
|
};
|
|
w.monthsDropdownContainer.tabIndex = -1, w.monthsDropdownContainer.innerHTML = "";
|
|
for (var t = 0; t < 12; t++) if (e(t)) {
|
|
var n = s("option", "flatpickr-monthDropdown-month");
|
|
n.value = new Date(w.currentYear, t).getMonth().toString(), n.textContent = h(t, w.config.shorthandCurrentMonth, w.l10n),
|
|
n.tabIndex = -1, w.currentMonth === t && (n.selected = !0), w.monthsDropdownContainer.appendChild(n);
|
|
}
|
|
}
|
|
}
|
|
function U() {
|
|
var e, t = s("div", "flatpickr-month"), n = window.document.createDocumentFragment();
|
|
w.config.showMonths > 1 || "static" === w.config.monthSelectorType ? e = s("span", "cur-month") : (w.monthsDropdownContainer = s("select", "flatpickr-monthDropdown-months"),
|
|
w.monthsDropdownContainer.setAttribute("aria-label", w.l10n.monthAriaLabel), A(w.monthsDropdownContainer, "change", (function(e) {
|
|
var t = g(e), n = parseInt(t.value, 10);
|
|
w.changeMonth(n - w.currentMonth), pe("onMonthChange");
|
|
})), K(), e = w.monthsDropdownContainer);
|
|
var a = m("cur-year", {
|
|
tabindex: "-1"
|
|
}), i = a.getElementsByTagName("input")[0];
|
|
i.setAttribute("aria-label", w.l10n.yearAriaLabel), w.config.minDate && i.setAttribute("min", w.config.minDate.getFullYear().toString()),
|
|
w.config.maxDate && (i.setAttribute("max", w.config.maxDate.getFullYear().toString()),
|
|
i.disabled = !!w.config.minDate && w.config.minDate.getFullYear() === w.config.maxDate.getFullYear());
|
|
var o = s("div", "flatpickr-current-month");
|
|
return o.appendChild(e), o.appendChild(a), n.appendChild(o), t.appendChild(n), {
|
|
container: t,
|
|
yearElement: i,
|
|
monthElement: e
|
|
};
|
|
}
|
|
function q() {
|
|
u(w.monthNav), w.monthNav.appendChild(w.prevMonthNav), w.config.showMonths && (w.yearElements = [],
|
|
w.monthElements = []);
|
|
for (var e = w.config.showMonths; e--; ) {
|
|
var t = U();
|
|
w.yearElements.push(t.yearElement), w.monthElements.push(t.monthElement), w.monthNav.appendChild(t.container);
|
|
}
|
|
w.monthNav.appendChild(w.nextMonthNav);
|
|
}
|
|
function $() {
|
|
w.weekdayContainer ? u(w.weekdayContainer) : w.weekdayContainer = s("div", "flatpickr-weekdays");
|
|
for (var e = w.config.showMonths; e--; ) {
|
|
var t = s("div", "flatpickr-weekdaycontainer");
|
|
w.weekdayContainer.appendChild(t);
|
|
}
|
|
return z(), w.weekdayContainer;
|
|
}
|
|
function z() {
|
|
if (w.weekdayContainer) {
|
|
var e = w.l10n.firstDayOfWeek, n = t(w.l10n.weekdays.shorthand);
|
|
e > 0 && e < n.length && (n = t(n.splice(e, n.length), n.splice(0, e)));
|
|
for (var a = w.config.showMonths; a--; ) w.weekdayContainer.children[a].innerHTML = "\n <span class='flatpickr-weekday'>\n " + n.join("</span><span class='flatpickr-weekday'>") + "\n </span>\n ";
|
|
}
|
|
}
|
|
function G(e, t) {
|
|
void 0 === t && (t = !0);
|
|
var n = t ? e : e - w.currentMonth;
|
|
n < 0 && !0 === w._hidePrevMonthArrow || n > 0 && !0 === w._hideNextMonthArrow || (w.currentMonth += n,
|
|
(w.currentMonth < 0 || w.currentMonth > 11) && (w.currentYear += w.currentMonth > 11 ? 1 : -1,
|
|
w.currentMonth = (w.currentMonth + 12) % 12, pe("onYearChange"), K()), J(), pe("onMonthChange"),
|
|
De());
|
|
}
|
|
function V(e) {
|
|
return !(!w.config.appendTo || !w.config.appendTo.contains(e)) || w.calendarContainer.contains(e);
|
|
}
|
|
function Z(e) {
|
|
if (w.isOpen && !w.config.inline) {
|
|
var t = g(e), n = V(t), a = t === w.input || t === w.altInput || w.element.contains(t) || e.path && e.path.indexOf && (~e.path.indexOf(w.input) || ~e.path.indexOf(w.altInput)), i = "blur" === e.type ? a && e.relatedTarget && !V(e.relatedTarget) : !a && !n && !V(e.relatedTarget), o = !w.config.ignoredFocusElements.some((function(e) {
|
|
return e.contains(t);
|
|
}));
|
|
i && o && (void 0 !== w.timeContainer && void 0 !== w.minuteElement && void 0 !== w.hourElement && "" !== w.input.value && void 0 !== w.input.value && I(),
|
|
w.close(), w.config && "range" === w.config.mode && 1 === w.selectedDates.length && (w.clear(!1),
|
|
w.redraw()));
|
|
}
|
|
}
|
|
function Q(e) {
|
|
if (!(!e || w.config.minDate && e < w.config.minDate.getFullYear() || w.config.maxDate && e > w.config.maxDate.getFullYear())) {
|
|
var t = e, n = w.currentYear !== t;
|
|
w.currentYear = t || w.currentYear, w.config.maxDate && w.currentYear === w.config.maxDate.getFullYear() ? w.currentMonth = Math.min(w.config.maxDate.getMonth(), w.currentMonth) : w.config.minDate && w.currentYear === w.config.minDate.getFullYear() && (w.currentMonth = Math.max(w.config.minDate.getMonth(), w.currentMonth)),
|
|
n && (w.redraw(), pe("onYearChange"), K());
|
|
}
|
|
}
|
|
function X(e, t) {
|
|
var n;
|
|
void 0 === t && (t = !0);
|
|
var a = w.parseDate(e, void 0, t);
|
|
if (w.config.minDate && a && M(a, w.config.minDate, void 0 !== t ? t : !w.minDateHasTime) < 0 || w.config.maxDate && a && M(a, w.config.maxDate, void 0 !== t ? t : !w.maxDateHasTime) > 0) return !1;
|
|
if (!w.config.enable && 0 === w.config.disable.length) return !0;
|
|
if (void 0 === a) return !1;
|
|
for (var i = !!w.config.enable, o = null !== (n = w.config.enable) && void 0 !== n ? n : w.config.disable, r = 0, l = void 0; r < o.length; r++) {
|
|
if ("function" == typeof (l = o[r]) && l(a)) return i;
|
|
if (l instanceof Date && void 0 !== a && l.getTime() === a.getTime()) return i;
|
|
if ("string" == typeof l) {
|
|
var c = w.parseDate(l, void 0, !0);
|
|
return c && c.getTime() === a.getTime() ? i : !i;
|
|
}
|
|
if ("object" == typeof l && void 0 !== a && l.from && l.to && a.getTime() >= l.from.getTime() && a.getTime() <= l.to.getTime()) return i;
|
|
}
|
|
return !i;
|
|
}
|
|
function ee(e) {
|
|
return void 0 !== w.daysContainer && (-1 === e.className.indexOf("hidden") && -1 === e.className.indexOf("flatpickr-disabled") && w.daysContainer.contains(e));
|
|
}
|
|
function te(e) {
|
|
!(e.target === w._input) || !(w.selectedDates.length > 0 || w._input.value.length > 0) || e.relatedTarget && V(e.relatedTarget) || w.setDate(w._input.value, !0, e.target === w.altInput ? w.config.altFormat : w.config.dateFormat);
|
|
}
|
|
function ne(e) {
|
|
var t = g(e), n = w.config.wrap ? p.contains(t) : t === w._input, a = w.config.allowInput, i = w.isOpen && (!a || !n), o = w.config.inline && n && !a;
|
|
if (13 === e.keyCode && n) {
|
|
if (a) return w.setDate(w._input.value, !0, t === w.altInput ? w.config.altFormat : w.config.dateFormat),
|
|
t.blur();
|
|
w.open();
|
|
} else if (V(t) || i || o) {
|
|
var r = !!w.timeContainer && w.timeContainer.contains(t);
|
|
switch (e.keyCode) {
|
|
case 13:
|
|
r ? (e.preventDefault(), I(), se()) : ue(e);
|
|
break;
|
|
|
|
case 27:
|
|
e.preventDefault(), se();
|
|
break;
|
|
|
|
case 8:
|
|
case 46:
|
|
n && !w.config.allowInput && (e.preventDefault(), w.clear());
|
|
break;
|
|
|
|
case 37:
|
|
case 39:
|
|
if (r || n) w.hourElement && w.hourElement.focus(); else if (e.preventDefault(),
|
|
void 0 !== w.daysContainer && (!1 === a || document.activeElement && ee(document.activeElement))) {
|
|
var l = 39 === e.keyCode ? 1 : -1;
|
|
e.ctrlKey ? (e.stopPropagation(), G(l), R(W(1), 0)) : R(void 0, l);
|
|
}
|
|
break;
|
|
|
|
case 38:
|
|
case 40:
|
|
e.preventDefault();
|
|
var c = 40 === e.keyCode ? 1 : -1;
|
|
w.daysContainer && void 0 !== t.$i || t === w.input || t === w.altInput ? e.ctrlKey ? (e.stopPropagation(),
|
|
Q(w.currentYear - c), R(W(1), 0)) : r || R(void 0, 7 * c) : t === w.currentYearElement ? Q(w.currentYear - c) : w.config.enableTime && (!r && w.hourElement && w.hourElement.focus(),
|
|
I(e), w._debouncedChange());
|
|
break;
|
|
|
|
case 9:
|
|
if (r) {
|
|
var d = [ w.hourElement, w.minuteElement, w.secondElement, w.amPM ].concat(w.pluginElements).filter((function(e) {
|
|
return e;
|
|
})), s = d.indexOf(t);
|
|
if (-1 !== s) {
|
|
var u = d[s + (e.shiftKey ? -1 : 1)];
|
|
e.preventDefault(), (u || w._input).focus();
|
|
}
|
|
} else !w.config.noCalendar && w.daysContainer && w.daysContainer.contains(t) && e.shiftKey && (e.preventDefault(),
|
|
w._input.focus());
|
|
}
|
|
}
|
|
if (void 0 !== w.amPM && t === w.amPM) switch (e.key) {
|
|
case w.l10n.amPM[0].charAt(0):
|
|
case w.l10n.amPM[0].charAt(0).toLowerCase():
|
|
w.amPM.textContent = w.l10n.amPM[0], S(), be();
|
|
break;
|
|
|
|
case w.l10n.amPM[1].charAt(0):
|
|
case w.l10n.amPM[1].charAt(0).toLowerCase():
|
|
w.amPM.textContent = w.l10n.amPM[1], S(), be();
|
|
}
|
|
(n || V(t)) && pe("onKeyDown", e);
|
|
}
|
|
function ae(e) {
|
|
if (1 === w.selectedDates.length && (!e || e.classList.contains("flatpickr-day") && !e.classList.contains("flatpickr-disabled"))) {
|
|
for (var t = e ? e.dateObj.getTime() : w.days.firstElementChild.dateObj.getTime(), n = w.parseDate(w.selectedDates[0], void 0, !0).getTime(), a = Math.min(t, w.selectedDates[0].getTime()), i = Math.max(t, w.selectedDates[0].getTime()), o = !1, r = 0, l = 0, c = a; c < i; c += y) X(new Date(c), !0) || (o = o || c > a && c < i,
|
|
c < n && (!r || c > r) ? r = c : c > n && (!l || c < l) && (l = c));
|
|
for (var d = 0; d < w.config.showMonths; d++) for (var s = w.daysContainer.children[d], u = function(a, i) {
|
|
var c, d, u, f = s.children[a], m = f.dateObj.getTime(), g = r > 0 && m < r || l > 0 && m > l;
|
|
return g ? (f.classList.add("notAllowed"), [ "inRange", "startRange", "endRange" ].forEach((function(e) {
|
|
f.classList.remove(e);
|
|
})), "continue") : o && !g ? "continue" : ([ "startRange", "inRange", "endRange", "notAllowed" ].forEach((function(e) {
|
|
f.classList.remove(e);
|
|
})), void (void 0 !== e && (e.classList.add(t <= w.selectedDates[0].getTime() ? "startRange" : "endRange"),
|
|
n < t && m === n ? f.classList.add("startRange") : n > t && m === n && f.classList.add("endRange"),
|
|
m >= r && (0 === l || m <= l) && (d = n, u = t, (c = m) > Math.min(d, u) && c < Math.max(d, u)) && f.classList.add("inRange"))));
|
|
}, f = 0, m = s.children.length; f < m; f++) u(f);
|
|
}
|
|
}
|
|
function ie() {
|
|
!w.isOpen || w.config.static || w.config.inline || ce();
|
|
}
|
|
function oe(e) {
|
|
return function(t) {
|
|
var n = w.config["_" + e + "Date"] = w.parseDate(t, w.config.dateFormat), a = w.config["_" + ("min" === e ? "max" : "min") + "Date"];
|
|
void 0 !== n && (w["min" === e ? "minDateHasTime" : "maxDateHasTime"] = n.getHours() > 0 || n.getMinutes() > 0 || n.getSeconds() > 0),
|
|
w.selectedDates && (w.selectedDates = w.selectedDates.filter((function(e) {
|
|
return X(e);
|
|
})), w.selectedDates.length || "min" !== e || _(n), be()), w.daysContainer && (de(),
|
|
void 0 !== n ? w.currentYearElement[e] = n.getFullYear().toString() : w.currentYearElement.removeAttribute(e),
|
|
w.currentYearElement.disabled = !!a && void 0 !== n && a.getFullYear() === n.getFullYear());
|
|
};
|
|
}
|
|
function re() {
|
|
return w.config.wrap ? p.querySelector("[data-input]") : p;
|
|
}
|
|
function le() {
|
|
"object" != typeof w.config.locale && void 0 === T.l10ns[w.config.locale] && w.config.errorHandler(new Error("flatpickr: invalid locale " + w.config.locale)),
|
|
w.l10n = e(e({}, T.l10ns.default), "object" == typeof w.config.locale ? w.config.locale : "default" !== w.config.locale ? T.l10ns[w.config.locale] : void 0),
|
|
D.K = "(" + w.l10n.amPM[0] + "|" + w.l10n.amPM[1] + "|" + w.l10n.amPM[0].toLowerCase() + "|" + w.l10n.amPM[1].toLowerCase() + ")",
|
|
void 0 === e(e({}, v), JSON.parse(JSON.stringify(p.dataset || {}))).time_24hr && void 0 === T.defaultConfig.time_24hr && (w.config.time_24hr = w.l10n.time_24hr),
|
|
w.formatDate = b(w), w.parseDate = C({
|
|
config: w.config,
|
|
l10n: w.l10n
|
|
});
|
|
}
|
|
function ce(e) {
|
|
if ("function" != typeof w.config.position) {
|
|
if (void 0 !== w.calendarContainer) {
|
|
pe("onPreCalendarPosition");
|
|
var t = e || w._positionElement, n = Array.prototype.reduce.call(w.calendarContainer.children, (function(e, t) {
|
|
return e + t.offsetHeight;
|
|
}), 0), a = w.calendarContainer.offsetWidth, i = w.config.position.split(" "), o = i[0], r = i.length > 1 ? i[1] : null, l = t.getBoundingClientRect(), c = window.innerHeight - l.bottom, s = "above" === o || "below" !== o && c < n && l.top > n, u = window.pageYOffset + l.top + (s ? -n - 2 : t.offsetHeight + 2);
|
|
if (d(w.calendarContainer, "arrowTop", !s), d(w.calendarContainer, "arrowBottom", s),
|
|
!w.config.inline) {
|
|
var f = window.pageXOffset + l.left, m = !1, g = !1;
|
|
"center" === r ? (f -= (a - l.width) / 2, m = !0) : "right" === r && (f -= a - l.width,
|
|
g = !0), d(w.calendarContainer, "arrowLeft", !m && !g), d(w.calendarContainer, "arrowCenter", m),
|
|
d(w.calendarContainer, "arrowRight", g);
|
|
var p = window.document.body.offsetWidth - (window.pageXOffset + l.right), h = f + a > window.document.body.offsetWidth, v = p + a > window.document.body.offsetWidth;
|
|
if (d(w.calendarContainer, "rightMost", h), !w.config.static) if (w.calendarContainer.style.top = u + "px",
|
|
h) if (v) {
|
|
var D = function() {
|
|
for (var e = null, t = 0; t < document.styleSheets.length; t++) {
|
|
var n = document.styleSheets[t];
|
|
try {
|
|
n.cssRules;
|
|
} catch (e) {
|
|
continue;
|
|
}
|
|
e = n;
|
|
break;
|
|
}
|
|
return null != e ? e : (a = document.createElement("style"), document.head.appendChild(a),
|
|
a.sheet);
|
|
var a;
|
|
}();
|
|
if (void 0 === D) return;
|
|
var b = window.document.body.offsetWidth, C = Math.max(0, b / 2 - a / 2), M = D.cssRules.length, y = "{left:" + l.left + "px;right:auto;}";
|
|
d(w.calendarContainer, "rightMost", !1), d(w.calendarContainer, "centerMost", !0),
|
|
D.insertRule(".flatpickr-calendar.centerMost:before,.flatpickr-calendar.centerMost:after" + y, M),
|
|
w.calendarContainer.style.left = C + "px", w.calendarContainer.style.right = "auto";
|
|
} else w.calendarContainer.style.left = "auto", w.calendarContainer.style.right = p + "px"; else w.calendarContainer.style.left = f + "px",
|
|
w.calendarContainer.style.right = "auto";
|
|
}
|
|
}
|
|
} else w.config.position(w, e);
|
|
}
|
|
function de() {
|
|
w.config.noCalendar || w.isMobile || (K(), De(), J());
|
|
}
|
|
function se() {
|
|
w._input.focus(), -1 !== window.navigator.userAgent.indexOf("MSIE") || void 0 !== navigator.msMaxTouchPoints ? setTimeout(w.close, 0) : w.close();
|
|
}
|
|
function ue(e) {
|
|
e.preventDefault(), e.stopPropagation();
|
|
var t = f(g(e), (function(e) {
|
|
return e.classList && e.classList.contains("flatpickr-day") && !e.classList.contains("flatpickr-disabled") && !e.classList.contains("notAllowed");
|
|
}));
|
|
if (void 0 !== t) {
|
|
var n = t, a = w.latestSelectedDateObj = new Date(n.dateObj.getTime()), i = (a.getMonth() < w.currentMonth || a.getMonth() > w.currentMonth + w.config.showMonths - 1) && "range" !== w.config.mode;
|
|
if (w.selectedDateElem = n, "single" === w.config.mode) w.selectedDates = [ a ]; else if ("multiple" === w.config.mode) {
|
|
var o = ve(a);
|
|
o ? w.selectedDates.splice(parseInt(o), 1) : w.selectedDates.push(a);
|
|
} else "range" === w.config.mode && (2 === w.selectedDates.length && w.clear(!1, !1),
|
|
w.latestSelectedDateObj = a, w.selectedDates.push(a), 0 !== M(a, w.selectedDates[0], !0) && w.selectedDates.sort((function(e, t) {
|
|
return e.getTime() - t.getTime();
|
|
})));
|
|
if (S(), i) {
|
|
var r = w.currentYear !== a.getFullYear();
|
|
w.currentYear = a.getFullYear(), w.currentMonth = a.getMonth(), r && (pe("onYearChange"),
|
|
K()), pe("onMonthChange");
|
|
}
|
|
if (De(), J(), be(), i || "range" === w.config.mode || 1 !== w.config.showMonths ? void 0 !== w.selectedDateElem && void 0 === w.hourElement && w.selectedDateElem && w.selectedDateElem.focus() : L(n),
|
|
void 0 !== w.hourElement && void 0 !== w.hourElement && w.hourElement.focus(), w.config.closeOnSelect) {
|
|
var l = "single" === w.config.mode && !w.config.enableTime, c = "range" === w.config.mode && 2 === w.selectedDates.length && !w.config.enableTime;
|
|
(l || c) && se();
|
|
}
|
|
N();
|
|
}
|
|
}
|
|
w.parseDate = C({
|
|
config: w.config,
|
|
l10n: w.l10n
|
|
}), w._handlers = [], w.pluginElements = [], w.loadedPlugins = [], w._bind = A,
|
|
w._setHoursFromDate = _, w._positionCalendar = ce, w.changeMonth = G, w.changeYear = Q,
|
|
w.clear = function(e, t) {
|
|
void 0 === e && (e = !0);
|
|
void 0 === t && (t = !0);
|
|
w.input.value = "", void 0 !== w.altInput && (w.altInput.value = "");
|
|
void 0 !== w.mobileInput && (w.mobileInput.value = "");
|
|
w.selectedDates = [], w.latestSelectedDateObj = void 0, !0 === t && (w.currentYear = w._initialDate.getFullYear(),
|
|
w.currentMonth = w._initialDate.getMonth());
|
|
if (!0 === w.config.enableTime) {
|
|
var n = x(w.config), a = n.hours, i = n.minutes, o = n.seconds;
|
|
O(a, i, o);
|
|
}
|
|
w.redraw(), e && pe("onChange");
|
|
}, w.close = function() {
|
|
w.isOpen = !1, w.isMobile || (void 0 !== w.calendarContainer && w.calendarContainer.classList.remove("open"),
|
|
void 0 !== w._input && w._input.classList.remove("active"));
|
|
pe("onClose");
|
|
}, w._createElement = s, w.destroy = function() {
|
|
void 0 !== w.config && pe("onDestroy");
|
|
for (var e = w._handlers.length; e--; ) w._handlers[e].remove();
|
|
if (w._handlers = [], w.mobileInput) w.mobileInput.parentNode && w.mobileInput.parentNode.removeChild(w.mobileInput),
|
|
w.mobileInput = void 0; else if (w.calendarContainer && w.calendarContainer.parentNode) if (w.config.static && w.calendarContainer.parentNode) {
|
|
var t = w.calendarContainer.parentNode;
|
|
if (t.lastChild && t.removeChild(t.lastChild), t.parentNode) {
|
|
for (;t.firstChild; ) t.parentNode.insertBefore(t.firstChild, t);
|
|
t.parentNode.removeChild(t);
|
|
}
|
|
} else w.calendarContainer.parentNode.removeChild(w.calendarContainer);
|
|
w.altInput && (w.input.type = "text", w.altInput.parentNode && w.altInput.parentNode.removeChild(w.altInput),
|
|
delete w.altInput);
|
|
w.input && (w.input.type = w.input._type, w.input.classList.remove("flatpickr-input"),
|
|
w.input.removeAttribute("readonly"));
|
|
[ "_showTimeInput", "latestSelectedDateObj", "_hideNextMonthArrow", "_hidePrevMonthArrow", "__hideNextMonthArrow", "__hidePrevMonthArrow", "isMobile", "isOpen", "selectedDateElem", "minDateHasTime", "maxDateHasTime", "days", "daysContainer", "_input", "_positionElement", "innerContainer", "rContainer", "monthNav", "todayDateElem", "calendarContainer", "weekdayContainer", "prevMonthNav", "nextMonthNav", "monthsDropdownContainer", "currentMonthElement", "currentYearElement", "navigationCurrentMonth", "selectedDateElem", "config" ].forEach((function(e) {
|
|
try {
|
|
delete w[e];
|
|
} catch (e) {}
|
|
}));
|
|
}, w.isEnabled = X, w.jumpToDate = P, w.open = function(e, t) {
|
|
void 0 === t && (t = w._positionElement);
|
|
if (!0 === w.isMobile) {
|
|
if (e) {
|
|
e.preventDefault();
|
|
var n = g(e);
|
|
n && n.blur();
|
|
}
|
|
return void 0 !== w.mobileInput && (w.mobileInput.focus(), w.mobileInput.click()),
|
|
void pe("onOpen");
|
|
}
|
|
if (w._input.disabled || w.config.inline) return;
|
|
var a = w.isOpen;
|
|
w.isOpen = !0, a || (w.calendarContainer.classList.add("open"), w._input.classList.add("active"),
|
|
pe("onOpen"), ce(t));
|
|
!0 === w.config.enableTime && !0 === w.config.noCalendar && (!1 !== w.config.allowInput || void 0 !== e && w.timeContainer.contains(e.relatedTarget) || setTimeout((function() {
|
|
return w.hourElement.select();
|
|
}), 50));
|
|
}, w.redraw = de, w.set = function(e, t) {
|
|
if (null !== e && "object" == typeof e) for (var a in Object.assign(w.config, e),
|
|
e) void 0 !== fe[a] && fe[a].forEach((function(e) {
|
|
return e();
|
|
})); else w.config[e] = t, void 0 !== fe[e] ? fe[e].forEach((function(e) {
|
|
return e();
|
|
})) : n.indexOf(e) > -1 && (w.config[e] = c(t));
|
|
w.redraw(), be(!0);
|
|
}, w.setDate = function(e, t, n) {
|
|
void 0 === t && (t = !1);
|
|
void 0 === n && (n = w.config.dateFormat);
|
|
if (0 !== e && !e || e instanceof Array && 0 === e.length) return w.clear(t);
|
|
me(e, n), w.latestSelectedDateObj = w.selectedDates[w.selectedDates.length - 1],
|
|
w.redraw(), P(void 0, t), _(), 0 === w.selectedDates.length && w.clear(!1);
|
|
be(t), t && pe("onChange");
|
|
}, w.toggle = function(e) {
|
|
if (!0 === w.isOpen) return w.close();
|
|
w.open(e);
|
|
};
|
|
var fe = {
|
|
locale: [ le, z ],
|
|
showMonths: [ q, k, $ ],
|
|
minDate: [ P ],
|
|
maxDate: [ P ],
|
|
clickOpens: [ function() {
|
|
!0 === w.config.clickOpens ? (A(w._input, "focus", w.open), A(w._input, "click", w.open)) : (w._input.removeEventListener("focus", w.open),
|
|
w._input.removeEventListener("click", w.open));
|
|
} ]
|
|
};
|
|
function me(e, t) {
|
|
var n = [];
|
|
if (e instanceof Array) n = e.map((function(e) {
|
|
return w.parseDate(e, t);
|
|
})); else if (e instanceof Date || "number" == typeof e) n = [ w.parseDate(e, t) ]; else if ("string" == typeof e) switch (w.config.mode) {
|
|
case "single":
|
|
case "time":
|
|
n = [ w.parseDate(e, t) ];
|
|
break;
|
|
|
|
case "multiple":
|
|
n = e.split(w.config.conjunction).map((function(e) {
|
|
return w.parseDate(e, t);
|
|
}));
|
|
break;
|
|
|
|
case "range":
|
|
n = e.split(w.l10n.rangeSeparator).map((function(e) {
|
|
return w.parseDate(e, t);
|
|
}));
|
|
} else w.config.errorHandler(new Error("Invalid date supplied: " + JSON.stringify(e)));
|
|
w.selectedDates = w.config.allowInvalidPreload ? n : n.filter((function(e) {
|
|
return e instanceof Date && X(e, !1);
|
|
})), "range" === w.config.mode && w.selectedDates.sort((function(e, t) {
|
|
return e.getTime() - t.getTime();
|
|
}));
|
|
}
|
|
function ge(e) {
|
|
return e.slice().map((function(e) {
|
|
return "string" == typeof e || "number" == typeof e || e instanceof Date ? w.parseDate(e, void 0, !0) : e && "object" == typeof e && e.from && e.to ? {
|
|
from: w.parseDate(e.from, void 0),
|
|
to: w.parseDate(e.to, void 0)
|
|
} : e;
|
|
})).filter((function(e) {
|
|
return e;
|
|
}));
|
|
}
|
|
function pe(e, t) {
|
|
if (void 0 !== w.config) {
|
|
var n = w.config[e];
|
|
if (void 0 !== n && n.length > 0) for (var a = 0; n[a] && a < n.length; a++) n[a](w.selectedDates, w.input.value, w, t);
|
|
"onChange" === e && (w.input.dispatchEvent(he("change")), w.input.dispatchEvent(he("input")));
|
|
}
|
|
}
|
|
function he(e) {
|
|
var t = document.createEvent("Event");
|
|
return t.initEvent(e, !0, !0), t;
|
|
}
|
|
function ve(e) {
|
|
for (var t = 0; t < w.selectedDates.length; t++) if (0 === M(w.selectedDates[t], e)) return "" + t;
|
|
return !1;
|
|
}
|
|
function De() {
|
|
w.config.noCalendar || w.isMobile || !w.monthNav || (w.yearElements.forEach((function(e, t) {
|
|
var n = new Date(w.currentYear, w.currentMonth, 1);
|
|
n.setMonth(w.currentMonth + t), w.config.showMonths > 1 || "static" === w.config.monthSelectorType ? w.monthElements[t].textContent = h(n.getMonth(), w.config.shorthandCurrentMonth, w.l10n) + " " : w.monthsDropdownContainer.value = n.getMonth().toString(),
|
|
e.value = n.getFullYear().toString();
|
|
})), w._hidePrevMonthArrow = void 0 !== w.config.minDate && (w.currentYear === w.config.minDate.getFullYear() ? w.currentMonth <= w.config.minDate.getMonth() : w.currentYear < w.config.minDate.getFullYear()),
|
|
w._hideNextMonthArrow = void 0 !== w.config.maxDate && (w.currentYear === w.config.maxDate.getFullYear() ? w.currentMonth + 1 > w.config.maxDate.getMonth() : w.currentYear > w.config.maxDate.getFullYear()));
|
|
}
|
|
function we(e) {
|
|
return w.selectedDates.map((function(t) {
|
|
return w.formatDate(t, e);
|
|
})).filter((function(e, t, n) {
|
|
return "range" !== w.config.mode || w.config.enableTime || n.indexOf(e) === t;
|
|
})).join("range" !== w.config.mode ? w.config.conjunction : w.l10n.rangeSeparator);
|
|
}
|
|
function be(e) {
|
|
void 0 === e && (e = !0), void 0 !== w.mobileInput && w.mobileFormatStr && (w.mobileInput.value = void 0 !== w.latestSelectedDateObj ? w.formatDate(w.latestSelectedDateObj, w.mobileFormatStr) : ""),
|
|
w.input.value = we(w.config.dateFormat), void 0 !== w.altInput && (w.altInput.value = we(w.config.altFormat)),
|
|
!1 !== e && pe("onValueUpdate");
|
|
}
|
|
function Ce(e) {
|
|
var t = g(e), n = w.prevMonthNav.contains(t), a = w.nextMonthNav.contains(t);
|
|
n || a ? G(n ? -1 : 1) : w.yearElements.indexOf(t) >= 0 ? t.select() : t.classList.contains("arrowUp") ? w.changeYear(w.currentYear + 1) : t.classList.contains("arrowDown") && w.changeYear(w.currentYear - 1);
|
|
}
|
|
return function() {
|
|
w.element = w.input = p, w.isOpen = !1, function() {
|
|
var t = [ "wrap", "weekNumbers", "allowInput", "allowInvalidPreload", "clickOpens", "time_24hr", "enableTime", "noCalendar", "altInput", "shorthandCurrentMonth", "inline", "static", "enableSeconds", "disableMobile" ], i = e(e({}, JSON.parse(JSON.stringify(p.dataset || {}))), v), o = {};
|
|
w.config.parseDate = i.parseDate, w.config.formatDate = i.formatDate, Object.defineProperty(w.config, "enable", {
|
|
get: function() {
|
|
return w.config._enable;
|
|
},
|
|
set: function(e) {
|
|
w.config._enable = ge(e);
|
|
}
|
|
}), Object.defineProperty(w.config, "disable", {
|
|
get: function() {
|
|
return w.config._disable;
|
|
},
|
|
set: function(e) {
|
|
w.config._disable = ge(e);
|
|
}
|
|
});
|
|
var r = "time" === i.mode;
|
|
if (!i.dateFormat && (i.enableTime || r)) {
|
|
var l = T.defaultConfig.dateFormat || a.dateFormat;
|
|
o.dateFormat = i.noCalendar || r ? "H:i" + (i.enableSeconds ? ":S" : "") : l + " H:i" + (i.enableSeconds ? ":S" : "");
|
|
}
|
|
if (i.altInput && (i.enableTime || r) && !i.altFormat) {
|
|
var d = T.defaultConfig.altFormat || a.altFormat;
|
|
o.altFormat = i.noCalendar || r ? "h:i" + (i.enableSeconds ? ":S K" : " K") : d + " h:i" + (i.enableSeconds ? ":S" : "") + " K";
|
|
}
|
|
Object.defineProperty(w.config, "minDate", {
|
|
get: function() {
|
|
return w.config._minDate;
|
|
},
|
|
set: oe("min")
|
|
}), Object.defineProperty(w.config, "maxDate", {
|
|
get: function() {
|
|
return w.config._maxDate;
|
|
},
|
|
set: oe("max")
|
|
});
|
|
var s = function(e) {
|
|
return function(t) {
|
|
w.config["min" === e ? "_minTime" : "_maxTime"] = w.parseDate(t, "H:i:S");
|
|
};
|
|
};
|
|
Object.defineProperty(w.config, "minTime", {
|
|
get: function() {
|
|
return w.config._minTime;
|
|
},
|
|
set: s("min")
|
|
}), Object.defineProperty(w.config, "maxTime", {
|
|
get: function() {
|
|
return w.config._maxTime;
|
|
},
|
|
set: s("max")
|
|
}), "time" === i.mode && (w.config.noCalendar = !0, w.config.enableTime = !0);
|
|
Object.assign(w.config, o, i);
|
|
for (var u = 0; u < t.length; u++) w.config[t[u]] = !0 === w.config[t[u]] || "true" === w.config[t[u]];
|
|
n.filter((function(e) {
|
|
return void 0 !== w.config[e];
|
|
})).forEach((function(e) {
|
|
w.config[e] = c(w.config[e] || []).map(E);
|
|
})), w.isMobile = !w.config.disableMobile && !w.config.inline && "single" === w.config.mode && !w.config.disable.length && !w.config.enable && !w.config.weekNumbers && /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
|
for (u = 0; u < w.config.plugins.length; u++) {
|
|
var f = w.config.plugins[u](w) || {};
|
|
for (var m in f) n.indexOf(m) > -1 ? w.config[m] = c(f[m]).map(E).concat(w.config[m]) : void 0 === i[m] && (w.config[m] = f[m]);
|
|
}
|
|
i.altInputClass || (w.config.altInputClass = re().className + " " + w.config.altInputClass);
|
|
pe("onParseConfig");
|
|
}(), le(), function() {
|
|
if (w.input = re(), !w.input) return void w.config.errorHandler(new Error("Invalid input element specified"));
|
|
w.input._type = w.input.type, w.input.type = "text", w.input.classList.add("flatpickr-input"),
|
|
w._input = w.input, w.config.altInput && (w.altInput = s(w.input.nodeName, w.config.altInputClass),
|
|
w._input = w.altInput, w.altInput.placeholder = w.input.placeholder, w.altInput.disabled = w.input.disabled,
|
|
w.altInput.required = w.input.required, w.altInput.tabIndex = w.input.tabIndex,
|
|
w.altInput.type = "text", w.input.setAttribute("type", "hidden"), !w.config.static && w.input.parentNode && w.input.parentNode.insertBefore(w.altInput, w.input.nextSibling));
|
|
w.config.allowInput || w._input.setAttribute("readonly", "readonly");
|
|
w._positionElement = w.config.positionElement || w._input;
|
|
}(), function() {
|
|
w.selectedDates = [], w.now = w.parseDate(w.config.now) || new Date;
|
|
var e = w.config.defaultDate || ("INPUT" !== w.input.nodeName && "TEXTAREA" !== w.input.nodeName || !w.input.placeholder || w.input.value !== w.input.placeholder ? w.input.value : null);
|
|
e && me(e, w.config.dateFormat);
|
|
w._initialDate = w.selectedDates.length > 0 ? w.selectedDates[0] : w.config.minDate && w.config.minDate.getTime() > w.now.getTime() ? w.config.minDate : w.config.maxDate && w.config.maxDate.getTime() < w.now.getTime() ? w.config.maxDate : w.now,
|
|
w.currentYear = w._initialDate.getFullYear(), w.currentMonth = w._initialDate.getMonth(),
|
|
w.selectedDates.length > 0 && (w.latestSelectedDateObj = w.selectedDates[0]);
|
|
void 0 !== w.config.minTime && (w.config.minTime = w.parseDate(w.config.minTime, "H:i"));
|
|
void 0 !== w.config.maxTime && (w.config.maxTime = w.parseDate(w.config.maxTime, "H:i"));
|
|
w.minDateHasTime = !!w.config.minDate && (w.config.minDate.getHours() > 0 || w.config.minDate.getMinutes() > 0 || w.config.minDate.getSeconds() > 0),
|
|
w.maxDateHasTime = !!w.config.maxDate && (w.config.maxDate.getHours() > 0 || w.config.maxDate.getMinutes() > 0 || w.config.maxDate.getSeconds() > 0);
|
|
}(), w.utils = {
|
|
getDaysInMonth: function(e, t) {
|
|
return void 0 === e && (e = w.currentMonth), void 0 === t && (t = w.currentYear),
|
|
1 === e && (t % 4 == 0 && t % 100 != 0 || t % 400 == 0) ? 29 : w.l10n.daysInMonth[e];
|
|
}
|
|
}, w.isMobile || function() {
|
|
var e = window.document.createDocumentFragment();
|
|
if (w.calendarContainer = s("div", "flatpickr-calendar"), w.calendarContainer.tabIndex = -1,
|
|
!w.config.noCalendar) {
|
|
if (e.appendChild((w.monthNav = s("div", "flatpickr-months"), w.yearElements = [],
|
|
w.monthElements = [], w.prevMonthNav = s("span", "flatpickr-prev-month"), w.prevMonthNav.innerHTML = w.config.prevArrow,
|
|
w.nextMonthNav = s("span", "flatpickr-next-month"), w.nextMonthNav.innerHTML = w.config.nextArrow,
|
|
q(), Object.defineProperty(w, "_hidePrevMonthArrow", {
|
|
get: function() {
|
|
return w.__hidePrevMonthArrow;
|
|
},
|
|
set: function(e) {
|
|
w.__hidePrevMonthArrow !== e && (d(w.prevMonthNav, "flatpickr-disabled", e), w.__hidePrevMonthArrow = e);
|
|
}
|
|
}), Object.defineProperty(w, "_hideNextMonthArrow", {
|
|
get: function() {
|
|
return w.__hideNextMonthArrow;
|
|
},
|
|
set: function(e) {
|
|
w.__hideNextMonthArrow !== e && (d(w.nextMonthNav, "flatpickr-disabled", e), w.__hideNextMonthArrow = e);
|
|
}
|
|
}), w.currentYearElement = w.yearElements[0], De(), w.monthNav)), w.innerContainer = s("div", "flatpickr-innerContainer"),
|
|
w.config.weekNumbers) {
|
|
var t = function() {
|
|
w.calendarContainer.classList.add("hasWeeks");
|
|
var e = s("div", "flatpickr-weekwrapper");
|
|
e.appendChild(s("span", "flatpickr-weekday", w.l10n.weekAbbreviation));
|
|
var t = s("div", "flatpickr-weeks");
|
|
return e.appendChild(t), {
|
|
weekWrapper: e,
|
|
weekNumbers: t
|
|
};
|
|
}(), n = t.weekWrapper, a = t.weekNumbers;
|
|
w.innerContainer.appendChild(n), w.weekNumbers = a, w.weekWrapper = n;
|
|
}
|
|
w.rContainer = s("div", "flatpickr-rContainer"), w.rContainer.appendChild($()),
|
|
w.daysContainer || (w.daysContainer = s("div", "flatpickr-days"), w.daysContainer.tabIndex = -1),
|
|
J(), w.rContainer.appendChild(w.daysContainer), w.innerContainer.appendChild(w.rContainer),
|
|
e.appendChild(w.innerContainer);
|
|
}
|
|
w.config.enableTime && e.appendChild(function() {
|
|
w.calendarContainer.classList.add("hasTime"), w.config.noCalendar && w.calendarContainer.classList.add("noCalendar");
|
|
var e = x(w.config);
|
|
w.timeContainer = s("div", "flatpickr-time"), w.timeContainer.tabIndex = -1;
|
|
var t = s("span", "flatpickr-time-separator", ":"), n = m("flatpickr-hour", {
|
|
"aria-label": w.l10n.hourAriaLabel
|
|
});
|
|
w.hourElement = n.getElementsByTagName("input")[0];
|
|
var a = m("flatpickr-minute", {
|
|
"aria-label": w.l10n.minuteAriaLabel
|
|
});
|
|
w.minuteElement = a.getElementsByTagName("input")[0], w.hourElement.tabIndex = w.minuteElement.tabIndex = -1,
|
|
w.hourElement.value = o(w.latestSelectedDateObj ? w.latestSelectedDateObj.getHours() : w.config.time_24hr ? e.hours : function(e) {
|
|
switch (e % 24) {
|
|
case 0:
|
|
case 12:
|
|
return 12;
|
|
|
|
default:
|
|
return e % 12;
|
|
}
|
|
}(e.hours)), w.minuteElement.value = o(w.latestSelectedDateObj ? w.latestSelectedDateObj.getMinutes() : e.minutes),
|
|
w.hourElement.setAttribute("step", w.config.hourIncrement.toString()), w.minuteElement.setAttribute("step", w.config.minuteIncrement.toString()),
|
|
w.hourElement.setAttribute("min", w.config.time_24hr ? "0" : "1"), w.hourElement.setAttribute("max", w.config.time_24hr ? "23" : "12"),
|
|
w.hourElement.setAttribute("maxlength", "2"), w.minuteElement.setAttribute("min", "0"),
|
|
w.minuteElement.setAttribute("max", "59"), w.minuteElement.setAttribute("maxlength", "2"),
|
|
w.timeContainer.appendChild(n), w.timeContainer.appendChild(t), w.timeContainer.appendChild(a),
|
|
w.config.time_24hr && w.timeContainer.classList.add("time24hr");
|
|
if (w.config.enableSeconds) {
|
|
w.timeContainer.classList.add("hasSeconds");
|
|
var i = m("flatpickr-second");
|
|
w.secondElement = i.getElementsByTagName("input")[0], w.secondElement.value = o(w.latestSelectedDateObj ? w.latestSelectedDateObj.getSeconds() : e.seconds),
|
|
w.secondElement.setAttribute("step", w.minuteElement.getAttribute("step")), w.secondElement.setAttribute("min", "0"),
|
|
w.secondElement.setAttribute("max", "59"), w.secondElement.setAttribute("maxlength", "2"),
|
|
w.timeContainer.appendChild(s("span", "flatpickr-time-separator", ":")), w.timeContainer.appendChild(i);
|
|
}
|
|
w.config.time_24hr || (w.amPM = s("span", "flatpickr-am-pm", w.l10n.amPM[r((w.latestSelectedDateObj ? w.hourElement.value : w.config.defaultHour) > 11)]),
|
|
w.amPM.title = w.l10n.toggleTitle, w.amPM.tabIndex = -1, w.timeContainer.appendChild(w.amPM));
|
|
return w.timeContainer;
|
|
}());
|
|
d(w.calendarContainer, "rangeMode", "range" === w.config.mode), d(w.calendarContainer, "animate", !0 === w.config.animate),
|
|
d(w.calendarContainer, "multiMonth", w.config.showMonths > 1), w.calendarContainer.appendChild(e);
|
|
var i = void 0 !== w.config.appendTo && void 0 !== w.config.appendTo.nodeType;
|
|
if ((w.config.inline || w.config.static) && (w.calendarContainer.classList.add(w.config.inline ? "inline" : "static"),
|
|
w.config.inline && (!i && w.element.parentNode ? w.element.parentNode.insertBefore(w.calendarContainer, w._input.nextSibling) : void 0 !== w.config.appendTo && w.config.appendTo.appendChild(w.calendarContainer)),
|
|
w.config.static)) {
|
|
var l = s("div", "flatpickr-wrapper");
|
|
w.element.parentNode && w.element.parentNode.insertBefore(l, w.element), l.appendChild(w.element),
|
|
w.altInput && l.appendChild(w.altInput), l.appendChild(w.calendarContainer);
|
|
}
|
|
w.config.static || w.config.inline || (void 0 !== w.config.appendTo ? w.config.appendTo : window.document.body).appendChild(w.calendarContainer);
|
|
}(), function() {
|
|
w.config.wrap && [ "open", "close", "toggle", "clear" ].forEach((function(e) {
|
|
Array.prototype.forEach.call(w.element.querySelectorAll("[data-" + e + "]"), (function(t) {
|
|
return A(t, "click", w[e]);
|
|
}));
|
|
}));
|
|
if (w.isMobile) return void function() {
|
|
var e = w.config.enableTime ? w.config.noCalendar ? "time" : "datetime-local" : "date";
|
|
w.mobileInput = s("input", w.input.className + " flatpickr-mobile"), w.mobileInput.tabIndex = 1,
|
|
w.mobileInput.type = e, w.mobileInput.disabled = w.input.disabled, w.mobileInput.required = w.input.required,
|
|
w.mobileInput.placeholder = w.input.placeholder, w.mobileFormatStr = "datetime-local" === e ? "Y-m-d\\TH:i:S" : "date" === e ? "Y-m-d" : "H:i:S",
|
|
w.selectedDates.length > 0 && (w.mobileInput.defaultValue = w.mobileInput.value = w.formatDate(w.selectedDates[0], w.mobileFormatStr));
|
|
w.config.minDate && (w.mobileInput.min = w.formatDate(w.config.minDate, "Y-m-d"));
|
|
w.config.maxDate && (w.mobileInput.max = w.formatDate(w.config.maxDate, "Y-m-d"));
|
|
w.input.getAttribute("step") && (w.mobileInput.step = String(w.input.getAttribute("step")));
|
|
w.input.type = "hidden", void 0 !== w.altInput && (w.altInput.type = "hidden");
|
|
try {
|
|
w.input.parentNode && w.input.parentNode.insertBefore(w.mobileInput, w.input.nextSibling);
|
|
} catch (e) {}
|
|
A(w.mobileInput, "change", (function(e) {
|
|
w.setDate(g(e).value, !1, w.mobileFormatStr), pe("onChange"), pe("onClose");
|
|
}));
|
|
}();
|
|
var e = l(ie, 50);
|
|
w._debouncedChange = l(N, 300), w.daysContainer && !/iPhone|iPad|iPod/i.test(navigator.userAgent) && A(w.daysContainer, "mouseover", (function(e) {
|
|
"range" === w.config.mode && ae(g(e));
|
|
}));
|
|
A(window.document.body, "keydown", ne), w.config.inline || w.config.static || A(window, "resize", e);
|
|
void 0 !== window.ontouchstart ? A(window.document, "touchstart", Z) : A(window.document, "mousedown", Z);
|
|
A(window.document, "focus", Z, {
|
|
capture: !0
|
|
}), !0 === w.config.clickOpens && (A(w._input, "focus", w.open), A(w._input, "click", w.open));
|
|
void 0 !== w.daysContainer && (A(w.monthNav, "click", Ce), A(w.monthNav, [ "keyup", "increment" ], F),
|
|
A(w.daysContainer, "click", ue));
|
|
if (void 0 !== w.timeContainer && void 0 !== w.minuteElement && void 0 !== w.hourElement) {
|
|
var t = function(e) {
|
|
return g(e).select();
|
|
};
|
|
A(w.timeContainer, [ "increment" ], I), A(w.timeContainer, "blur", I, {
|
|
capture: !0
|
|
}), A(w.timeContainer, "click", Y), A([ w.hourElement, w.minuteElement ], [ "focus", "click" ], t),
|
|
void 0 !== w.secondElement && A(w.secondElement, "focus", (function() {
|
|
return w.secondElement && w.secondElement.select();
|
|
})), void 0 !== w.amPM && A(w.amPM, "click", (function(e) {
|
|
I(e), N();
|
|
}));
|
|
}
|
|
w.config.allowInput && A(w._input, "blur", te);
|
|
}(), (w.selectedDates.length || w.config.noCalendar) && (w.config.enableTime && _(w.config.noCalendar ? w.latestSelectedDateObj : void 0),
|
|
be(!1)), k();
|
|
var t = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
!w.isMobile && t && ce(), pe("onReady");
|
|
}(), w;
|
|
}
|
|
function k(e, t) {
|
|
for (var n = Array.prototype.slice.call(e).filter((function(e) {
|
|
return e instanceof HTMLElement;
|
|
})), a = [], i = 0; i < n.length; i++) {
|
|
var o = n[i];
|
|
try {
|
|
if (null !== o.getAttribute("data-fp-omit")) continue;
|
|
void 0 !== o._flatpickr && (o._flatpickr.destroy(), o._flatpickr = void 0), o._flatpickr = E(o, t || {}),
|
|
a.push(o._flatpickr);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
return 1 === a.length ? a[0] : a;
|
|
}
|
|
"undefined" != typeof HTMLElement && "undefined" != typeof HTMLCollection && "undefined" != typeof NodeList && (HTMLCollection.prototype.flatpickr = NodeList.prototype.flatpickr = function(e) {
|
|
return k(this, e);
|
|
}, HTMLElement.prototype.flatpickr = function(e) {
|
|
return k([ this ], e);
|
|
});
|
|
var T = function(e, t) {
|
|
return "string" == typeof e ? k(window.document.querySelectorAll(e), t) : e instanceof Node ? k([ e ], t) : k(e, t);
|
|
};
|
|
return T.defaultConfig = {}, T.l10ns = {
|
|
en: e({}, i),
|
|
default: e({}, i)
|
|
}, T.localize = function(t) {
|
|
T.l10ns.default = e(e({}, T.l10ns.default), t);
|
|
}, T.setDefaults = function(t) {
|
|
T.defaultConfig = e(e({}, T.defaultConfig), t);
|
|
}, T.parseDate = C({}), T.formatDate = b({}), T.compareDates = M, "undefined" != typeof jQuery && void 0 !== jQuery.fn && (jQuery.fn.flatpickr = function(e) {
|
|
return k(this, e);
|
|
}), Date.prototype.fp_incr = function(e) {
|
|
return new Date(this.getFullYear(), this.getMonth(), this.getDate() + ("string" == typeof e ? parseInt(e, 10) : e));
|
|
}, "undefined" != typeof window && (window.flatpickr = T), T;
|
|
}));
|
|
})(flatpickr$1);
|
|
return flatpickr$1.exports;
|
|
}
|
|
var hasRequiredCalendar;
|
|
function requireCalendar() {
|
|
if (hasRequiredCalendar) return calendar$1.exports;
|
|
hasRequiredCalendar = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, Hash, Constants, Realtime, Cache, Rec, nThen, Listmap, FP, Crypto, ChainPad) => {
|
|
var Calendar = {};
|
|
Calendar.setCustomize = () => {};
|
|
var getStore = function(ctx, id) {
|
|
if (!id || id === 1) {
|
|
return ctx.store;
|
|
}
|
|
var m = ctx.store.modules && ctx.store.modules.team;
|
|
if (!m) {
|
|
return;
|
|
}
|
|
return m.getTeam(id);
|
|
};
|
|
var makeCalendar = function() {
|
|
var hash = Hash.createRandomHash("calendar");
|
|
var secret = Hash.getSecrets("calendar", hash);
|
|
var roHash = Hash.getViewHashFromKeys(secret);
|
|
var href = Hash.hashToHref(hash, "calendar");
|
|
var roHref = Hash.hashToHref(roHash, "calendar");
|
|
return {
|
|
href,
|
|
roHref,
|
|
channel: secret.channel
|
|
};
|
|
};
|
|
var initializeCalendars = function(ctx, cb) {
|
|
var proxy = ctx.store.proxy;
|
|
proxy.calendars = proxy.calendars || {};
|
|
setTimeout(cb);
|
|
};
|
|
var sendUpdate = function(ctx, c) {
|
|
ctx.emit("UPDATE", {
|
|
teams: c.stores,
|
|
roTeams: c.roStores,
|
|
id: c.channel,
|
|
loading: !c.ready && !c.cacheready,
|
|
readOnly: c.readOnly || !c.ready && c.cacheready || c.offline,
|
|
offline: c.offline,
|
|
deleted: !c.stores.length,
|
|
restricted: c.restricted,
|
|
owned: ctx.Store.isOwned(c.owners),
|
|
content: Util.clone(c.proxy),
|
|
hashes: c.hashes
|
|
}, ctx.clients);
|
|
};
|
|
var clearReminders = function(ctx, id) {
|
|
var calendar = ctx.calendars[id];
|
|
if (!calendar || !calendar.reminders) {
|
|
return;
|
|
}
|
|
Object.keys(calendar.reminders).forEach((function(uid) {
|
|
if (!Array.isArray(calendar.reminders[uid])) {
|
|
return;
|
|
}
|
|
calendar.reminders[uid].forEach((function(to) {
|
|
clearTimeout(to);
|
|
}));
|
|
}));
|
|
};
|
|
var closeCalendar = function(ctx, id) {
|
|
var ctxCal = ctx.calendars[id];
|
|
if (!ctxCal) {
|
|
return;
|
|
}
|
|
if (!ctxCal.stores.length) {
|
|
ctxCal.lm.stop();
|
|
clearReminders(ctx, id);
|
|
delete ctx.calendars[id];
|
|
}
|
|
};
|
|
var updateLocalCalendars = function(ctx, c, data) {
|
|
c.stores.forEach((function(id) {
|
|
var s = getStore(ctx, id);
|
|
if (!s || !s.proxy) {
|
|
return;
|
|
}
|
|
if (!s.rpc) {
|
|
return;
|
|
}
|
|
if (!s.proxy.calendars) {
|
|
return;
|
|
}
|
|
var cal = s.proxy.calendars[c.channel];
|
|
if (!cal) {
|
|
return;
|
|
}
|
|
if (cal.color !== data.color) {
|
|
cal.color = data.color;
|
|
}
|
|
if (cal.title !== data.title) {
|
|
cal.title = data.title;
|
|
}
|
|
}));
|
|
};
|
|
var getRecurring = function(ev) {
|
|
var mid = new Date;
|
|
var start = new Date(mid.getFullYear(), mid.getMonth() - 1, 15);
|
|
var end = new Date(mid.getFullYear(), mid.getMonth() + 1, 15);
|
|
var startId = Rec.getMonthId(start);
|
|
var midId = Rec.getMonthId(mid);
|
|
var endId = Rec.getMonthId(end);
|
|
var toAdd = Rec.getRecurring([ startId, midId, endId ], [ ev ]);
|
|
var all = [ ev ];
|
|
Array.prototype.push.apply(all, toAdd);
|
|
return Rec.applyUpdates(all);
|
|
};
|
|
var clearDismissed = function(ctx, uid) {
|
|
var h = Util.find(ctx, [ "store", "proxy", "hideReminders" ]) || {};
|
|
Object.keys(h).filter((function(id) {
|
|
return id === uid;
|
|
})).forEach((function(id) {
|
|
delete h[id];
|
|
}));
|
|
};
|
|
var _updateEventReminders = function(ctx, reminders, _ev, useLastVisit) {
|
|
var now = +new Date;
|
|
var ev = Util.clone(_ev);
|
|
var uid = ev.id;
|
|
if (Array.isArray(reminders[uid])) {
|
|
reminders[uid].forEach((function(to) {
|
|
clearTimeout(to);
|
|
}));
|
|
}
|
|
reminders[uid] = [];
|
|
if (_ev.deleted) {
|
|
return;
|
|
}
|
|
var d = Util.find(ctx, [ "store", "proxy", "hideReminders", uid ]) || [];
|
|
var last = ctx.store.data.lastVisit;
|
|
if (ev.isAllDay) {
|
|
if (ev.startDay) {
|
|
ev.start = +FP.parseDate(ev.startDay);
|
|
}
|
|
if (ev.endDay) {
|
|
var endDate = FP.parseDate(ev.endDay);
|
|
endDate.setHours(23);
|
|
endDate.setMinutes(59);
|
|
endDate.setSeconds(59);
|
|
ev.end = +endDate;
|
|
}
|
|
}
|
|
var oneWeekAgo = now - 7 * 24 * 3600 * 1e3;
|
|
var missed = useLastVisit && ev.start > last && ev.end <= now && ev.end > oneWeekAgo;
|
|
if (ev.end <= now && !missed) {
|
|
delete reminders[uid];
|
|
clearDismissed(ctx, uid);
|
|
return;
|
|
}
|
|
var send = function(d) {
|
|
var hide = Util.find(ctx, [ "store", "proxy", "settings", "general", "calendar", "hideNotif" ]);
|
|
if (hide) {
|
|
return;
|
|
}
|
|
var ctime = ev.start <= now ? ev.start : +new Date;
|
|
ctx.store.mailbox.showMessage("reminders", {
|
|
msg: {
|
|
ctime,
|
|
type: "REMINDER",
|
|
missed: Boolean(missed),
|
|
content: ev
|
|
},
|
|
hash: "REMINDER|" + uid + "-" + d
|
|
}, null, (function() {}));
|
|
};
|
|
var sent = false;
|
|
var sendNotif = function(delay) {
|
|
sent = true;
|
|
ctx.Store.onReadyEvt.reg((function() {
|
|
send(delay);
|
|
}));
|
|
};
|
|
var notifs = ev.reminders || [];
|
|
notifs.sort((function(a, b) {
|
|
return a - b;
|
|
}));
|
|
notifs.some((function(delayMinutes) {
|
|
var delay = delayMinutes * 6e4;
|
|
var time = now + delay;
|
|
if (d.some((function(minutes) {
|
|
return delayMinutes >= minutes;
|
|
}))) {
|
|
return;
|
|
}
|
|
if (ev.start - time >= 2147483647) {
|
|
return true;
|
|
}
|
|
if (ev.start <= time) {
|
|
sendNotif(delayMinutes);
|
|
return true;
|
|
}
|
|
reminders[uid].push(setTimeout((function() {
|
|
sendNotif(delayMinutes);
|
|
}), ev.start - time));
|
|
}));
|
|
if (!sent) {
|
|
ctx.Store.onReadyEvt.reg((function() {
|
|
ctx.store.mailbox.hideMessage("reminders", {
|
|
hash: "REMINDER|" + uid
|
|
}, null, (function() {}));
|
|
}));
|
|
}
|
|
};
|
|
var updateEventReminders = function(ctx, reminders, ev, useLastVisit) {
|
|
var all = getRecurring(Util.clone(ev));
|
|
all.forEach((function(_ev) {
|
|
_updateEventReminders(ctx, reminders, _ev, useLastVisit);
|
|
}));
|
|
};
|
|
var addReminders = function(ctx, id, ev) {
|
|
var calendar = ctx.calendars[id];
|
|
if (!ev) {
|
|
return;
|
|
}
|
|
if (!calendar || !calendar.reminders) {
|
|
return;
|
|
}
|
|
if (calendar.stores.length === 1 && calendar.stores[0] === 0) {
|
|
return;
|
|
}
|
|
updateEventReminders(ctx, calendar.reminders, ev);
|
|
};
|
|
var addInitialReminders = function(ctx, id, useLastVisit) {
|
|
var calendar = ctx.calendars[id];
|
|
if (!calendar || !calendar.reminders) {
|
|
return;
|
|
}
|
|
if (Object.keys(calendar.reminders).length) {
|
|
return;
|
|
}
|
|
if (calendar.stores.length === 1 && calendar.stores[0] === 0) {
|
|
return;
|
|
}
|
|
var content = Util.find(calendar, [ "proxy", "content" ]);
|
|
if (!content) {
|
|
return;
|
|
}
|
|
Object.keys(content).forEach((function(uid) {
|
|
updateEventReminders(ctx, calendar.reminders, content[uid], useLastVisit);
|
|
}));
|
|
};
|
|
var openChannel = function(ctx, cfg, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb || function() {}));
|
|
var teamId = cfg.storeId;
|
|
var data = cfg.data;
|
|
var channel = data.channel;
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
var c = ctx.calendars[channel];
|
|
var update = function() {
|
|
sendUpdate(ctx, c);
|
|
};
|
|
if (c) {
|
|
if (c.readOnly && data.href) {
|
|
var upgradeParsed = Hash.parsePadUrl(data.href);
|
|
var upgradeSecret = Hash.getSecrets("calendar", upgradeParsed.hash, data.password);
|
|
var upgradeCrypto = Crypto.createEncryptor(upgradeSecret.keys);
|
|
c.hashes.editHash = Hash.getEditHashFromKeys(upgradeSecret);
|
|
c.lm.setReadOnly(false, upgradeCrypto);
|
|
c.readOnly = false;
|
|
} else if (teamId === 0) {
|
|
if (c.stores.length === 1 && c.stores[0] === 0 && c.tempId.length && cfg.cId) {
|
|
c.tempId.push(cfg.cId);
|
|
}
|
|
return void cb();
|
|
}
|
|
if (c.roStores.indexOf(teamId) !== -1 && data.href) {
|
|
c.roStores.splice(c.roStores.indexOf(teamId), 1);
|
|
if (c.stores.indexOf(0) !== -1) {
|
|
c.stores.splice(c.stores.indexOf(0), 1);
|
|
}
|
|
update();
|
|
}
|
|
if (c.stores && c.stores.indexOf(teamId) !== -1) {
|
|
return void cb();
|
|
}
|
|
if (c.stores.indexOf(0) !== -1) {
|
|
c.stores.splice(c.stores.indexOf(0), 1);
|
|
c.tempId = [];
|
|
}
|
|
c.stores.push(teamId);
|
|
if (!data.href) {
|
|
c.roStores.push(teamId);
|
|
}
|
|
update();
|
|
return void cb();
|
|
}
|
|
c = ctx.calendars[channel] = {
|
|
ready: false,
|
|
channel,
|
|
readOnly: !data.href,
|
|
tempId: [],
|
|
stores: [ teamId ],
|
|
roStores: data.href ? [] : [ teamId ],
|
|
reminders: {},
|
|
hashes: {}
|
|
};
|
|
if (teamId === 0) {
|
|
c.tempId.push(cfg.cId);
|
|
}
|
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
|
var secret = Hash.getSecrets("calendar", parsed.hash, data.password);
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
c.hashes.viewHash = Hash.getViewHashFromKeys(secret);
|
|
if (data.href) {
|
|
c.hashes.editHash = Hash.getEditHashFromKeys(secret);
|
|
}
|
|
c.proxy = {
|
|
metadata: {
|
|
color: data.color,
|
|
title: data.title
|
|
}
|
|
};
|
|
update();
|
|
var onDeleted = function() {
|
|
c.stores.forEach((function(storeId) {
|
|
var store = getStore(ctx, storeId);
|
|
if (!store || !store.rpc || !store.proxy.calendars) {
|
|
return;
|
|
}
|
|
delete store.proxy.calendars[channel];
|
|
var unpin = store.unpin || ctx.unpinPads;
|
|
unpin([ channel ], (function(res) {
|
|
if (res && res.error) {
|
|
console.error(res.error);
|
|
}
|
|
}));
|
|
}));
|
|
if (c.lm) {
|
|
c.lm.stop();
|
|
}
|
|
c.stores = [];
|
|
sendUpdate(ctx, c);
|
|
clearReminders(ctx, channel);
|
|
delete ctx.calendars[channel];
|
|
};
|
|
nThen((function(waitFor) {
|
|
if (!ctx.store.network || cfg.isNew) {
|
|
return;
|
|
}
|
|
ctx.Store.isNewChannel(null, channel, waitFor((function(obj) {
|
|
if (obj && obj.error) {
|
|
return;
|
|
}
|
|
if (obj && typeof obj.isNew === "boolean") {
|
|
if (obj.isNew) {
|
|
onDeleted();
|
|
cb({
|
|
error: "EDELETED"
|
|
});
|
|
waitFor.abort();
|
|
return;
|
|
}
|
|
}
|
|
})));
|
|
})).nThen((function() {
|
|
var edPublic;
|
|
if (teamId === 1 || !teamId) {
|
|
edPublic = ctx.store.proxy.edPublic;
|
|
} else {
|
|
var teams = ctx.store.modules.team && ctx.store.modules.team.getTeamsData();
|
|
var team = teams && teams[teamId];
|
|
edPublic = team ? team.edPublic : undefined;
|
|
}
|
|
var config = {
|
|
data: {},
|
|
network: ctx.store.network || ctx.store.networkPromise,
|
|
channel: secret.channel,
|
|
crypto,
|
|
owners: [ edPublic ],
|
|
ChainPad,
|
|
validateKey: secret.keys.validateKey || undefined,
|
|
userName: "calendar",
|
|
Cache,
|
|
classic: true,
|
|
onRejected: ctx.Store && ctx.Store.onRejected
|
|
};
|
|
var lm = Listmap.create(config);
|
|
c.lm = lm;
|
|
var proxy = c.proxy = lm.proxy;
|
|
var _updateCalled = false;
|
|
var _update = function() {
|
|
if (_updateCalled) {
|
|
return;
|
|
}
|
|
_updateCalled = true;
|
|
setTimeout((function() {
|
|
_updateCalled = false;
|
|
update();
|
|
}));
|
|
};
|
|
lm.proxy.on("cacheready", (function() {
|
|
if (!proxy.metadata) {
|
|
return;
|
|
}
|
|
c.cacheready = true;
|
|
_update();
|
|
if (cb) {
|
|
cb(null, lm.proxy);
|
|
}
|
|
addInitialReminders(ctx, channel, cfg.lastVisitNotif);
|
|
})).on("ready", (function(info) {
|
|
var md = info.metadata;
|
|
c.owners = md.owners || [];
|
|
c.ready = true;
|
|
if (!proxy.metadata) {
|
|
if (!cfg.isNew) {
|
|
return void onDeleted();
|
|
}
|
|
proxy.metadata = {
|
|
color: data.color,
|
|
title: data.title
|
|
};
|
|
}
|
|
_update();
|
|
if (cb) {
|
|
cb(null, lm.proxy);
|
|
}
|
|
addInitialReminders(ctx, channel, cfg.lastVisitNotif);
|
|
})).on("change", [], (function() {
|
|
if (!c.ready) {
|
|
return;
|
|
}
|
|
_update();
|
|
})).on("change", [ "content" ], (function(o, n, p) {
|
|
if (p.length === 2 && n && !o) {
|
|
return void addReminders(ctx, channel, n);
|
|
}
|
|
if (p.length === 2 && !n && o) {
|
|
return void addReminders(ctx, channel, {
|
|
id: p[1],
|
|
start: 0
|
|
});
|
|
}
|
|
if (p.length >= 3 && [ "start", "reminders", "isAllDay" ].includes(p[2])) {
|
|
return void setTimeout((function() {
|
|
addReminders(ctx, channel, proxy.content[p[1]]);
|
|
}));
|
|
}
|
|
if (p.length >= 6 && [ "start", "reminders", "isAllDay" ].includes(p[5])) {
|
|
return void setTimeout((function() {
|
|
addReminders(ctx, channel, proxy.content[p[1]]);
|
|
}));
|
|
}
|
|
})).on("remove", [ "content" ], (function(x, p) {
|
|
_update();
|
|
if (p.length >= 3 && p[2] === "reminders" || p.length >= 6 && p[5] === "reminders") {
|
|
return void setTimeout((function() {
|
|
addReminders(ctx, channel, proxy.content[p[1]]);
|
|
}));
|
|
}
|
|
})).on("change", [ "metadata" ], (function() {
|
|
var md = proxy.metadata;
|
|
if (!md || !md.title || !md.color) {
|
|
return;
|
|
}
|
|
updateLocalCalendars(ctx, c, md);
|
|
})).on("disconnect", (function() {
|
|
c.offline = true;
|
|
_update();
|
|
})).on("reconnect", (function() {
|
|
c.offline = false;
|
|
_update();
|
|
})).on("error", (function(info) {
|
|
if (!info || !info.error) {
|
|
return;
|
|
}
|
|
if (info.error === "EDELETED") {
|
|
return void onDeleted();
|
|
}
|
|
if (info.error === "ERESTRICTED") {
|
|
c.restricted = true;
|
|
_update();
|
|
}
|
|
cb(info);
|
|
}));
|
|
}));
|
|
};
|
|
var decryptTeamCalendarHref = function(store, calData) {
|
|
if (!calData.href) {
|
|
return;
|
|
}
|
|
if (calData.href.indexOf("#") !== -1) {
|
|
return;
|
|
}
|
|
if (store.secondaryKey) {
|
|
try {
|
|
calData.href = store.userObject.cryptor.decrypt(calData.href);
|
|
} catch (e) {
|
|
console.error(e);
|
|
delete calData.href;
|
|
}
|
|
} else {
|
|
delete calData.href;
|
|
}
|
|
};
|
|
var initializeStore = function(ctx, store) {
|
|
var c = store.proxy.calendars;
|
|
var storeId = store.id || 1;
|
|
store.proxy.on("change", [ "calendars" ], (function(o, n, p) {
|
|
if (p.length < 2) {
|
|
return;
|
|
}
|
|
if (o && !n) {
|
|
(function() {
|
|
var id = p[1];
|
|
var ctxCal = ctx.calendars[id];
|
|
if (!ctxCal) {
|
|
return;
|
|
}
|
|
var idx = ctxCal.stores.indexOf(storeId);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
ctxCal.stores.splice(idx, 1);
|
|
var roIdx = ctxCal.roStores.indexOf(storeId);
|
|
if (roIdx !== -1) {
|
|
ctxCal.roStores.splice(roIdx, 1);
|
|
}
|
|
closeCalendar(ctx, id);
|
|
sendUpdate(ctx, ctxCal);
|
|
})();
|
|
}
|
|
if (!o && n) {
|
|
(function() {
|
|
var id = p[1];
|
|
var _cal = store.proxy.calendars[id];
|
|
if (!_cal) {
|
|
return;
|
|
}
|
|
var cal = Util.clone(_cal);
|
|
decryptTeamCalendarHref(store, cal);
|
|
openChannel(ctx, {
|
|
storeId,
|
|
data: cal
|
|
});
|
|
})();
|
|
}
|
|
}));
|
|
Object.keys(c || {}).forEach((function(channel) {
|
|
var cal = Util.clone(c[channel]);
|
|
decryptTeamCalendarHref(store, cal);
|
|
openChannel(ctx, {
|
|
storeId,
|
|
lastVisitNotif: true,
|
|
data: cal
|
|
});
|
|
}));
|
|
};
|
|
var openChannels = function(ctx) {
|
|
initializeStore(ctx, ctx.store);
|
|
var teams = ctx.store.modules.team && ctx.store.modules.team.getTeamsData();
|
|
if (!teams) {
|
|
return;
|
|
}
|
|
Object.keys(teams).forEach((function(id) {
|
|
var store = getStore(ctx, id);
|
|
initializeStore(ctx, store);
|
|
}));
|
|
};
|
|
var subscribe = function(ctx, data, cId, cb) {
|
|
var idx = ctx.clients.indexOf(cId);
|
|
if (idx === -1) {
|
|
ctx.clients.push(cId);
|
|
}
|
|
cb({
|
|
length: Object.keys(ctx.calendars).length
|
|
});
|
|
Object.keys(ctx.calendars).forEach((function(channel) {
|
|
var c = ctx.calendars[channel] || {};
|
|
sendUpdate(ctx, c);
|
|
}));
|
|
};
|
|
var importICSCalendar = function(ctx, data, cId, cb) {
|
|
var id = data.id;
|
|
var c = ctx.calendars[id];
|
|
if (!c || !c.proxy) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var json = data.json;
|
|
c.proxy.content = c.proxy.content || {};
|
|
Object.keys(json).forEach((function(uid) {
|
|
c.proxy.content[uid] = json[uid];
|
|
addReminders(ctx, id, json[uid]);
|
|
}));
|
|
Realtime.whenRealtimeSyncs(c.lm.realtime, (function() {
|
|
sendUpdate(ctx, c);
|
|
cb();
|
|
}));
|
|
};
|
|
var openCalendar = function(ctx, data, cId, cb) {
|
|
var secret = Hash.getSecrets("calendar", data.hash, data.password);
|
|
var hash = Hash.getEditHashFromKeys(secret);
|
|
var roHash = Hash.getViewHashFromKeys(secret);
|
|
var cal = {
|
|
href: hash && Hash.hashToHref(hash, "calendar"),
|
|
roHref: roHash && Hash.hashToHref(roHash, "calendar"),
|
|
channel: secret.channel,
|
|
color: Util.getRandomColor(),
|
|
title: "..."
|
|
};
|
|
openChannel(ctx, {
|
|
cId,
|
|
storeId: 0,
|
|
data: cal
|
|
}, cb);
|
|
};
|
|
var importCalendar = function(ctx, data, cId, cb) {
|
|
var id = data.id;
|
|
var c = ctx.calendars[id];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
if (!Array.isArray(c.stores) || c.stores.indexOf(data.teamId) === -1) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var store = ctx.store;
|
|
var calendars = store.proxy.calendars = store.proxy.calendars || {};
|
|
var hash = c.hashes.editHash;
|
|
var roHash = c.hashes.viewHash;
|
|
calendars[id] = {
|
|
href: hash && Hash.hashToHref(hash, "calendar"),
|
|
roHref: roHash && Hash.hashToHref(roHash, "calendar"),
|
|
channel: id,
|
|
color: Util.find(c, [ "proxy", "metadata", "color" ]) || Util.getRandomColor(),
|
|
title: Util.find(c, [ "proxy", "metadata", "title" ]) || "..."
|
|
};
|
|
ctx.Store.onSync(null, cb);
|
|
openChannel(ctx, {
|
|
storeId: 1,
|
|
data: {
|
|
href: calendars[id].href,
|
|
toHref: calendars[id].roHref,
|
|
channel: id
|
|
}
|
|
});
|
|
};
|
|
var addCalendar = function(ctx, data, cId, cb) {
|
|
var store = getStore(ctx, data.teamId);
|
|
if (!store) {
|
|
return void cb({
|
|
error: "NO_STORE"
|
|
});
|
|
}
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
var c = store.proxy.calendars = store.proxy.calendars || {};
|
|
var parsed = Hash.parsePadUrl(data.href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
|
if (secret.channel !== data.channel) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var hash = Hash.getEditHashFromKeys(secret);
|
|
var roHash = Hash.getViewHashFromKeys(secret);
|
|
var href = hash && Hash.hashToHref(hash, "calendar");
|
|
var cal = {
|
|
href,
|
|
roHref: roHash && Hash.hashToHref(roHash, "calendar"),
|
|
color: data.color,
|
|
title: data.title,
|
|
channel: data.channel
|
|
};
|
|
if (c[data.channel] && (c[data.channel].href || !cal.href)) {
|
|
return void cb();
|
|
}
|
|
cal.color = data.color;
|
|
cal.title = data.title;
|
|
openChannel(ctx, {
|
|
storeId: store.id || 1,
|
|
data: Util.clone(cal)
|
|
}, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return void cb({
|
|
error: err.error
|
|
});
|
|
}
|
|
if (href && store.id && store.secondaryKey) {
|
|
try {
|
|
cal.href = store.userObject.cryptor.encrypt(href);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}
|
|
c[cal.channel] = cal;
|
|
var pin = store.pin || ctx.pinPads;
|
|
pin([ cal.channel ], (function(res) {
|
|
if (res && res.error) {
|
|
console.error(res.error);
|
|
}
|
|
}));
|
|
ctx.Store.onSync(store.id, cb);
|
|
}));
|
|
};
|
|
var createCalendar = function(ctx, data, cId, cb) {
|
|
var store = getStore(ctx, data.teamId);
|
|
if (!store) {
|
|
return void cb({
|
|
error: "NO_STORE"
|
|
});
|
|
}
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
var c = store.proxy.calendars = store.proxy.calendars || {};
|
|
var cal = makeCalendar();
|
|
cal.color = data.color;
|
|
cal.title = data.title;
|
|
openChannel(ctx, {
|
|
storeId: store.id || 1,
|
|
data: cal,
|
|
isNew: true
|
|
}, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
return void cb({
|
|
error: err.error
|
|
});
|
|
}
|
|
var ctxCal = ctx.calendars[cal.channel];
|
|
Realtime.whenRealtimeSyncs(ctxCal.lm.realtime, (function() {
|
|
c[cal.channel] = cal;
|
|
var pin = store.pin || ctx.pinPads;
|
|
pin([ cal.channel ], (function(res) {
|
|
if (res && res.error) {
|
|
console.error(res.error);
|
|
}
|
|
}));
|
|
ctx.Store.onSync(store.id, cb);
|
|
}));
|
|
}));
|
|
};
|
|
var updateCalendar = function(ctx, data, cId, cb) {
|
|
var id = data.id;
|
|
var c = ctx.calendars[id];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var md = Util.find(c, [ "proxy", "metadata" ]);
|
|
if (!md) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
md.title = data.title;
|
|
md.color = data.color;
|
|
Realtime.whenRealtimeSyncs(c.lm.realtime, cb);
|
|
sendUpdate(ctx, c);
|
|
updateLocalCalendars(ctx, c, data);
|
|
};
|
|
var deleteCalendar = function(ctx, data, cId, cb) {
|
|
var store = getStore(ctx, data.teamId);
|
|
if (!store) {
|
|
return void cb({
|
|
error: "NO_STORE"
|
|
});
|
|
}
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "EFORBIDDEN"
|
|
});
|
|
}
|
|
if (!store.proxy.calendars) {
|
|
return;
|
|
}
|
|
var id = data.id;
|
|
var cal = store.proxy.calendars[id];
|
|
if (!cal) {
|
|
return void cb();
|
|
}
|
|
delete store.proxy.calendars[id];
|
|
var unpin = store.unpin || ctx.unpinPads;
|
|
unpin([ id ], (function(res) {
|
|
if (res && res.error) {
|
|
console.error(res.error);
|
|
}
|
|
}));
|
|
var ctxCal = ctx.calendars[id];
|
|
var idx = ctxCal.stores.indexOf(store.id || 1);
|
|
ctxCal.stores.splice(idx, 1);
|
|
closeCalendar(ctx, id);
|
|
ctx.Store.onSync(store.id, (function() {
|
|
sendUpdate(ctx, ctxCal);
|
|
cb();
|
|
}));
|
|
};
|
|
var createEvent = function(ctx, data, cId, cb) {
|
|
var id = data.calendarId;
|
|
var c = ctx.calendars[id];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var startDate = new Date(data.start);
|
|
var endDate = new Date(data.end);
|
|
if (data.isAllDay) {
|
|
data.startDay = startDate.getFullYear() + "-" + (startDate.getMonth() + 1) + "-" + startDate.getDate();
|
|
data.endDay = endDate.getFullYear() + "-" + (endDate.getMonth() + 1) + "-" + endDate.getDate();
|
|
} else {
|
|
delete data.startDay;
|
|
delete data.endDay;
|
|
}
|
|
c.proxy.content = c.proxy.content || {};
|
|
c.proxy.content[data.id] = data;
|
|
Realtime.whenRealtimeSyncs(c.lm.realtime, (function() {
|
|
addReminders(ctx, id, data);
|
|
sendUpdate(ctx, c);
|
|
cb();
|
|
}));
|
|
};
|
|
var updateEvent = function(ctx, data, cId, cb) {
|
|
if (!data || !data.ev) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var id = data.ev.calendarId;
|
|
var c = ctx.calendars[id];
|
|
if (!c || !c.proxy || !c.proxy.content) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
var ev = c.proxy.content[data.ev.id];
|
|
if (!ev) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
data.rawData = data.rawData || {};
|
|
var changes = data.changes || {};
|
|
var type = data.type || {};
|
|
var newC;
|
|
if (changes.calendarId) {
|
|
newC = ctx.calendars[changes.calendarId];
|
|
if (!newC || !newC.proxy) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
newC.proxy.content = newC.proxy.content || {};
|
|
}
|
|
var RECUPDATE = {
|
|
one: {},
|
|
from: {}
|
|
};
|
|
if ([ "one", "from", "all" ].includes(type.which)) {
|
|
ev.recUpdate = ev.recUpdate || RECUPDATE;
|
|
if (!ev.recUpdate.one) {
|
|
ev.recUpdate.one = {};
|
|
}
|
|
if (!ev.recUpdate.from) {
|
|
ev.recUpdate.from = {};
|
|
}
|
|
}
|
|
var update = ev.recUpdate;
|
|
var alwaysAll = [ "calendarId" ];
|
|
var keys = Object.keys(changes).filter((function(s) {
|
|
return !alwaysAll.includes(s);
|
|
}));
|
|
var cleanAfter = function(time) {
|
|
[ update.from, update.one ].forEach((function(obj) {
|
|
Object.keys(obj).forEach((function(d) {
|
|
if (Number(d) < time) {
|
|
return;
|
|
}
|
|
delete obj[d];
|
|
}));
|
|
}));
|
|
};
|
|
var cleanKeys = function(obj, when) {
|
|
Object.keys(obj).forEach((function(d) {
|
|
if (when && Number(d) < when) {
|
|
return;
|
|
}
|
|
keys.forEach((function(k) {
|
|
delete obj[d][k];
|
|
}));
|
|
}));
|
|
};
|
|
var dontSendUpdate = false;
|
|
if (typeof changes.recurrenceRule !== "undefined") {
|
|
if (type.which === "all" && changes.recurrenceRule.until) {
|
|
cleanAfter(changes.recurrenceRule.until);
|
|
} else if ([ "one", "from" ].includes(type.which) && !data.rawData.isOrigin) {
|
|
cleanAfter(type.when + 1);
|
|
} else {
|
|
update = ev.recUpdate = RECUPDATE;
|
|
}
|
|
}
|
|
if (type.which === "one") {
|
|
update.one[type.when] = update.one[type.when] || {};
|
|
} else if (type.which === "from") {
|
|
update.from[type.when] = update.from[type.when] || {};
|
|
cleanKeys(update.from, type.when);
|
|
cleanKeys(update.one, type.when);
|
|
} else if (type.which === "all") {
|
|
cleanKeys(update.from);
|
|
cleanKeys(update.one);
|
|
}
|
|
if (changes.start && update && (!type.which || type.which === "all")) {
|
|
var diff = changes.start - ev.start;
|
|
var newOne = {};
|
|
var newFrom = {};
|
|
Object.keys(update.one || {}).forEach((function(time) {
|
|
newOne[Number(time) + diff] = update.one[time];
|
|
}));
|
|
Object.keys(update.from || {}).forEach((function(time) {
|
|
newFrom[Number(time) + diff] = update.from[time];
|
|
}));
|
|
update.one = newOne;
|
|
update.from = newFrom;
|
|
}
|
|
var h = Util.find(ctx, [ "store", "proxy", "hideReminders" ]) || {};
|
|
if (changes.reminders) {
|
|
if (type.which === "one") {
|
|
if (!type.when || type.when === ev.start) {
|
|
delete h[data.ev.id];
|
|
} else {
|
|
delete h[data.ev.id + "|" + type.when];
|
|
}
|
|
} else if (type.which === "from") {
|
|
Object.keys(h).filter((function(id) {
|
|
return id.indexOf(data.ev.id) === 0;
|
|
})).forEach((function(id) {
|
|
var time = Number(id.split("|")[1]);
|
|
if (!time) {
|
|
return;
|
|
}
|
|
if (time < type.when) {
|
|
return;
|
|
}
|
|
delete h[id];
|
|
}));
|
|
} else {
|
|
Object.keys(h).filter((function(id) {
|
|
return id.indexOf(data.ev.id) === 0;
|
|
})).forEach((function(id) {
|
|
delete h[id];
|
|
}));
|
|
}
|
|
}
|
|
Object.keys(changes).forEach((function(key) {
|
|
if (!alwaysAll.includes(key) && type.which === "one") {
|
|
if (key === "recurrenceRule") {
|
|
if (data.rawData && data.rawData.isOrigin) {
|
|
return ev[key] = changes[key];
|
|
}
|
|
update.from[type.when] = update.from[type.when] || {};
|
|
return update.from[type.when][key] = changes[key];
|
|
}
|
|
update.one[type.when][key] = changes[key];
|
|
return;
|
|
}
|
|
if (!alwaysAll.includes(key) && type.which === "from") {
|
|
update.from[type.when][key] = changes[key];
|
|
return;
|
|
}
|
|
ev[key] = changes[key];
|
|
}));
|
|
var startDate = new Date(ev.start);
|
|
var endDate = new Date(ev.end);
|
|
if (ev.isAllDay) {
|
|
ev.startDay = startDate.getFullYear() + "-" + (startDate.getMonth() + 1) + "-" + startDate.getDate();
|
|
ev.endDay = endDate.getFullYear() + "-" + (endDate.getMonth() + 1) + "-" + endDate.getDate();
|
|
} else {
|
|
delete ev.startDay;
|
|
delete ev.endDay;
|
|
}
|
|
if (changes.calendarId && newC) {
|
|
newC.proxy.content[data.ev.id] = Util.clone(ev);
|
|
delete c.proxy.content[data.ev.id];
|
|
}
|
|
nThen((function(waitFor) {
|
|
Realtime.whenRealtimeSyncs(c.lm.realtime, waitFor());
|
|
if (newC) {
|
|
Realtime.whenRealtimeSyncs(newC.lm.realtime, waitFor());
|
|
}
|
|
})).nThen((function() {
|
|
if (newC) {
|
|
addReminders(ctx, id, {
|
|
id: ev.id,
|
|
start: 0
|
|
});
|
|
addReminders(ctx, ev.calendarId, ev);
|
|
} else if (changes.start || changes.reminders || changes.isAllDay) {
|
|
addReminders(ctx, id, ev);
|
|
}
|
|
{
|
|
sendUpdate(ctx, c);
|
|
}
|
|
if (newC && !dontSendUpdate) {
|
|
sendUpdate(ctx, newC);
|
|
}
|
|
cb();
|
|
}));
|
|
};
|
|
var deleteEvent = function(ctx, data, cId, cb) {
|
|
var id = data.calendarId;
|
|
var c = ctx.calendars[id];
|
|
if (!c) {
|
|
return void cb({
|
|
error: "ENOENT"
|
|
});
|
|
}
|
|
c.proxy.content = c.proxy.content || {};
|
|
var evId = data.id.split("|")[0];
|
|
if (data.id === evId) {
|
|
delete c.proxy.content[data.id];
|
|
} else {
|
|
var ev = c.proxy.content[evId];
|
|
var s = data.raw && data.raw.start;
|
|
if (s) {
|
|
ev.recUpdate = ev.recUpdate || {
|
|
one: {},
|
|
from: {}
|
|
};
|
|
ev.recUpdate.one[s] = {
|
|
deleted: true
|
|
};
|
|
}
|
|
}
|
|
Realtime.whenRealtimeSyncs(c.lm.realtime, (function() {
|
|
addReminders(ctx, id, {
|
|
id: data.id,
|
|
start: 0
|
|
});
|
|
sendUpdate(ctx, c);
|
|
cb();
|
|
}));
|
|
};
|
|
var removeClient = function(ctx, cId) {
|
|
var idx = ctx.clients.indexOf(cId);
|
|
if (idx !== -1) {
|
|
ctx.clients.splice(idx, 1);
|
|
}
|
|
Object.keys(ctx.calendars).forEach((function(id) {
|
|
var cal = ctx.calendars[id];
|
|
if (cal.stores.length !== 1 || cal.stores[0] !== 0 || !cal.tempId.length) {
|
|
return;
|
|
}
|
|
var idx = cal.tempId.indexOf(cId);
|
|
if (idx !== -1) {
|
|
cal.tempId.splice(idx, 1);
|
|
}
|
|
if (!cal.tempId.length) {
|
|
cal.stores = [];
|
|
closeCalendar(ctx, id);
|
|
}
|
|
}));
|
|
};
|
|
Calendar.init = function(cfg, waitFor, emit) {
|
|
var calendar = {};
|
|
var store = cfg.store;
|
|
var ctx = {
|
|
loggedIn: store.loggedIn && store.proxy.edPublic,
|
|
store,
|
|
Store: cfg.Store,
|
|
pinPads: cfg.pinPads,
|
|
unpinPads: cfg.unpinPads,
|
|
updateMetadata: cfg.updateMetadata,
|
|
emit,
|
|
onReady: Util.mkEvent(true),
|
|
calendars: {},
|
|
clients: []
|
|
};
|
|
initializeCalendars(ctx, waitFor((function(err) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
openChannels(ctx);
|
|
})));
|
|
ctx.store.proxy.on("change", [ "hideReminders" ], (function(o, n, p) {
|
|
var uid = p[1].split("|")[0];
|
|
Object.keys(ctx.calendars).some((function(calId) {
|
|
var c = ctx.calendars[calId];
|
|
if (!c || !c.proxy || !c.proxy.content) {
|
|
return;
|
|
}
|
|
if (c.proxy.content[uid]) {
|
|
setTimeout((function() {
|
|
addReminders(ctx, calId, c.proxy.content[uid]);
|
|
}));
|
|
return true;
|
|
}
|
|
}));
|
|
}));
|
|
calendar.closeTeam = function(teamId) {
|
|
Object.keys(ctx.calendars).forEach((function(id) {
|
|
var ctxCal = ctx.calendars[id];
|
|
var idx = ctxCal.stores.indexOf(teamId);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
ctxCal.stores.splice(idx, 1);
|
|
var roIdx = ctxCal.roStores.indexOf(teamId);
|
|
if (roIdx !== -1) {
|
|
ctxCal.roStores.splice(roIdx, 1);
|
|
}
|
|
closeCalendar(ctx, id);
|
|
sendUpdate(ctx, ctxCal);
|
|
}));
|
|
};
|
|
calendar.openTeam = function(teamId) {
|
|
var store = getStore(ctx, teamId);
|
|
if (!store) {
|
|
return;
|
|
}
|
|
initializeStore(ctx, store);
|
|
};
|
|
calendar.upgradeTeam = function(teamId) {
|
|
if (!teamId) {
|
|
return;
|
|
}
|
|
var store = getStore(ctx, teamId);
|
|
if (!store) {
|
|
return;
|
|
}
|
|
Object.keys(ctx.calendars).forEach((function(id) {
|
|
var ctxCal = ctx.calendars[id];
|
|
var idx = ctxCal.stores.indexOf(teamId);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
var _cal = store.proxy.calendars[id];
|
|
var cal = Util.clone(_cal);
|
|
decryptTeamCalendarHref(store, cal);
|
|
openChannel(ctx, {
|
|
storeId: teamId,
|
|
data: cal
|
|
});
|
|
sendUpdate(ctx, ctxCal);
|
|
}));
|
|
};
|
|
calendar.removeClient = function(clientId) {
|
|
removeClient(ctx, clientId);
|
|
};
|
|
calendar.execCommand = function(clientId, obj, cb) {
|
|
var cmd = obj.cmd;
|
|
var data = obj.data;
|
|
if (cmd === "SUBSCRIBE") {
|
|
return void subscribe(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "OPEN") {
|
|
ctx.Store.onReadyEvt.reg((function() {
|
|
openCalendar(ctx, data, clientId, cb);
|
|
}));
|
|
return;
|
|
}
|
|
if (cmd === "IMPORT") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
if (!ctx.loggedIn) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
return void importCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "IMPORT_ICS") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void importICSCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "ADD") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
if (!ctx.loggedIn) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
return void addCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CREATE") {
|
|
if (!ctx.loggedIn) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
if (data.initialCalendar) {
|
|
return void ctx.Store.onReadyEvt.reg((function() {
|
|
createCalendar(ctx, data, clientId, cb);
|
|
}));
|
|
}
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void createCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "UPDATE") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void updateCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DELETE") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
if (!ctx.loggedIn) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
return void deleteCalendar(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "CREATE_EVENT") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void createEvent(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "UPDATE_EVENT") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void updateEvent(ctx, data, clientId, cb);
|
|
}
|
|
if (cmd === "DELETE_EVENT") {
|
|
if (ctx.store.offline) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
return void deleteEvent(ctx, data, clientId, cb);
|
|
}
|
|
};
|
|
return calendar;
|
|
};
|
|
return Calendar;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil(), requireCommonHash(), requireCommonConstants(), requireCommonRealtime(), requireCacheStore(), requireRecurrence(), requireNthen(), requireChainpadListmap(), requireFlatpickr(), requireCrypto(), requireChainpad_dist());
|
|
}
|
|
})();
|
|
})(calendar$1);
|
|
return calendar$1.exports;
|
|
}
|
|
var hasRequiredAsyncStore;
|
|
function requireAsyncStore() {
|
|
if (hasRequiredAsyncStore) return asyncStore$1.exports;
|
|
hasRequiredAsyncStore = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (ApiConfig = {}, Sortify, UserObject, ProxyManager, Migrate, Hash, Util, Constants, Feedback, Realtime, Messaging, Pinpad, Rpc, Merge, Cache, SF, AccountTS, DriveTS, Cursor, Support, Integration, OnlyOffice, Mailbox, Profile, Team, Messenger, History, Calendar, Block, NetConfig, AppConfig = {}, Crypto, ChainPad, CpNetflux, Listmap, Netflux, nThen) => {
|
|
const Account = AccountTS.Account;
|
|
const Drive = DriveTS.Drive;
|
|
const window = globalThis;
|
|
globalThis.nacl = globalThis.nacl || Crypto.Nacl;
|
|
const Saferphore = Util.Saferphore;
|
|
var onReadyEvt = Util.mkEvent(true);
|
|
var onCacheReadyEvt = Util.mkEvent(true);
|
|
var onJoinedEvt = Util.mkEvent(true);
|
|
var onPadRejectedEvt = Util.mkEvent(true);
|
|
const setCustomize = data => {
|
|
ApiConfig = data.ApiConfig;
|
|
AppConfig = data.AppConfig;
|
|
};
|
|
var CACHE_MAX_AGE = 90;
|
|
var NEW_USER_SETTINGS = {
|
|
drive: {
|
|
hideDuplicate: true
|
|
},
|
|
pad: {
|
|
width: true,
|
|
spellcheck: true
|
|
},
|
|
security: {
|
|
unsafeLinks: false
|
|
},
|
|
general: {
|
|
allowUserFeedback: true
|
|
}
|
|
};
|
|
var create = function(config) {
|
|
var Store = window.Cryptpad_Store = {};
|
|
let postMessage = config.query || function() {};
|
|
let broadcast = config.broadcast || function() {};
|
|
var store = window.CryptPad_AsyncStore = {
|
|
modules: {}
|
|
};
|
|
Store.onReadyEvt = onReadyEvt;
|
|
var driveEventClients = [];
|
|
var sendDriveEvent = function(q, data, sender) {
|
|
driveEventClients.forEach((function(cId) {
|
|
if (cId === sender) {
|
|
return;
|
|
}
|
|
postMessage(cId, q, data);
|
|
}));
|
|
};
|
|
var getStore = function(teamId) {
|
|
if (!teamId) {
|
|
return store;
|
|
}
|
|
try {
|
|
var teams = store.modules["team"];
|
|
var team = teams.getTeam(teamId);
|
|
if (!team) {
|
|
console.error("Team not found", teamId);
|
|
return;
|
|
}
|
|
return team;
|
|
} catch (e) {
|
|
console.error(e);
|
|
console.error("Team not found", teamId);
|
|
return;
|
|
}
|
|
};
|
|
var onSync = Store.onSync = function(teamId, cb) {
|
|
var s = getStore(teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
nThen((function(waitFor) {
|
|
if (s.realtime) {
|
|
Realtime.whenRealtimeSyncs(s.realtime, waitFor());
|
|
}
|
|
if (!s.id && s.drive?.realtime) {
|
|
Realtime.whenRealtimeSyncs(s.drive.realtime, waitFor());
|
|
}
|
|
if (s.sharedFolders && typeof s.sharedFolders === "object") {
|
|
for (var k in s.sharedFolders) {
|
|
if (!s.sharedFolders[k].realtime) {
|
|
continue;
|
|
}
|
|
Realtime.whenRealtimeSyncs(s.sharedFolders[k].realtime, waitFor());
|
|
}
|
|
}
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
Store.get = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.proxy) {
|
|
return void cb({
|
|
error: "ENODRIVE"
|
|
});
|
|
}
|
|
cb(Util.find(s.proxy, data.key));
|
|
};
|
|
Store.set = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.proxy) {
|
|
return void cb({
|
|
error: "ENODRIVE"
|
|
});
|
|
}
|
|
var path = data.key.slice();
|
|
var key = path.pop();
|
|
var obj = Util.find(s.proxy, path);
|
|
if (!obj || typeof obj !== "object") {
|
|
return void cb({
|
|
error: "INVALID_PATH"
|
|
});
|
|
}
|
|
if (typeof data.value === "undefined") {
|
|
delete obj[key];
|
|
} else {
|
|
obj[key] = data.value;
|
|
}
|
|
if (!data.teamId) {
|
|
broadcast([ clientId ], "UPDATE_METADATA");
|
|
if (Array.isArray(path) && path[0] === "profile" && store.messenger) {
|
|
Messaging.updateMyData(store);
|
|
}
|
|
}
|
|
onSync(data.teamId, cb);
|
|
};
|
|
const copyObject = (src, target) => {
|
|
Object.keys(src).forEach((k => {
|
|
delete src[k];
|
|
}));
|
|
Object.keys(target).forEach((k => {
|
|
src[k] = Util.clone(target[k]);
|
|
}));
|
|
};
|
|
const getProxy = teamId => {
|
|
let proxy;
|
|
if (!teamId) {
|
|
proxy = store.drive?.proxy;
|
|
} else {
|
|
const s = getStore(teamId);
|
|
proxy = s?.drive?.proxy;
|
|
}
|
|
return proxy;
|
|
};
|
|
Store.drive = {
|
|
get: (clientId, data, cb) => {
|
|
let proxy = getProxy(data.teamId);
|
|
if (!proxy) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
cb(proxy);
|
|
},
|
|
set: (clientId, data, cb) => {
|
|
let proxy = getProxy(data.teamId);
|
|
if (!proxy) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
copyObject(proxy, data.value);
|
|
onSync(data.teamId, cb);
|
|
}
|
|
};
|
|
Store.getSharedFolder = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
var id = data.id;
|
|
var proxy;
|
|
if (!s || !s.manager) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (s.manager.folders[id]) {
|
|
proxy = Util.clone(s.manager.folders[id].proxy);
|
|
proxy.offline = Boolean(s.manager.folders[id].offline);
|
|
return void cb(proxy);
|
|
} else {
|
|
var shared = Util.find(s.proxy, [ "drive", UserObject.SHARED_FOLDERS ]) || {};
|
|
if (shared[id]) {
|
|
return void Store.loadSharedFolder(data.teamId, id, shared[id], (function() {
|
|
cb(s.manager.folders[id].proxy);
|
|
}));
|
|
}
|
|
}
|
|
cb({});
|
|
};
|
|
Store.restoreSharedFolder = function(clientId, data, cb) {
|
|
if (!data.sfId || !data.drive) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var s = getStore(data.teamId);
|
|
if (s.sharedFolders[data.sfId]) {
|
|
Object.keys(data.drive).forEach((function(k) {
|
|
s.sharedFolders[data.sfId].proxy[k] = data.drive[k];
|
|
}));
|
|
Object.keys(s.sharedFolders[data.sfId].proxy).forEach((function(k) {
|
|
if (data.drive[k]) {
|
|
return;
|
|
}
|
|
delete s.sharedFolders[data.sfId].proxy[k];
|
|
}));
|
|
}
|
|
onSync(data.teamId, cb);
|
|
};
|
|
Store.hasSigningKeys = function() {
|
|
if (!store.proxy) {
|
|
return;
|
|
}
|
|
return typeof store.proxy.edPrivate === "string" && typeof store.proxy.edPublic === "string";
|
|
};
|
|
Store.hasCurveKeys = function() {
|
|
if (!store.proxy) {
|
|
return;
|
|
}
|
|
return typeof store.proxy.curvePrivate === "string" && typeof store.proxy.curvePublic === "string";
|
|
};
|
|
Store.isOwned = function(owners) {
|
|
var edPublic = store.proxy.edPublic;
|
|
if (!edPublic) {
|
|
return false;
|
|
}
|
|
if (!Array.isArray(owners) || !owners.length) {
|
|
return false;
|
|
}
|
|
if (owners.indexOf(edPublic) !== -1) {
|
|
return true;
|
|
}
|
|
var teams = store.proxy.teams;
|
|
if (!teams) {
|
|
return false;
|
|
}
|
|
return Object.keys(teams).some((function(id) {
|
|
var ed = Util.find(teams[id], [ "keys", "drive", "edPublic" ]);
|
|
return ed && owners.indexOf(ed) !== -1;
|
|
}));
|
|
};
|
|
var getUserChannelList = function() {
|
|
var userChannel = `${store.driveChannel}#drive`;
|
|
if (!userChannel) {
|
|
return null;
|
|
}
|
|
var list = store.manager.getChannelsList("pin");
|
|
var profile = store.proxy.profile;
|
|
if (profile) {
|
|
var profileChan = profile.edit ? Hash.hrefToHexChannelId("/profile/#" + profile.edit, null) : null;
|
|
if (profileChan) {
|
|
list.push(profileChan);
|
|
}
|
|
var avatarChan = profile.avatar ? Hash.hrefToHexChannelId(profile.avatar, null) : null;
|
|
if (avatarChan) {
|
|
list.push(avatarChan);
|
|
}
|
|
}
|
|
if (store.proxy.todo) {
|
|
list.push(Hash.hrefToHexChannelId("/todo/#" + store.proxy.todo, null));
|
|
}
|
|
if (store.proxy.friends) {
|
|
var fList = Messaging.getFriendChannelsList(store.proxy);
|
|
list = list.concat(fList);
|
|
}
|
|
if (store.proxy.mailboxes) {
|
|
var mList = Object.keys(store.proxy.mailboxes).map((function(m) {
|
|
if (m === "broadcast" && !store.isAdmin) {
|
|
return;
|
|
}
|
|
return store.proxy.mailboxes[m].channel;
|
|
})).filter(Boolean);
|
|
list = list.concat(mList);
|
|
}
|
|
if (store.proxy.calendars) {
|
|
var cList = Object.keys(store.proxy.calendars).map((function(c) {
|
|
return store.proxy.calendars[c].channel;
|
|
}));
|
|
list = list.concat(cList);
|
|
}
|
|
list.push(userChannel);
|
|
if (store.data && store.data.blockId) {
|
|
list.push(`${store.data.blockId}#block`);
|
|
}
|
|
list.sort();
|
|
return list;
|
|
};
|
|
var getExpirableChannelList = function() {
|
|
return store.manager.getChannelsList("expirable");
|
|
};
|
|
var getCanonicalChannelList = function(expirable) {
|
|
var list = expirable ? getExpirableChannelList() : getUserChannelList();
|
|
return Util.deduplicateString(list).sort();
|
|
};
|
|
Store.pinPads = function(clientId, data, cb) {
|
|
if (!data) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var s = getStore(data && data.teamId);
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (typeof cb !== "function") {
|
|
console.error("expected a callback");
|
|
cb = function() {};
|
|
}
|
|
var pads = data.pads || data;
|
|
s.rpc.pin(pads, (function(e) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
cb({});
|
|
}));
|
|
};
|
|
Store.unpinPads = function(clientId, data, cb) {
|
|
if (!data) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var s = getStore(data && data.teamId);
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
var pads = data.pads || data;
|
|
s.rpc.unpin(pads, (function(e) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
cb({});
|
|
}));
|
|
};
|
|
var account = store.account = {};
|
|
Store.getPinnedUsage = function(clientId, data, cb) {
|
|
var s = getStore(data && data.teamId);
|
|
if (!s || !s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
s.rpc.getFileListSize((function(err, bytes) {
|
|
if (!s.id && typeof bytes === "number") {
|
|
account.usage = bytes;
|
|
}
|
|
cb({
|
|
bytes
|
|
});
|
|
}));
|
|
};
|
|
Store.updatePinLimit = function(clientId, data, cb) {
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
store.rpc.updatePinLimits((function(e, limit, plan, note) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
account.limit = limit;
|
|
account.plan = plan;
|
|
account.note = note;
|
|
cb(account);
|
|
}));
|
|
};
|
|
Store.getPinLimit = function(clientId, data, cb) {
|
|
var s = getStore(data && data.teamId);
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
{
|
|
return void s.rpc.getLimit((function(e, limit, plan, note) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
var data = s.id ? {} : account;
|
|
data.limit = limit;
|
|
data.plan = plan;
|
|
data.note = note;
|
|
cb(data);
|
|
}));
|
|
}
|
|
};
|
|
Store.clearOwnedChannel = function(clientId, data, cb) {
|
|
var s = getStore(data && data.teamId);
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
s.rpc.clearOwnedChannel(data.channel, (function(err) {
|
|
cb({
|
|
error: err
|
|
});
|
|
}));
|
|
};
|
|
var myDeletions = {};
|
|
Store.removeOwnedChannel = function(clientId, data, cb) {
|
|
var channel = data;
|
|
var force = false;
|
|
var teamId;
|
|
var reason;
|
|
if (data && typeof data === "object") {
|
|
channel = data.channel;
|
|
force = data.force;
|
|
teamId = data.teamId;
|
|
reason = data.reason;
|
|
}
|
|
if (channel === store.driveChannel && !force) {
|
|
return void cb({
|
|
error: "User drive removal blocked!"
|
|
});
|
|
}
|
|
var s = getStore(teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (Store.channels[channel]) {
|
|
myDeletions[channel] = true;
|
|
}
|
|
s.rpc.removeOwnedChannel(channel, (function(err) {
|
|
if (err) {
|
|
delete myDeletions[channel];
|
|
}
|
|
cb({
|
|
error: err
|
|
});
|
|
}), reason);
|
|
};
|
|
var arePinsSynced = function(cb) {
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
var list = getCanonicalChannelList(false);
|
|
var local = Hash.hashChannelList(list);
|
|
store.rpc.getServerHash((function(e, hash) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
cb(null, hash === local);
|
|
}));
|
|
};
|
|
var resetPins = function(cb) {
|
|
if (!store.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
var list = getCanonicalChannelList(false);
|
|
store.rpc.reset(list, (function(e) {
|
|
if (e) {
|
|
return void cb(e);
|
|
}
|
|
cb(null);
|
|
}));
|
|
};
|
|
Store.uploadComplete = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (data.owned) {
|
|
s.rpc.ownedUploadComplete(data.id, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
return;
|
|
}
|
|
s.rpc.uploadComplete(data.id, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
Store.uploadStatus = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
s.rpc.uploadStatus(data.size, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
Store.uploadCancel = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
s.rpc.uploadCancel(data.size, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
Store.uploadChunk = function(clientId, data, cb) {
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!s.rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
s.rpc.send.unauthenticated("UPLOAD", data.chunk, (function(e, msg) {
|
|
cb({
|
|
error: e,
|
|
msg
|
|
});
|
|
}));
|
|
};
|
|
var initTempRpc = (clientId, cb) => {
|
|
if (store.rpc) {
|
|
return void cb(store.rpc);
|
|
}
|
|
var kp = Crypto.Nacl.sign.keyPair();
|
|
var keys = {
|
|
edPublic: Crypto.Nacl.util.encodeBase64(kp.publicKey),
|
|
edPrivate: Crypto.Nacl.util.encodeBase64(kp.secretKey)
|
|
};
|
|
Pinpad.create(store.network, keys, (function(e, call) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
store.rpc = call;
|
|
cb(call);
|
|
}));
|
|
};
|
|
var initRpc = function(clientId, data, cb) {
|
|
if (!store.loggedIn) {
|
|
return cb();
|
|
}
|
|
if (store.rpc) {
|
|
return void cb(account);
|
|
}
|
|
Pinpad.create(store.network, store.proxy, (function(e, call) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
store.rpc = call;
|
|
store.onRpcReadyEvt.fire();
|
|
Store.getPinLimit(null, null, (function(obj) {
|
|
if (obj.error) {
|
|
console.error(obj.error);
|
|
}
|
|
account.limit = obj.limit;
|
|
account.plan = obj.plan;
|
|
account.note = obj.note;
|
|
cb(obj);
|
|
}));
|
|
}), Cache);
|
|
};
|
|
Store.anonRpcMsg = function(clientId, data, cb) {
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "ANON_RPC_NOT_READY"
|
|
});
|
|
}
|
|
store.anon_rpc.send(data.msg, data.data, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
Store.getFileSize = function(clientId, data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "ANON_RPC_NOT_READY"
|
|
});
|
|
}
|
|
var channelId = data.channel || Hash.hrefToHexChannelId(data.href, data.password);
|
|
store.anon_rpc.send("GET_FILE_SIZE", channelId, (function(e, response) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
if (response && response.length && typeof response[0] === "number") {
|
|
if (response[0] === 0) {
|
|
Cache.clearChannel(channelId);
|
|
}
|
|
return void cb({
|
|
size: response[0]
|
|
});
|
|
} else {
|
|
cb({
|
|
error: "INVALID_RESPONSE"
|
|
});
|
|
}
|
|
}));
|
|
};
|
|
Store.isNewChannel = function(clientId, data, cb) {
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "ANON_RPC_NOT_READY"
|
|
});
|
|
}
|
|
var channelId = data.channel || Hash.hrefToHexChannelId(data.href, data.password);
|
|
store.anon_rpc.send("IS_NEW_CHANNEL", channelId, (function(e, response) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
if (response && response.length && typeof response[0] === "object") {
|
|
if (response[0].isNew) {
|
|
Cache.clearChannel(channelId);
|
|
}
|
|
return void cb(response[0]);
|
|
} else {
|
|
cb({
|
|
error: "INVALID_RESPONSE"
|
|
});
|
|
}
|
|
}));
|
|
};
|
|
Store.getMultipleFileSize = function(clientId, data, cb) {
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "ANON_RPC_NOT_READY"
|
|
});
|
|
}
|
|
if (!Array.isArray(data.files)) {
|
|
return void cb({
|
|
error: "INVALID_FILE_LIST"
|
|
});
|
|
}
|
|
store.anon_rpc.send("GET_MULTIPLE_FILE_SIZE", data.files, (function(e, res) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
if (res && res.length && typeof res[0] === "object") {
|
|
cb({
|
|
size: res[0]
|
|
});
|
|
} else {
|
|
cb({
|
|
error: "UNEXPECTED_RESPONSE"
|
|
});
|
|
}
|
|
}));
|
|
};
|
|
Store.getDeletedPads = function(clientId, data, cb) {
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "ANON_RPC_NOT_READY"
|
|
});
|
|
}
|
|
var list = data && data.list || getCanonicalChannelList(true);
|
|
if (!Array.isArray(list)) {
|
|
return void cb({
|
|
error: "INVALID_FILE_LIST"
|
|
});
|
|
}
|
|
store.anon_rpc.send("GET_DELETED_PADS", list, (function(e, res) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
if (res && res.length && Array.isArray(res[0])) {
|
|
cb(res[0]);
|
|
} else {
|
|
cb({
|
|
error: "UNEXPECTED_RESPONSE"
|
|
});
|
|
}
|
|
}));
|
|
};
|
|
var initAnonRpc = function(clientId, data, cb) {
|
|
if (store.anon_rpc) {
|
|
return void cb();
|
|
}
|
|
Rpc.createAnonymous(store.network, (function(e, call) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
store.anon_rpc = call;
|
|
cb();
|
|
}));
|
|
};
|
|
var getAllStores = Store.getAllStores = function() {
|
|
if (!store.proxy || !store.manager) {
|
|
return [];
|
|
}
|
|
var stores = [ store ];
|
|
var teamModule = store.modules["team"];
|
|
if (teamModule) {
|
|
var teams = teamModule.getTeams().map((function(id) {
|
|
return teamModule.getTeam(id);
|
|
}));
|
|
Array.prototype.push.apply(stores, teams);
|
|
}
|
|
return stores;
|
|
};
|
|
Store.getUserColor = function() {
|
|
var color = Util.find(store, [ "proxy", "settings", "general", "cursor", "color" ]);
|
|
if (!color) {
|
|
color = Util.getRandomColor(true);
|
|
Store.setAttribute(null, {
|
|
attr: [ "general", "cursor", "color" ],
|
|
value: color
|
|
}, (function() {}));
|
|
}
|
|
return color;
|
|
};
|
|
Store.getMetadata = function(clientId, app, cb) {
|
|
var proxy = store.proxy || {};
|
|
var disableThumbnails = Util.find(proxy, [ "settings", "general", "disableThumbnails" ]);
|
|
var teams = store.modules["team"] && store.modules["team"].getTeamsData(app) || {};
|
|
if (!proxy.uid) {
|
|
store.noDriveUid = store.noDriveUid || Hash.createChannelId();
|
|
}
|
|
var metadata = {
|
|
user: {
|
|
name: proxy[Constants.displayNameKey] || store.noDriveName || "",
|
|
uid: proxy.uid || store.noDriveUid,
|
|
avatar: Util.find(proxy, [ "profile", "avatar" ]),
|
|
profile: Util.find(proxy, [ "profile", "view" ]),
|
|
color: Store.getUserColor(),
|
|
notifications: Util.find(proxy, [ "mailboxes", "notifications", "channel" ]),
|
|
curvePublic: proxy.curvePublic
|
|
},
|
|
priv: {
|
|
clientId,
|
|
edPublic: proxy.edPublic,
|
|
friends: proxy.friends || {},
|
|
settings: proxy.settings || NEW_USER_SETTINGS,
|
|
thumbnails: disableThumbnails === false,
|
|
isDriveOwned: Boolean(Util.find(store, [ "driveMetadata", "owners" ])),
|
|
driveChannel: store.driveChannel,
|
|
pendingFriends: proxy.friends_pending || {},
|
|
supportPrivateKey: Util.find(proxy, [ "mailboxes", "supportadmin", "keys", "curvePrivate" ]),
|
|
accountName: proxy.login_name || "",
|
|
offline: store.proxy && store.offline,
|
|
teams,
|
|
plan: store.ready ? account.plan || "" : undefined,
|
|
mutedChannels: proxy.mutedChannels
|
|
}
|
|
};
|
|
cb(JSON.parse(JSON.stringify(metadata)));
|
|
return metadata;
|
|
};
|
|
Store.onMaintenanceUpdate = function() {
|
|
let origin = ApiConfig.httpUnsafeOrigin;
|
|
Util.fetchApi(origin, "broadcast", true, (Broadcast => {
|
|
if (!Broadcast) {
|
|
return;
|
|
}
|
|
broadcast([], "UNIVERSAL_EVENT", {
|
|
type: "broadcast",
|
|
data: {
|
|
ev: "MAINTENANCE",
|
|
data: Broadcast.maintenance
|
|
}
|
|
});
|
|
}));
|
|
};
|
|
Store.onSurveyUpdate = function() {
|
|
let origin = ApiConfig.httpUnsafeOrigin;
|
|
Util.fetchApi(origin, "broadcast", true, (Broadcast => {
|
|
broadcast([], "UNIVERSAL_EVENT", {
|
|
type: "broadcast",
|
|
data: {
|
|
ev: "SURVEY",
|
|
data: Broadcast.surveyURL
|
|
}
|
|
});
|
|
}));
|
|
};
|
|
var makePad = function(href, roHref, title) {
|
|
var now = +new Date;
|
|
return {
|
|
href,
|
|
roHref,
|
|
atime: now,
|
|
ctime: now,
|
|
title: title || UserObject.getDefaultName(Hash.parsePadUrl(href))
|
|
};
|
|
};
|
|
Store.addPad = function(clientId, data, cb) {
|
|
if (!data.href && !data.roHref) {
|
|
return void cb({
|
|
error: "NO_HREF"
|
|
});
|
|
}
|
|
var secret;
|
|
if (!data.roHref) {
|
|
var parsed = Hash.parsePadUrl(data.href);
|
|
if (parsed.hashData.type === "pad") {
|
|
secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
|
data.roHref = "/" + parsed.type + "/#" + Hash.getViewHashFromKeys(secret);
|
|
}
|
|
}
|
|
var pad = makePad(data.href, data.roHref, data.title);
|
|
if (data.owners) {
|
|
pad.owners = data.owners;
|
|
}
|
|
if (data.expire) {
|
|
pad.expire = data.expire;
|
|
}
|
|
if (data.password) {
|
|
pad.password = data.password;
|
|
}
|
|
if (data.channel || secret) {
|
|
pad.channel = data.channel || secret.channel;
|
|
}
|
|
if (data.readme) {
|
|
pad.readme = 1;
|
|
}
|
|
if (data.teamId === -1) {
|
|
data.teamId = undefined;
|
|
}
|
|
var s = getStore(data.teamId);
|
|
if (!s || !s.manager) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
s.manager.addPad(data.path, pad, (function(e) {
|
|
if (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
getAllStores().forEach((function(_s) {
|
|
var send = _s.id ? _s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
}));
|
|
onSync(data.teamId, cb);
|
|
}));
|
|
};
|
|
var getOwnedPads = function(account) {
|
|
var list = [];
|
|
if (account) {
|
|
if (store.proxy.todo) {
|
|
list.push(Hash.hrefToHexChannelId("/todo/#" + store.proxy.todo, null));
|
|
}
|
|
if (store.proxy.profile && store.proxy.profile.edit) {
|
|
list.push(Hash.hrefToHexChannelId("/profile/#" + store.proxy.profile.edit, null));
|
|
}
|
|
if (store.proxy.mailboxes) {
|
|
Object.keys(store.proxy.mailboxes || {}).forEach((function(id) {
|
|
if (id === "supportadmin") {
|
|
return;
|
|
}
|
|
var m = store.proxy.mailboxes[id];
|
|
list.push(m.channel);
|
|
}));
|
|
}
|
|
} else {
|
|
list = store.manager.getChannelsList("owned");
|
|
}
|
|
return list.filter((function(channel) {
|
|
if (typeof channel !== "string") {
|
|
return;
|
|
}
|
|
return [ 32, 48 ].indexOf(channel.length) !== -1;
|
|
}));
|
|
};
|
|
var removeOwnedPads = function(account, waitFor) {
|
|
var edPublic = Util.find(store, [ "proxy", "edPublic" ]);
|
|
var ownedPads = getOwnedPads(account);
|
|
var sem = Saferphore.create(10);
|
|
var deleteChannel = function(c) {
|
|
if (account) {
|
|
return;
|
|
}
|
|
var all = store.manager.findChannel(c);
|
|
all.forEach((function(d) {
|
|
var p = store.manager.findFile(d.id);
|
|
store.manager.delete({
|
|
paths: p
|
|
});
|
|
}));
|
|
};
|
|
ownedPads.forEach((function(c) {
|
|
var w = waitFor();
|
|
sem.take((function(give) {
|
|
var otherOwners = false;
|
|
nThen((function(_w) {
|
|
if (c.length !== 32) {
|
|
return;
|
|
}
|
|
Store.anonRpcMsg(null, {
|
|
msg: "GET_METADATA",
|
|
data: c
|
|
}, _w((function(obj) {
|
|
if (obj && obj.error) {
|
|
give();
|
|
w();
|
|
return void _w.abort();
|
|
}
|
|
var md = obj[0];
|
|
if (!Object.keys(md || {}).length) {
|
|
deleteChannel(c);
|
|
give();
|
|
w();
|
|
return void _w.abort();
|
|
}
|
|
var isOwner = md && Array.isArray(md.owners) && md.owners.indexOf(edPublic) !== -1;
|
|
if (!isOwner) {
|
|
give();
|
|
w();
|
|
return void _w.abort();
|
|
}
|
|
otherOwners = md.owners.some((function(ed) {
|
|
return ed !== edPublic;
|
|
}));
|
|
})));
|
|
})).nThen((function(_w) {
|
|
if (otherOwners) {
|
|
Store.setPadMetadata(null, {
|
|
channel: c,
|
|
command: "RM_OWNERS",
|
|
value: [ edPublic ]
|
|
}, _w());
|
|
return;
|
|
}
|
|
store.rpc.removeOwnedChannel(c, _w((function(err) {
|
|
if (err) {
|
|
return void console.error(err);
|
|
}
|
|
deleteChannel(c);
|
|
})));
|
|
})).nThen((function() {
|
|
give();
|
|
w();
|
|
}));
|
|
}));
|
|
}));
|
|
};
|
|
Store.removeOwnedPads = function(clientId, data, cb) {
|
|
var edPublic = store.proxy.edPublic;
|
|
if (!edPublic) {
|
|
return void cb({
|
|
error: "NOT_LOGGED_IN"
|
|
});
|
|
}
|
|
nThen((function(waitFor) {
|
|
removeOwnedPads(false, waitFor);
|
|
})).nThen(cb);
|
|
};
|
|
Store.deleteAccount = function(clientId, data, cb) {
|
|
var edPublic = store.proxy.edPublic;
|
|
var blockKeys = data && data.keys;
|
|
var auth = data && data.auth;
|
|
Store.anonRpcMsg(clientId, {
|
|
msg: "GET_METADATA",
|
|
data: store.driveChannel
|
|
}, (function(data) {
|
|
var metadata = data[0];
|
|
if (metadata && metadata.owners && metadata.owners.length === 1 && metadata.owners.indexOf(edPublic) !== -1) {
|
|
nThen((function(waitFor) {
|
|
Block.checkRights({
|
|
auth,
|
|
blockKeys
|
|
}, waitFor((function(err) {
|
|
if (err) {
|
|
waitFor.abort();
|
|
console.error(err);
|
|
return void cb({
|
|
error: "INVALID_CODE"
|
|
});
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
globalThis.accountDeletion = clientId;
|
|
store.proxy[Constants.tokenKey] = "DELETED";
|
|
onSync(null, waitFor());
|
|
})).nThen((function(waitFor) {
|
|
store.rpc.removePins(waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
store.ownDeletion = true;
|
|
Store.removeOwnedChannel(clientId, {
|
|
channel: store.driveChannel,
|
|
force: true
|
|
}, waitFor());
|
|
})).nThen((function(waitFor) {
|
|
if (!blockKeys) {
|
|
return;
|
|
}
|
|
Block.removeLoginBlock({
|
|
reason: "ARCHIVE_OWNED",
|
|
auth,
|
|
edPublic,
|
|
blockKeys
|
|
}, waitFor((function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
})));
|
|
})).nThen((function(waitFor) {
|
|
removeOwnedPads(true, waitFor);
|
|
})).nThen((function() {
|
|
broadcast([ clientId ], "DRIVE_DELETED", "ARCHIVE_OWNED");
|
|
postMessage(clientId, "DELETE_ACCOUNT", "DELETED", (function() {}));
|
|
store.network.disconnect();
|
|
cb({
|
|
state: true
|
|
});
|
|
}));
|
|
return;
|
|
}
|
|
var toSign = {
|
|
intent: "Please delete my account."
|
|
};
|
|
toSign.drive = store.driveChannel;
|
|
toSign.edPublic = edPublic;
|
|
var signKey = Crypto.Nacl.util.decodeBase64(store.proxy.edPrivate);
|
|
var proof = Crypto.Nacl.sign.detached(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), signKey);
|
|
var check = Crypto.Nacl.sign.detached.verify(Crypto.Nacl.util.decodeUTF8(Sortify(toSign)), proof, Crypto.Nacl.util.decodeBase64(edPublic));
|
|
if (!check) {
|
|
console.error("signed message failed verification");
|
|
}
|
|
var proofTxt = Crypto.Nacl.util.encodeBase64(proof);
|
|
cb({
|
|
proof: proofTxt,
|
|
toSign: JSON.parse(Sortify(toSign))
|
|
});
|
|
}));
|
|
};
|
|
Store.migrateAnonDrive = function(clientId, data, cb) {
|
|
var hash = data.anonHash;
|
|
Merge.anonDriveIntoUser(store, hash, cb);
|
|
};
|
|
Store.setDisplayName = function(clientId, value, cb) {
|
|
if (!store.proxy) {
|
|
store.noDriveName = value;
|
|
broadcast([ clientId ], "UPDATE_METADATA");
|
|
return void cb();
|
|
}
|
|
if (store.modules["profile"]) {
|
|
store.modules["profile"].setName(value);
|
|
}
|
|
store.proxy[Constants.displayNameKey] = value;
|
|
broadcast([ clientId ], "UPDATE_METADATA");
|
|
Messaging.updateMyData(store);
|
|
onSync(null, cb);
|
|
};
|
|
Store.resetDrive = function(clientId, data, cb) {
|
|
nThen((function(waitFor) {
|
|
removeOwnedPads(waitFor);
|
|
})).nThen((function() {
|
|
store.proxy.drive = store.userObject.getStructure();
|
|
sendDriveEvent("DRIVE_CHANGE", {
|
|
path: [ "drive", "filesData" ]
|
|
}, clientId);
|
|
onSync(null, cb);
|
|
}));
|
|
};
|
|
Store.setPadAttribute = function(clientId, data, cb) {
|
|
nThen((function(waitFor) {
|
|
getAllStores().forEach((function(s) {
|
|
s.manager.setPadAttribute(data, waitFor((function() {
|
|
var send = s.id ? s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
onSync(s.id, waitFor());
|
|
})));
|
|
}));
|
|
})).nThen(cb);
|
|
};
|
|
Store.getPadAttribute = function(clientId, data, cb) {
|
|
var res = {};
|
|
nThen((function(waitFor) {
|
|
getAllStores().forEach((function(s) {
|
|
s.manager.getPadAttribute(data, waitFor((function(err, val) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
if (!val || typeof val !== "object") {
|
|
return void console.error("Not an object!");
|
|
}
|
|
if (!res.value || res.atime < val.atime) {
|
|
res.atime = val.atime;
|
|
res.value = val.value;
|
|
}
|
|
})));
|
|
}));
|
|
})).nThen((function() {
|
|
cb(res.value);
|
|
}));
|
|
};
|
|
var getAttributeObject = function(attr) {
|
|
if (typeof attr === "string") {
|
|
console.error("DEPRECATED: use setAttribute with an array, not a string");
|
|
return {
|
|
path: [ "settings" ],
|
|
obj: store.proxy.settings,
|
|
key: attr
|
|
};
|
|
}
|
|
if (!Array.isArray(attr)) {
|
|
return void console.error("Attribute must be string or array");
|
|
}
|
|
if (attr.length === 0) {
|
|
return void console.error("Attribute can't be empty");
|
|
}
|
|
var obj = store.proxy.settings;
|
|
attr.forEach((function(el, i) {
|
|
if (i === attr.length - 1) {
|
|
return;
|
|
}
|
|
if (!obj[el]) {
|
|
obj[el] = {};
|
|
} else if (typeof obj[el] !== "object") {
|
|
return void console.error("Wrong attribute");
|
|
}
|
|
obj = obj[el];
|
|
}));
|
|
return {
|
|
path: [ "settings" ].concat(attr),
|
|
obj,
|
|
key: attr[attr.length - 1]
|
|
};
|
|
};
|
|
Store.setAttribute = function(clientId, data, cb) {
|
|
try {
|
|
var object = getAttributeObject(data.attr);
|
|
object.obj[object.key] = data.value;
|
|
} catch (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
onSync(null, (function() {
|
|
cb();
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
};
|
|
Store.getAttribute = function(clientId, data, cb) {
|
|
var object;
|
|
try {
|
|
object = getAttributeObject(data.attr);
|
|
} catch (e) {
|
|
return void cb({
|
|
error: e
|
|
});
|
|
}
|
|
cb(object.obj[object.key]);
|
|
};
|
|
Store.listAllTags = function(clientId, data, cb) {
|
|
var tags = {};
|
|
getAllStores().forEach((function(s) {
|
|
var l = s.manager.getTagsList();
|
|
Object.keys(l).forEach((function(tag) {
|
|
tags[tag] = (tags[tag] || 0) + l[tag];
|
|
}));
|
|
}));
|
|
cb(tags);
|
|
};
|
|
Store.getTemplates = function(clientId, data, cb) {
|
|
var res = [];
|
|
var channels = [];
|
|
getAllStores().forEach((function(s) {
|
|
var templateFiles = s.userObject.getFiles([ "template" ]);
|
|
templateFiles.forEach((function(f) {
|
|
var data = s.userObject.getFileData(f);
|
|
if (channels.indexOf(data.channel) !== -1) {
|
|
return;
|
|
}
|
|
channels.push(data.channel);
|
|
res.push(JSON.parse(JSON.stringify(data)));
|
|
}));
|
|
}));
|
|
cb(res);
|
|
};
|
|
Store.incrementTemplateUse = function(clientId, href) {
|
|
getAllStores().forEach((function(s) {
|
|
s.userObject.getPadAttribute(href, "used", (function(err, data) {
|
|
if (err) {
|
|
return;
|
|
}
|
|
var used = typeof data === "number" ? ++data : 1;
|
|
s.userObject.setPadAttribute(href, "used", used);
|
|
}));
|
|
}));
|
|
};
|
|
Store.isOnlyInSharedFolder = function(clientId, channel, cb) {
|
|
var res = false;
|
|
var isInMainDrive = function(obj) {
|
|
return !obj.fId;
|
|
};
|
|
getAllStores().some((function(s) {
|
|
var _res = s.manager.findChannel(channel);
|
|
if (!_res.length) {
|
|
return;
|
|
}
|
|
if (_res.some(isInMainDrive)) {
|
|
res = false;
|
|
return true;
|
|
}
|
|
res = true;
|
|
}));
|
|
return cb(res);
|
|
};
|
|
Store.moveToTrash = function(clientId, data, cb) {
|
|
var href = Hash.getRelativeHref(data.href);
|
|
var allErrors = true;
|
|
nThen((function(waitFor) {
|
|
getAllStores().forEach((function(s) {
|
|
var deleted = s.userObject.forget(href);
|
|
if (!deleted) {
|
|
return;
|
|
}
|
|
allErrors = false;
|
|
var send = s.id ? s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
onSync(s.id, waitFor());
|
|
}));
|
|
})).nThen((function() {
|
|
cb({
|
|
error: allErrors ? "FORBIDDEN" : undefined
|
|
});
|
|
}));
|
|
};
|
|
Store.setPadTitle = function(clientId, data, cb) {
|
|
onReadyEvt.reg((function() {
|
|
var title = data.title;
|
|
var href = data.href;
|
|
var channel = data.channel;
|
|
var p = Hash.parsePadUrl(href);
|
|
var h = p.hashData;
|
|
if (title.trim() === "") {
|
|
title = UserObject.getDefaultName(p);
|
|
}
|
|
if (AppConfig.disableAnonymousStore && !store.loggedIn) {
|
|
return void cb({
|
|
notStored: true
|
|
});
|
|
}
|
|
if (p.type === "debug") {
|
|
return void cb({
|
|
notStored: true
|
|
});
|
|
}
|
|
var channelData = Store.channels && Store.channels[channel];
|
|
var owners;
|
|
if (channelData && channelData.wc && channel === channelData.wc.id) {
|
|
owners = channelData.data.owners || undefined;
|
|
}
|
|
if (data.owners) {
|
|
owners = data.owners;
|
|
}
|
|
var expire;
|
|
if (channelData && channelData.wc && channel === channelData.wc.id) {
|
|
expire = +channelData.data.expire || undefined;
|
|
}
|
|
if (data.expire) {
|
|
expire = data.expire;
|
|
}
|
|
if (data.teamId) {
|
|
data.teamId = Number(data.teamId);
|
|
}
|
|
var allData = [];
|
|
var sendTo = [];
|
|
var inTargetDrive, inMyDrive;
|
|
getAllStores().forEach((function(s) {
|
|
if (h.mode === "edit" && s.id && !s.secondaryKey) {
|
|
return;
|
|
}
|
|
var res = s.manager.findChannel(channel, true);
|
|
if (res.length) {
|
|
sendTo.push(s.id);
|
|
}
|
|
if (!s.id && (!data.teamId || data.teamId === -1) || Number(s.id) === data.teamId) {
|
|
if (!inTargetDrive) {
|
|
inTargetDrive = res.length;
|
|
}
|
|
}
|
|
if (!s.id) {
|
|
inMyDrive = res.length;
|
|
}
|
|
Array.prototype.push.apply(allData, res);
|
|
}));
|
|
var contains = allData.length !== 0;
|
|
if (store.offline && !contains) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
} else if (store.offline) {
|
|
return void cb();
|
|
}
|
|
allData.forEach((function(obj) {
|
|
var pad = obj.data;
|
|
pad.atime = +new Date;
|
|
pad.title = title;
|
|
if (owners || h.type !== "file") {
|
|
pad.owners = owners;
|
|
}
|
|
pad.expire = expire;
|
|
if (pad.readme) {
|
|
delete pad.readme;
|
|
Feedback.send("OPEN_README");
|
|
}
|
|
if (h.mode === "view") {
|
|
return;
|
|
}
|
|
if (!pad.href) {
|
|
obj.userObject.restoreHref(href);
|
|
}
|
|
obj.userObject.setHref(channel, null, href);
|
|
}));
|
|
if (!contains || data.forceSave && !inTargetDrive) {
|
|
var autoStore = Util.find(store.proxy, [ "settings", "general", "autostore" ]);
|
|
if (autoStore !== 1 && !data.forceSave && !data.path) {
|
|
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
|
autoStore
|
|
});
|
|
return void cb({
|
|
notStored: true
|
|
});
|
|
} else {
|
|
var roHref;
|
|
if (h.mode === "view") {
|
|
roHref = href;
|
|
href = undefined;
|
|
}
|
|
Store.addPad(clientId, {
|
|
teamId: data.teamId,
|
|
href,
|
|
roHref,
|
|
channel,
|
|
title,
|
|
owners,
|
|
expire,
|
|
password: data.password,
|
|
path: data.path
|
|
}, cb);
|
|
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
|
stored: true,
|
|
inMyDrive: inMyDrive || !contains && !data.teamId
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
sendTo.forEach((function(teamId) {
|
|
var send = teamId ? getStore(teamId).sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
}));
|
|
postMessage(clientId, "AUTOSTORE_DISPLAY_POPUP", {
|
|
stored: true,
|
|
inMyDrive
|
|
});
|
|
nThen((function(waitFor) {
|
|
sendTo.forEach((function(teamId) {
|
|
onSync(teamId, waitFor());
|
|
}));
|
|
})).nThen(cb);
|
|
}));
|
|
};
|
|
Store.getSecureFilesList = function(clientId, query, cb) {
|
|
var list = {};
|
|
var types = query.types;
|
|
var where = query.where;
|
|
var filter = query.filter || {};
|
|
var isFiltered = function(type, data) {
|
|
var filtered;
|
|
var fType = filter.fileType || [];
|
|
if (type === "file" && fType.length) {
|
|
if (!data.fileType) {
|
|
return true;
|
|
}
|
|
filtered = !fType.some((function(t) {
|
|
return data.fileType.indexOf(t) === 0;
|
|
}));
|
|
}
|
|
return filtered;
|
|
};
|
|
var channels = [];
|
|
getAllStores().forEach((function(s) {
|
|
s.manager.getSecureFilesList(where).forEach((function(obj) {
|
|
var data = obj.data;
|
|
if (channels.indexOf(data.channel || data.id) !== -1) {
|
|
return;
|
|
}
|
|
var id = obj.id;
|
|
if (data.channel) {
|
|
channels.push(data.channel || data.id);
|
|
}
|
|
if (data.static) {
|
|
if (types.indexOf("link") !== -1) {
|
|
list[id] = data;
|
|
}
|
|
return;
|
|
}
|
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
|
if ((!types || types.length === 0 || types.indexOf(parsed.type) !== -1) && !isFiltered(parsed.type, data)) {
|
|
list[id] = data;
|
|
}
|
|
}));
|
|
}));
|
|
cb(list);
|
|
};
|
|
Store.getPadData = function(clientId, id, cb) {
|
|
var res = {};
|
|
getAllStores().some((function(s) {
|
|
var d = s.userObject.getFileData(id);
|
|
if (!d.roHref && !d.href) {
|
|
return;
|
|
}
|
|
res = d;
|
|
return true;
|
|
}));
|
|
cb(res);
|
|
};
|
|
Store.getPadDataFromChannel = function(clientId, obj, cb) {
|
|
var channel = obj.channel;
|
|
var edit = obj.edit;
|
|
var isFile = obj.file;
|
|
var res;
|
|
var viewRes;
|
|
getAllStores().some((function(s) {
|
|
var chans = s.manager.findChannel(channel);
|
|
if (!Array.isArray(chans)) {
|
|
return;
|
|
}
|
|
return chans.some((function(pad) {
|
|
if (!pad || !pad.data) {
|
|
return;
|
|
}
|
|
var data = pad.data;
|
|
if (edit && data.href || !edit && data.roHref || isFile) {
|
|
res = data;
|
|
return true;
|
|
}
|
|
if (edit && !viewRes && data.roHref) {
|
|
viewRes = data;
|
|
}
|
|
}));
|
|
}));
|
|
var result = res || viewRes;
|
|
if (!result && store.offline) {
|
|
onReadyEvt.reg((function() {
|
|
Store.getPadDataFromChannel(clientId, obj, cb);
|
|
}));
|
|
return;
|
|
}
|
|
cb(result || {});
|
|
};
|
|
Store.checkDeletedPad = function(channel, cb) {
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
Store.getPadDataFromChannel(null, {
|
|
channel,
|
|
isFile: true
|
|
}, (function(res) {
|
|
if (typeof cb === "function") {
|
|
setTimeout(cb);
|
|
}
|
|
if (Object.keys(res).length) {
|
|
return;
|
|
}
|
|
broadcast([], "CHANNEL_DELETED", channel);
|
|
}));
|
|
};
|
|
Store.answerFriendRequest = function(clientId, obj, cb) {
|
|
var value = obj.value;
|
|
var data = obj.data;
|
|
if (data.type !== "notifications") {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var hash = data.content.hash;
|
|
var msg = data.content.msg;
|
|
var dismiss = function(cb) {
|
|
cb = cb || function() {};
|
|
store.mailbox.dismiss({
|
|
hash,
|
|
type: "notifications"
|
|
}, cb);
|
|
};
|
|
if (value) {
|
|
Messaging.acceptFriendRequest(store, msg.content.user, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb(obj);
|
|
}
|
|
Messaging.addToFriendList({
|
|
proxy: store.proxy,
|
|
realtime: store.realtime,
|
|
pinPads: function(data, cb) {
|
|
Store.pinPads(null, data, cb);
|
|
}
|
|
}, msg.content.user, (function(err) {
|
|
if (store.messenger) {
|
|
store.messenger.onFriendAdded(msg.content.user);
|
|
}
|
|
broadcast([], "UPDATE_METADATA");
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
dismiss(cb);
|
|
}));
|
|
}));
|
|
return;
|
|
}
|
|
Messaging.declineFriendRequest(store, msg.content.user, (function(obj) {
|
|
broadcast([], "UPDATE_METADATA");
|
|
cb(obj);
|
|
}));
|
|
dismiss();
|
|
};
|
|
Store.sendFriendRequest = function(clientId, data, cb) {
|
|
var friend = Messaging.getFriend(store.proxy, data.curvePublic);
|
|
if (friend) {
|
|
return void cb({
|
|
error: "ALREADY_FRIEND"
|
|
});
|
|
}
|
|
if (!data.notifications || !data.curvePublic) {
|
|
return void cb({
|
|
error: "INVALID_USER"
|
|
});
|
|
}
|
|
store.proxy.friends_pending = store.proxy.friends_pending || {};
|
|
var p = store.proxy.friends_pending[data.curvePublic];
|
|
if (p) {
|
|
return void cb({
|
|
error: "ALREADY_SENT"
|
|
});
|
|
}
|
|
store.proxy.friends_pending[data.curvePublic] = {
|
|
time: +new Date,
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
};
|
|
broadcast([], "UPDATE_METADATA");
|
|
store.mailbox.sendTo("FRIEND_REQUEST", {
|
|
user: Messaging.createData(store.proxy)
|
|
}, {
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
}, (function(obj) {
|
|
cb(obj);
|
|
}));
|
|
};
|
|
Store.cancelFriendRequest = function(data, cb) {
|
|
if (!data.curvePublic || !data.notifications) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var proxy = store.proxy;
|
|
var f = Messaging.getFriend(proxy, data.curvePublic);
|
|
if (f) {
|
|
console.error("You can't cancel an accepted friend request");
|
|
return void cb({
|
|
error: "ALREADY_FRIEND"
|
|
});
|
|
}
|
|
var pending = Util.find(store, [ "proxy", "friends_pending" ]) || {};
|
|
if (!pending) {
|
|
return void cb();
|
|
}
|
|
store.mailbox.sendTo("CANCEL_FRIEND_REQUEST", {
|
|
user: Messaging.createData(store.proxy)
|
|
}, {
|
|
channel: data.notifications,
|
|
curvePublic: data.curvePublic
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return void cb(obj);
|
|
}
|
|
delete store.proxy.friends_pending[data.curvePublic];
|
|
broadcast([], "UPDATE_METADATA");
|
|
onSync(null, (function() {
|
|
cb(obj);
|
|
}));
|
|
}));
|
|
};
|
|
Store.anonGetPreviewContent = function(clientId, data, cb) {
|
|
Team.anonGetPreviewContent({
|
|
store
|
|
}, data, cb);
|
|
};
|
|
Store.getStrongerHash = function(clientId, data, _cb) {
|
|
var cb = Util.once(_cb);
|
|
var found = getAllStores().some((function(s) {
|
|
var stronger = s.manager.getEditHash(data.channel);
|
|
if (stronger) {
|
|
cb(stronger);
|
|
return true;
|
|
}
|
|
}));
|
|
if (!found) {
|
|
cb();
|
|
}
|
|
};
|
|
Store.universal = {
|
|
execCommand: function(clientId, obj, cb) {
|
|
var todo = function() {
|
|
var type = obj.type;
|
|
var data = obj.data;
|
|
if (store.modules[type]) {
|
|
store.modules[type].execCommand(clientId, data, cb);
|
|
} else {
|
|
return void cb({
|
|
error: type + " is disabled"
|
|
});
|
|
}
|
|
};
|
|
if ([ "team", "calendar" ].indexOf(obj.type) !== -1) {
|
|
return void todo();
|
|
}
|
|
if (!store.proxy) {
|
|
return void todo();
|
|
}
|
|
onReadyEvt.reg(todo);
|
|
}
|
|
};
|
|
var loadUniversal = function(Module, type, waitFor, clientId) {
|
|
if (store.modules[type]) {
|
|
return;
|
|
}
|
|
store.modules[type] = Module.init({
|
|
Store,
|
|
store,
|
|
updateLoadingProgress: function(data) {
|
|
data.type = "team";
|
|
postMessage(clientId, "LOADING_DRIVE", data);
|
|
},
|
|
updateMetadata: function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
},
|
|
pinPads: function(data, cb) {
|
|
Store.pinPads(null, data, cb);
|
|
},
|
|
unpinPads: function(data, cb) {
|
|
Store.unpinPads(null, data, cb);
|
|
}
|
|
}, waitFor, (function(ev, data, clients) {
|
|
clients.forEach((function(cId) {
|
|
postMessage(cId, "UNIVERSAL_EVENT", {
|
|
type,
|
|
data: {
|
|
ev,
|
|
data
|
|
}
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
Store.onlyoffice = {
|
|
execCommand: function(clientId, data, cb) {
|
|
if (!store.onlyoffice) {
|
|
return void cb({
|
|
error: "OnlyOffice is disabled"
|
|
});
|
|
}
|
|
store.onlyoffice.execCommand(clientId, data, cb);
|
|
}
|
|
};
|
|
Store.mailbox = {
|
|
execCommand: function(clientId, data, cb) {
|
|
onReadyEvt.reg((function() {
|
|
if (!store.mailbox) {
|
|
return void cb({
|
|
error: "Mailbox is disabled"
|
|
});
|
|
}
|
|
store.mailbox.execCommand(clientId, data, cb);
|
|
}));
|
|
}
|
|
};
|
|
Store.adminRpc = function(clientId, data, cb) {
|
|
store.rpc.adminRpc(data, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
Store.addAdminMailbox = function(clientId, data, cb) {
|
|
var priv = data && data.priv;
|
|
var pub = Hash.getBoxPublicFromSecret(priv);
|
|
var isNewSupport = data && data.version === 2;
|
|
if (!priv || !pub) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var channel = Hash.getChannelIdFromKey(pub);
|
|
var mailboxes = store.proxy.mailboxes = store.proxy.mailboxes || {};
|
|
var key = isNewSupport ? "supportteam" : "supportadmin";
|
|
var box = mailboxes[key] = {
|
|
channel,
|
|
viewed: [],
|
|
lastKnownHash: data.lastKnownHash || "",
|
|
keys: {
|
|
curvePublic: pub,
|
|
curvePrivate: priv
|
|
}
|
|
};
|
|
Store.pinPads(null, [ channel ], (function() {}));
|
|
store.mailbox.open(key, box, (function() {
|
|
console.log("ready");
|
|
}));
|
|
onSync(null, cb);
|
|
};
|
|
var channels = Store.channels = store.channels = {};
|
|
Store.getSnapshot = function(clientId, data, cb) {
|
|
Store.getHistoryRange(clientId, {
|
|
cpCount: 1,
|
|
channel: data.channel,
|
|
lastKnownHash: data.hash
|
|
}, cb);
|
|
};
|
|
var getVersionHash = function(clientId, data) {
|
|
var validateKey;
|
|
var fakeNetflux = Hash.createChannelId();
|
|
nThen((function(waitFor) {
|
|
Store.getPadMetadata(null, {
|
|
channel: data.channel
|
|
}, waitFor((function(md) {
|
|
if (md && md.rejected) {
|
|
postMessage(clientId, "PAD_ERROR", {
|
|
type: "ERESTRICTED"
|
|
});
|
|
waitFor.abort();
|
|
return;
|
|
}
|
|
validateKey = md.validateKey;
|
|
})));
|
|
})).nThen((function() {
|
|
Store.getHistoryRange(clientId, {
|
|
cpCount: 1,
|
|
channel: data.channel,
|
|
lastKnownHash: data.versionHash
|
|
}, (function(obj) {
|
|
if (obj && obj.error) {
|
|
postMessage(clientId, "PAD_ERROR", obj.error);
|
|
return;
|
|
}
|
|
var msgs = obj.messages || [];
|
|
if (msgs.length && msgs[msgs.length - 1].serverHash !== data.versionHash) {
|
|
postMessage(clientId, "PAD_ERROR", {
|
|
type: "HASH_NOT_FOUND"
|
|
});
|
|
return;
|
|
}
|
|
postMessage(clientId, "PAD_CONNECT", {
|
|
myID: fakeNetflux,
|
|
id: data.channel,
|
|
members: [ fakeNetflux ]
|
|
});
|
|
(obj.messages || []).forEach((function(data) {
|
|
postMessage(clientId, "PAD_MESSAGE", {
|
|
msg: data.msg,
|
|
time: data.time,
|
|
user: fakeNetflux.slice(0, 16)
|
|
});
|
|
}));
|
|
if (validateKey && store.messenger) {
|
|
store.messenger.storeValidateKey(data.channel, validateKey);
|
|
}
|
|
postMessage(clientId, "PAD_READY");
|
|
}));
|
|
}));
|
|
};
|
|
Store.onRejected = function(allowed, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (!Array.isArray(allowed)) {
|
|
return void cb("ERESTRICTED");
|
|
}
|
|
onPadRejectedEvt.fire();
|
|
onReadyEvt.reg((() => {
|
|
if (!store.loggedIn || !store.proxy.edPublic) {
|
|
return void cb("ERESTRICTED");
|
|
}
|
|
var teamModule = store.modules["team"];
|
|
var teams = teamModule && teamModule.getTeams() || [];
|
|
var _store;
|
|
if (allowed.indexOf(store.proxy.edPublic) !== -1) {
|
|
_store = store;
|
|
} else if (teams.some((function(teamId) {
|
|
var ed = Util.find(store, [ "proxy", "teams", teamId, "keys", "drive", "edPublic" ]);
|
|
var edPrivate = Util.find(store, [ "proxy", "teams", teamId, "keys", "drive", "edPrivate" ]);
|
|
if (allowed.indexOf(ed) === -1) {
|
|
return false;
|
|
}
|
|
if (!edPrivate) {
|
|
return false;
|
|
}
|
|
var t = teamModule.getTeam(teamId);
|
|
_store = t;
|
|
return true;
|
|
}))) ;
|
|
var auth = function() {
|
|
if (!_store) {
|
|
return void cb("ERESTRICTED");
|
|
}
|
|
var rpc = _store.rpc;
|
|
if (!rpc) {
|
|
return void cb("ERESTRICTED");
|
|
}
|
|
rpc.send("COOKIE", "", (function(err) {
|
|
cb(err);
|
|
}));
|
|
};
|
|
if (_store && _store.onRpcReadyEvt) {
|
|
_store.onRpcReadyEvt.reg((function() {
|
|
auth();
|
|
}));
|
|
return;
|
|
}
|
|
auth();
|
|
}));
|
|
};
|
|
Store.joinPad = function(clientId, data) {
|
|
if (data.versionHash) {
|
|
return void getVersionHash(clientId, data);
|
|
}
|
|
if (!Hash.isValidChannel(data.channel)) {
|
|
return void postMessage(clientId, "PAD_ERROR", "INVALID_CHAN");
|
|
}
|
|
var isNew = typeof channels[data.channel] === "undefined";
|
|
var channel = channels[data.channel] = channels[data.channel] || {
|
|
queue: [],
|
|
data: {},
|
|
clients: [],
|
|
bcast: function(cmd, data, notMe) {
|
|
channel.clients.forEach((function(cId) {
|
|
if (cId === notMe) {
|
|
return;
|
|
}
|
|
postMessage(cId, cmd, data);
|
|
}));
|
|
},
|
|
history: [],
|
|
pushHistory: function(msg, isCp) {
|
|
if (isCp) {
|
|
channel.history.push("cp|" + msg);
|
|
var i;
|
|
for (i = channel.history.length - 101; i > 0; i--) {
|
|
if (/^cp\|/.test(channel.history[i])) {
|
|
break;
|
|
}
|
|
}
|
|
channel.history = channel.history.slice(Math.max(i, 0));
|
|
return;
|
|
}
|
|
channel.history.push(msg);
|
|
}
|
|
};
|
|
if (channel.clients.indexOf(clientId) === -1) {
|
|
channel.clients.push(clientId);
|
|
}
|
|
if (!isNew && channel.wc) {
|
|
postMessage(clientId, "PAD_CONNECT", {
|
|
myID: channel.wc.myID,
|
|
id: channel.wc.id,
|
|
members: channel.wc.members
|
|
});
|
|
channel.wc.members.forEach((function(m) {
|
|
postMessage(clientId, "PAD_JOIN", m);
|
|
}));
|
|
channel.history.forEach((function(msg) {
|
|
postMessage(clientId, "PAD_MESSAGE", {
|
|
msg: CpNetflux.removeCp(msg),
|
|
user: channel.wc.myID,
|
|
validateKey: channel.data.validateKey
|
|
});
|
|
}));
|
|
postMessage(clientId, "PAD_READY");
|
|
return;
|
|
}
|
|
var onError = function(err) {
|
|
if (err && err.type === "EDELETED" && myDeletions[data.channel]) {
|
|
delete myDeletions[channel];
|
|
err.ownDeletion = true;
|
|
}
|
|
channel.bcast("PAD_ERROR", err);
|
|
if (err && err.type === "EDELETED" && Cache && Cache.clearChannel) {
|
|
Cache.clearChannel(data.channel);
|
|
}
|
|
if ([ "EDELETED", "EEXPIRED", "ERESTRICTED" ].indexOf(err.type) === -1) {
|
|
return;
|
|
}
|
|
Store.leavePad(null, data, (function() {}));
|
|
};
|
|
var conf = {
|
|
Cache: store.neverCache ? undefined : Cache,
|
|
priority: 1,
|
|
onCacheStart: function() {
|
|
postMessage(clientId, "PAD_CACHE");
|
|
},
|
|
onCacheReady: function() {
|
|
console.error("PAD CACHE READY");
|
|
postMessage(clientId, "PAD_CACHE_READY");
|
|
},
|
|
onReady: function(pad) {
|
|
console.error("PAD READY");
|
|
var padData = pad.metadata || {};
|
|
channel.data = padData;
|
|
if (padData && padData.validateKey && store.messenger) {
|
|
store.messenger.storeValidateKey(data.channel, padData.validateKey);
|
|
}
|
|
postMessage(clientId, "PAD_READY", pad.noCache);
|
|
},
|
|
onMessage: function(m, user, validateKey, isCp, hash) {
|
|
channel.lastHash = hash;
|
|
channel.pushHistory(m, isCp);
|
|
channel.bcast("PAD_MESSAGE", {
|
|
user,
|
|
msg: m,
|
|
validateKey
|
|
});
|
|
},
|
|
onJoin: function(m) {
|
|
channel.bcast("PAD_JOIN", m);
|
|
},
|
|
onLeave: function(m) {
|
|
channel.bcast("PAD_LEAVE", m);
|
|
},
|
|
onError,
|
|
onChannelError: onError,
|
|
onRejected: Store.onRejected,
|
|
onConnectionChange: function(info) {
|
|
if (!info.state) {
|
|
channel.bcast("PAD_DISCONNECT");
|
|
}
|
|
},
|
|
onMetadataUpdate: function(metadata) {
|
|
channel.data = metadata || {};
|
|
getAllStores().forEach((function(s) {
|
|
var allData = s.manager.findChannel(data.channel, true);
|
|
allData.forEach((function(obj) {
|
|
obj.data.owners = metadata.owners;
|
|
obj.data.atime = +new Date;
|
|
if (metadata.expire) {
|
|
obj.data.expire = +metadata.expire;
|
|
}
|
|
}));
|
|
var send = s.sendEvent || sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
});
|
|
}));
|
|
channel.bcast("PAD_METADATA", metadata);
|
|
},
|
|
crypto: {
|
|
encrypt: function(m) {
|
|
return m;
|
|
},
|
|
decrypt: function(m) {
|
|
return m;
|
|
}
|
|
},
|
|
noChainPad: true,
|
|
channel: data.channel,
|
|
metadata: data.metadata,
|
|
network: store.network || store.networkPromise,
|
|
websocketURL: NetConfig.getWebsocketURL(),
|
|
onInit: function() {
|
|
onJoinedEvt.fire();
|
|
},
|
|
onConnect: function(wc, sendMessage) {
|
|
channel.sendMessage = function(msg, cId, cb) {
|
|
sendMessage(msg, (function(err) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
channel.lastHash = msg.slice(0, 64);
|
|
channel.pushHistory(CpNetflux.removeCp(msg), /^cp\|/.test(msg));
|
|
channel.bcast("PAD_MESSAGE", {
|
|
user: wc.myID,
|
|
msg: CpNetflux.removeCp(msg),
|
|
validateKey: channel.data.validateKey
|
|
}, cId);
|
|
cb();
|
|
}));
|
|
};
|
|
channel.wc = wc;
|
|
channel.queue.forEach((function(data) {
|
|
channel.sendMessage(data.message, clientId);
|
|
}));
|
|
channel.queue = [];
|
|
channel.bcast("PAD_CONNECT", {
|
|
myID: wc.myID,
|
|
id: wc.id,
|
|
members: wc.members
|
|
});
|
|
}
|
|
};
|
|
channel.cpNf = CpNetflux.start(conf);
|
|
};
|
|
Store.leavePad = function(clientId, data, cb) {
|
|
var channel = channels[data.channel];
|
|
if (!channel || !channel.cpNf) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
Store.dropChannel(data.channel);
|
|
cb();
|
|
};
|
|
Store.sendPadMsg = function(clientId, data, cb) {
|
|
var msg = data.msg;
|
|
var channel = channels[data.channel];
|
|
if (!channel) {
|
|
return;
|
|
}
|
|
if (!channel.wc) {
|
|
channel.queue.push(msg);
|
|
return void cb();
|
|
}
|
|
channel.sendMessage(msg, clientId, cb);
|
|
};
|
|
Store.corruptedCache = function(clientId, channel) {
|
|
var chan = channels[channel];
|
|
if (!chan || !chan.cpNf) {
|
|
return;
|
|
}
|
|
Cache.clearChannel(channel);
|
|
if (!chan.cpNf.resetCache) {
|
|
return;
|
|
}
|
|
chan.cpNf.resetCache();
|
|
};
|
|
Store.changePadPasswordPin = function(clientId, data, cb) {
|
|
var oldChannel = data.oldChannel;
|
|
var channel = data.channel;
|
|
nThen((function(waitFor) {
|
|
getAllStores().forEach((function(s) {
|
|
var allData = s.manager.findChannel(channel);
|
|
if (!allData.length) {
|
|
return;
|
|
}
|
|
s.rpc.unpin([ oldChannel ], waitFor());
|
|
s.rpc.pin([ channel ], waitFor());
|
|
}));
|
|
})).nThen(cb);
|
|
};
|
|
Store.contactPadOwner = function(clientId, data, cb) {
|
|
var owners = data.owners;
|
|
if (!Array.isArray(owners) || !owners.length) {
|
|
return cb({
|
|
state: false
|
|
});
|
|
}
|
|
if (!data.send) {
|
|
return void cb({
|
|
state: true
|
|
});
|
|
}
|
|
nThen((function(waitFor) {
|
|
owners.forEach((function(owner) {
|
|
var sendTo = function(query, msg, user, _cb) {
|
|
if (store.mailbox && !data.anon) {
|
|
return store.mailbox.sendTo(query, msg, user, _cb);
|
|
}
|
|
Mailbox.sendToAnon(store.anon_rpc, query, msg, user, _cb);
|
|
};
|
|
sendTo(data.query, {
|
|
channel: data.channel,
|
|
data: data.msgData
|
|
}, {
|
|
channel: owner.notifications,
|
|
curvePublic: owner.curvePublic
|
|
}, waitFor());
|
|
}));
|
|
})).nThen((function() {
|
|
cb({
|
|
state: true
|
|
});
|
|
}));
|
|
};
|
|
Store.givePadAccess = function(clientId, data, cb) {
|
|
var edPublic = store.proxy.edPublic;
|
|
var channel = data.channel;
|
|
var res = store.manager.findChannel(channel);
|
|
if (!data.user || !data.user.notifications || !data.user.curvePublic) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var href, title;
|
|
if (!res.some((function(obj) {
|
|
if (obj.data && Array.isArray(obj.data.owners) && obj.data.owners.indexOf(edPublic) !== -1 && obj.data.href) {
|
|
href = obj.data.href;
|
|
title = obj.data.title;
|
|
return true;
|
|
}
|
|
}))) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
store.mailbox.sendTo("GIVE_PAD_ACCESS", {
|
|
channel,
|
|
href,
|
|
title
|
|
}, {
|
|
channel: data.user.notifications,
|
|
curvePublic: data.user.curvePublic
|
|
});
|
|
cb();
|
|
};
|
|
Store.getLastHash = function(clientId, data, cb) {
|
|
var chan = channels[data.channel];
|
|
if (!chan) {
|
|
return void cb({
|
|
error: "ENOCHAN"
|
|
});
|
|
}
|
|
if (!chan.lastHash) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
cb({
|
|
hash: chan.lastHash
|
|
});
|
|
};
|
|
var notifyOwnerPadRemoved = function(data, obj) {
|
|
var channel = data.channel;
|
|
var href = data.href;
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, data.password);
|
|
if (obj && obj.error) {
|
|
return;
|
|
}
|
|
if (!obj.mailbox) {
|
|
return;
|
|
}
|
|
var crypto = Crypto.createEncryptor(secret.keys);
|
|
var m = [];
|
|
try {
|
|
if (typeof obj.mailbox === "string") {
|
|
m.push(crypto.decrypt(obj.mailbox, true, true));
|
|
} else {
|
|
Object.keys(obj.mailbox).forEach((function(k) {
|
|
m.push(crypto.decrypt(obj.mailbox[k], true, true));
|
|
}));
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
var curvePublic;
|
|
try {
|
|
curvePublic = store.proxy.curvePublic;
|
|
} catch (err) {
|
|
console.error(err);
|
|
return;
|
|
}
|
|
m.forEach((function(obj) {
|
|
var mb = JSON.parse(obj);
|
|
if (mb.curvePublic === curvePublic) {
|
|
return;
|
|
}
|
|
store.mailbox.sendTo("OWNED_PAD_REMOVED", {
|
|
channel
|
|
}, {
|
|
channel: mb.notifications,
|
|
curvePublic: mb.curvePublic
|
|
}, (function() {}));
|
|
}));
|
|
};
|
|
Store.burnPad = function(clientId, data) {
|
|
var channel = data.channel;
|
|
var ownerKey = Crypto.b64AddSlashes(data.ownerKey || "");
|
|
if (!channel || !ownerKey) {
|
|
return void console.error("Can't delete BAR pad");
|
|
}
|
|
try {
|
|
var signKey = Hash.decodeBase64(ownerKey);
|
|
var pair = Crypto.Nacl.sign.keyPair.fromSecretKey(signKey);
|
|
Pinpad.create(store.network, {
|
|
edPublic: Hash.encodeBase64(pair.publicKey),
|
|
edPrivate: Hash.encodeBase64(pair.secretKey)
|
|
}, (function(e, rpc) {
|
|
if (e) {
|
|
return void console.error(e);
|
|
}
|
|
Store.getPadMetadata(null, {
|
|
channel
|
|
}, (function(md) {
|
|
rpc.removeOwnedChannel(channel, (function(err) {
|
|
if (err) {
|
|
return void console.error(err);
|
|
}
|
|
notifyOwnerPadRemoved(data, md);
|
|
}));
|
|
}));
|
|
}));
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
};
|
|
Store.getPadMetadata = function(clientId, data, _cb) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
if (store.offline || !store.anon_rpc) {
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
if (!data.channel) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (data.channel.length !== 32) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
if (!Hash.isValidChannel(data.channel)) {
|
|
Feedback.send("METADATA_INVALID_CHAN");
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
store.anon_rpc.send("GET_METADATA", data.channel, (function(err, obj) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
var metadata = obj && obj[0] || {};
|
|
cb(metadata);
|
|
if (metadata.rejected) {
|
|
return;
|
|
}
|
|
getAllStores().forEach((function(s) {
|
|
var allData = s.manager.findChannel(data.channel, true);
|
|
var changed = false;
|
|
allData.forEach((function(obj) {
|
|
if (Sortify(obj.data.owners) !== Sortify(metadata.owners)) {
|
|
changed = true;
|
|
}
|
|
obj.data.owners = metadata.owners;
|
|
obj.data.atime = +new Date;
|
|
if (metadata.expire) {
|
|
obj.data.expire = +metadata.expire;
|
|
}
|
|
}));
|
|
if (!changed) {
|
|
return;
|
|
}
|
|
var send = s.sendEvent || sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
Store.setPadMetadata = function(clientId, data, cb) {
|
|
if (!data.channel) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
if (!data.command) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
var s = getStore(data.teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
var otherChannels = data.channels;
|
|
delete data.channels;
|
|
s.rpc.setMetadata(data, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
if (!Array.isArray(res) || !res.length) {
|
|
return void cb({});
|
|
}
|
|
cb(res[0]);
|
|
}));
|
|
if (Array.isArray(otherChannels)) {
|
|
otherChannels.forEach((function(chan) {
|
|
var _d = Util.clone(data);
|
|
_d.channel = chan;
|
|
Store.setPadMetadata(clientId, _d, (function() {}));
|
|
}));
|
|
}
|
|
};
|
|
Store.deleteMailboxMessage = function(clientId, data, cb) {
|
|
if (!store.anon_rpc) {
|
|
return void cb({
|
|
error: "RPC_NOT_READY"
|
|
});
|
|
}
|
|
store.anon_rpc.send("DELETE_MAILBOX_MESSAGE", data, (function(e) {
|
|
cb({
|
|
error: e
|
|
});
|
|
}));
|
|
};
|
|
Store.getFullHistory = function(clientId, data, cb) {
|
|
var network = store.network;
|
|
var hk = network.historyKeeper;
|
|
var parse = function(msg) {
|
|
try {
|
|
return JSON.parse(msg);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
};
|
|
var msgs = [];
|
|
var completed = false;
|
|
var onMsg = function(msg) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
var parsed = parse(msg);
|
|
if (!parsed) {
|
|
return;
|
|
}
|
|
if (parsed[0] === "FULL_HISTORY_END") {
|
|
cb(msgs);
|
|
network.off("message", onMsg);
|
|
completed = true;
|
|
return;
|
|
}
|
|
if (parsed[0] !== "FULL_HISTORY") {
|
|
return;
|
|
}
|
|
if (parsed[1] && parsed[1].validateKey) {
|
|
return;
|
|
}
|
|
if (parsed[1][3] !== data.channel) {
|
|
return;
|
|
}
|
|
msg = parsed[1][4];
|
|
if (msg) {
|
|
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, "");
|
|
if (data.debug) {
|
|
msgs.push({
|
|
serverHash: msg.slice(0, 64),
|
|
msg,
|
|
author: parsed[1][1],
|
|
time: parsed[1][5]
|
|
});
|
|
} else {
|
|
msgs.push(msg);
|
|
}
|
|
}
|
|
};
|
|
network.on("message", onMsg);
|
|
network.sendto(hk, JSON.stringify([ "GET_FULL_HISTORY", data.channel, data.validateKey ]));
|
|
};
|
|
Store.getHistory = function(clientId, data, _cb, full) {
|
|
var cb = Util.once(Util.mkAsync(_cb));
|
|
var network = store.network;
|
|
var hk = network.historyKeeper;
|
|
var parse = function(msg) {
|
|
try {
|
|
return JSON.parse(msg);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
};
|
|
var txid = Math.floor(Math.random() * 1e6);
|
|
var msgs = [];
|
|
var completed = false;
|
|
var onMsg = function(msg, sender) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
if (sender !== hk) {
|
|
return;
|
|
}
|
|
var parsed = parse(msg);
|
|
if (!parsed) {
|
|
return;
|
|
}
|
|
if (parsed.txid && parsed.txid !== txid) {
|
|
return;
|
|
}
|
|
if (parsed.validateKey && parsed.channel) {
|
|
return;
|
|
}
|
|
if (parsed.error && parsed.channel) {
|
|
if (parsed.channel === data.channel) {
|
|
network.off("message", onMsg);
|
|
completed = true;
|
|
cb({
|
|
error: parsed.error
|
|
});
|
|
}
|
|
return;
|
|
}
|
|
if (parsed.state === 1 && parsed.channel) {
|
|
if (parsed.channel !== data.channel) {
|
|
return;
|
|
}
|
|
cb(msgs);
|
|
network.off("message", onMsg);
|
|
completed = true;
|
|
return;
|
|
}
|
|
if (Array.isArray(parsed) && parsed[0] && parsed[0] !== txid) {
|
|
return;
|
|
}
|
|
if (parsed[3] !== data.channel) {
|
|
return;
|
|
}
|
|
if (parsed[4] && full) {
|
|
msgs.push({
|
|
msg,
|
|
hash: parsed[4].slice(0, 64)
|
|
});
|
|
return;
|
|
}
|
|
msg = parsed[4];
|
|
if (msg) {
|
|
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, "");
|
|
msgs.push(msg);
|
|
}
|
|
};
|
|
network.on("message", onMsg);
|
|
var cfg = {
|
|
txid,
|
|
lastKnownHash: data.lastKnownHash
|
|
};
|
|
var msg = [ "GET_HISTORY", data.channel, cfg ];
|
|
network.sendto(hk, JSON.stringify(msg));
|
|
};
|
|
Store.getHistoryRange = function(clientId, data, cb) {
|
|
var network = store.network;
|
|
var hk = network.historyKeeper;
|
|
var parse = function(msg) {
|
|
try {
|
|
return JSON.parse(msg);
|
|
} catch (e) {
|
|
return null;
|
|
}
|
|
};
|
|
var msgs = [];
|
|
var first = true;
|
|
var fullHistory = false;
|
|
var completed = false;
|
|
var lastKnownHash;
|
|
var txid = Util.uid();
|
|
var onMsg = function(msg) {
|
|
if (completed) {
|
|
return;
|
|
}
|
|
var parsed = parse(msg);
|
|
if (parsed[1] !== txid) {
|
|
console.log("bad txid");
|
|
return;
|
|
}
|
|
if (parsed[0] === "HISTORY_RANGE_ERROR") {
|
|
cb({
|
|
error: parsed[2]
|
|
});
|
|
return;
|
|
}
|
|
if (parsed[0] === "HISTORY_RANGE_END") {
|
|
cb({
|
|
messages: msgs,
|
|
isFull: fullHistory,
|
|
lastKnownHash
|
|
});
|
|
completed = true;
|
|
return;
|
|
}
|
|
if (parsed[0] !== "HISTORY_RANGE") {
|
|
return;
|
|
}
|
|
if (parsed[2] && parsed[1].validateKey) {
|
|
return;
|
|
}
|
|
if (parsed[2][3] !== data.channel) {
|
|
return;
|
|
}
|
|
msg = parsed[2][4];
|
|
if (msg) {
|
|
if (first) {
|
|
if (!/^cp\|/.test(msg) && !data.toHash) {
|
|
fullHistory = true;
|
|
}
|
|
lastKnownHash = msg.slice(0, 64);
|
|
first = false;
|
|
}
|
|
msg = msg.replace(/cp\|(([A-Za-z0-9+\/=]+)\|)?/, "");
|
|
msgs.push({
|
|
serverHash: msg.slice(0, 64),
|
|
msg,
|
|
author: parsed[2][1],
|
|
time: parsed[2][5]
|
|
});
|
|
}
|
|
};
|
|
network.on("message", onMsg);
|
|
network.sendto(hk, JSON.stringify([ "GET_HISTORY_RANGE", data.channel, {
|
|
from: data.lastKnownHash,
|
|
to: data.toHash,
|
|
cpCount: data.cpCount || 2,
|
|
txid
|
|
} ]));
|
|
};
|
|
var registerProxyEvents = function(proxy, fId) {
|
|
if (!proxy) {
|
|
return;
|
|
}
|
|
if (proxy.deprecated || proxy.restricted) {
|
|
return;
|
|
}
|
|
if (!fId) {
|
|
proxy.on("change", [ "drive", UserObject.SHARED_FOLDERS ], (function(o, n, p) {
|
|
if (p.length > 3 && p[3] === "password") {
|
|
var id = p[2];
|
|
var data = proxy.drive[UserObject.SHARED_FOLDERS][id];
|
|
var href = store.manager.user.userObject.getHref ? store.manager.user.userObject.getHref(data) : data.href;
|
|
var parsed = Hash.parsePadUrl(href);
|
|
var secret = Hash.getSecrets(parsed.type, parsed.hash, o);
|
|
SF.updatePassword(Store, {
|
|
oldChannel: secret.channel,
|
|
password: n,
|
|
href
|
|
}, store.network, (function() {
|
|
console.log("Shared folder password changed");
|
|
}));
|
|
return false;
|
|
}
|
|
}));
|
|
}
|
|
proxy.on("change", [], (function(o, n, p) {
|
|
if (fId) {
|
|
if (p[0] === UserObject.FILES_DATA && typeof n === "object" && n.channel && !n.owners) {
|
|
var toPin = [ n.channel ];
|
|
if (n.rtChannel) {
|
|
toPin.push(n.rtChannel);
|
|
}
|
|
if (n.lastVersion) {
|
|
toPin.push(n.lastVersion);
|
|
}
|
|
Store.pinPads(null, toPin, (function(obj) {
|
|
console.error(obj);
|
|
}));
|
|
}
|
|
if (p[0] === UserObject.FILES_DATA && typeof o === "object" && o.channel && !n) {
|
|
var toUnpin = [ o.channel ];
|
|
var c = store.manager.findChannel(o.channel);
|
|
var exists = c.some((function(data) {
|
|
return data.fId !== fId;
|
|
}));
|
|
if (!exists) {
|
|
if (o.rtChannel) {
|
|
toUnpin.push(o.rtChannel);
|
|
}
|
|
if (o.lastVersion) {
|
|
toUnpin.push(o.lastVersion);
|
|
}
|
|
Store.unpinPads(null, toUnpin, (function(obj) {
|
|
console.error(obj);
|
|
}));
|
|
}
|
|
}
|
|
}
|
|
if (o && !n && Array.isArray(p) && (p[0] === UserObject.FILES_DATA || p[0] === "drive" && p[1] === UserObject.FILES_DATA)) {
|
|
setTimeout((function() {
|
|
Store.checkDeletedPad(o && o.channel);
|
|
}));
|
|
}
|
|
sendDriveEvent("DRIVE_CHANGE", {
|
|
id: fId,
|
|
old: o,
|
|
new: n,
|
|
path: p
|
|
});
|
|
}));
|
|
proxy.on("remove", [], (function(o, p) {
|
|
sendDriveEvent("DRIVE_REMOVE", {
|
|
id: fId,
|
|
old: o,
|
|
path: p
|
|
});
|
|
}));
|
|
};
|
|
var addSharedFolderHandler = function() {
|
|
store.sharedFolders = {};
|
|
store.handleSharedFolder = function(id, rt) {
|
|
if (!rt) {
|
|
delete store.sharedFolders[id];
|
|
return;
|
|
}
|
|
store.sharedFolders[id] = rt;
|
|
if (store.driveEvents) {
|
|
registerProxyEvents(rt.proxy, id);
|
|
}
|
|
};
|
|
};
|
|
Store.loadSharedFolder = function(teamId, id, data, cb, isNew) {
|
|
var s = getStore(teamId);
|
|
if (!s) {
|
|
return void cb({
|
|
error: "ENOTFOUND"
|
|
});
|
|
}
|
|
var parsed = Hash.parsePadUrl(data.href || data.roHref);
|
|
if (!parsed && !parsed.hashData) {
|
|
return void cb({
|
|
error: "EINVAL"
|
|
});
|
|
}
|
|
SF.load({
|
|
isNew,
|
|
network: store.network || store.networkPromise,
|
|
store: s,
|
|
Store,
|
|
isNewChannel: Store.isNewChannel
|
|
}, id, data, cb);
|
|
};
|
|
var loadSharedFolder = function(id, data, cb, isNew) {
|
|
Store.loadSharedFolder(null, id, data, cb, isNew);
|
|
};
|
|
Store.loadSharedFolderAnon = function(clientId, data, cb) {
|
|
Store.loadSharedFolder(null, data.id, data.data, (function(rt) {
|
|
cb({
|
|
error: rt ? undefined : "EDELETED"
|
|
});
|
|
}));
|
|
};
|
|
Store.addSharedFolder = function(clientId, data, cb) {
|
|
onReadyEvt.reg((function() {
|
|
var s = getStore(data.teamId);
|
|
s.manager.addSharedFolder(data, (function(id) {
|
|
if (id && typeof id === "object" && id.error) {
|
|
return void cb(id);
|
|
}
|
|
var send = data.teamId ? s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
cb(id);
|
|
}));
|
|
}));
|
|
};
|
|
Store.updateSharedFolderPassword = function(clientId, data, cb) {
|
|
SF.updatePassword(Store, data, store.network, cb);
|
|
};
|
|
Store.userObjectCommand = function(clientId, cmdData, cb) {
|
|
if (!cmdData || !cmdData.cmd) {
|
|
return;
|
|
}
|
|
var s = getStore(cmdData.teamId);
|
|
if (s.offline) {
|
|
var send = s.id ? s.sendEvent : sendDriveEvent;
|
|
send("NETWORK_DISCONNECT");
|
|
return void cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}
|
|
var cb2 = function(data2) {
|
|
getAllStores().forEach((function(_s) {
|
|
var send = _s.id ? _s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
}, clientId);
|
|
}));
|
|
onSync(cmdData.teamId, (function() {
|
|
cb(data2);
|
|
}));
|
|
};
|
|
s.manager.command(cmdData, cb2);
|
|
};
|
|
var alwaysOnline = function(chanId) {
|
|
if (!store) {
|
|
return;
|
|
}
|
|
if (store.driveChannel === chanId) {
|
|
return true;
|
|
}
|
|
if (SF.isSharedFolderChannel(chanId)) {
|
|
return true;
|
|
}
|
|
if (Util.find(store, [ "proxy", "teams" ])) {
|
|
var t = Util.find(store, [ "proxy", "teams" ]) || {};
|
|
return Object.keys(t).some((function(id) {
|
|
return t[id].channel === chanId;
|
|
}));
|
|
}
|
|
if (Util.find(store, [ "proxy", "profile", "href" ])) {
|
|
return Hash.hrefToHexChannelId(Util.find(store, [ "proxy", "profile", "href" ])) === chanId;
|
|
}
|
|
};
|
|
var dropChannel = Store.dropChannel = function(chanId) {
|
|
console.error("Drop channel", chanId);
|
|
try {
|
|
store.messenger.leavePad(chanId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
store.modules["cursor"].leavePad(chanId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
store.modules["integration"].leavePad(chanId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
store.onlyoffice.leavePad(chanId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
if (alwaysOnline(chanId)) {
|
|
delete Store.channels[chanId];
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
Cache.leaveChannel(chanId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
if (!Store.channels[chanId]) {
|
|
return;
|
|
}
|
|
if (Store.channels[chanId].cpNf) {
|
|
Store.channels[chanId].cpNf.stop();
|
|
}
|
|
delete Store.channels[chanId];
|
|
};
|
|
Store._removeClient = function(clientId) {
|
|
var driveIdx = driveEventClients.indexOf(clientId);
|
|
if (driveIdx !== -1) {
|
|
driveEventClients.splice(driveIdx, 1);
|
|
}
|
|
try {
|
|
store.onlyoffice?.removeClient(clientId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
try {
|
|
store.mailbox?.removeClient(clientId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
Object.keys(store.modules).forEach((function(key) {
|
|
if (!store.modules[key]) {
|
|
return;
|
|
}
|
|
if (!store.modules[key].removeClient) {
|
|
return;
|
|
}
|
|
try {
|
|
store.modules[key].removeClient(clientId);
|
|
} catch (e) {
|
|
console.error(e);
|
|
}
|
|
}));
|
|
Object.keys(Store.channels).forEach((function(chanId) {
|
|
var chanIdx = Store.channels[chanId].clients.indexOf(clientId);
|
|
if (chanIdx !== -1) {
|
|
Store.channels[chanId].clients.splice(chanIdx, 1);
|
|
}
|
|
if (Store.channels[chanId].clients.length === 0) {
|
|
dropChannel(chanId);
|
|
}
|
|
}));
|
|
};
|
|
var loadOnlyOffice = function() {
|
|
if (store.onlyoffice) {
|
|
return;
|
|
}
|
|
store.onlyoffice = OnlyOffice.init(store, (function(ev, data, clients) {
|
|
clients.forEach((function(cId) {
|
|
postMessage(cId, "OO_EVENT", {
|
|
ev,
|
|
data
|
|
});
|
|
}));
|
|
}));
|
|
};
|
|
var loadMailbox = function(waitFor) {
|
|
store.mailbox = Mailbox.init({
|
|
Store,
|
|
store,
|
|
updateMetadata: function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
},
|
|
updateDrive: function() {
|
|
sendDriveEvent("DRIVE_CHANGE", {
|
|
path: [ "drive", "filesData" ]
|
|
});
|
|
},
|
|
pinPads: function(data, cb) {
|
|
Store.pinPads(null, data, cb);
|
|
}
|
|
}, waitFor, (function(ev, data, clients, _cb) {
|
|
var cb = Util.once(_cb || function() {});
|
|
clients.forEach((function(cId) {
|
|
postMessage(cId, "MAILBOX_EVENT", {
|
|
ev,
|
|
data
|
|
}, cb);
|
|
}));
|
|
}));
|
|
};
|
|
Store.refreshDriveUI = function() {
|
|
getAllStores().forEach((function(_s) {
|
|
var send = _s.id ? _s.sendEvent : sendDriveEvent;
|
|
send("DRIVE_CHANGE", {
|
|
path: [ "drive", UserObject.FILES_DATA ]
|
|
});
|
|
}));
|
|
};
|
|
var onCacheReady = function(clientId, _cb) {
|
|
const cb = Util.mkAsync(_cb);
|
|
var proxy = store.proxy;
|
|
var drive = store.drive;
|
|
if (store.manager) {
|
|
return void cb();
|
|
}
|
|
var unpin = function(data, cb) {
|
|
if (!store.loggedIn) {
|
|
return void cb();
|
|
}
|
|
Store.unpinPads(null, data, cb);
|
|
};
|
|
var pin = function(data, cb) {
|
|
if (!store.loggedIn) {
|
|
return void cb();
|
|
}
|
|
Store.pinPads(null, data, cb);
|
|
};
|
|
var manager = store.manager = ProxyManager.create(drive.proxy, {
|
|
onSync: function(cb) {
|
|
onSync(null, cb);
|
|
},
|
|
edPublic: proxy.edPublic,
|
|
pin,
|
|
unpin,
|
|
loadSharedFolder,
|
|
settings: proxy.settings,
|
|
removeOwnedChannel: function(channel, cb) {
|
|
Store.removeOwnedChannel("", channel, cb);
|
|
},
|
|
store,
|
|
Store
|
|
}, {
|
|
outer: true,
|
|
edPublic: store.proxy.edPublic,
|
|
loggedIn: store.loggedIn,
|
|
log: function(msg) {
|
|
sendDriveEvent("DRIVE_LOG", msg);
|
|
},
|
|
rt: drive.realtime
|
|
});
|
|
var userObject = store.userObject = manager.user.userObject;
|
|
nThen((function(waitFor) {
|
|
addSharedFolderHandler();
|
|
userObject.migrate(waitFor());
|
|
})).nThen((function(waitFor) {
|
|
console.error("START LOADING SF");
|
|
var network = store.network || store.networkPromise;
|
|
SF.loadSharedFolders(Store, network, store, drive.proxy, userObject, waitFor, (obj => {
|
|
var data = {
|
|
type: "sf",
|
|
progress: 100 * obj.progress / obj.max
|
|
};
|
|
postMessage(clientId, "LOADING_DRIVE", data);
|
|
}), true);
|
|
})).nThen((function(waitFor) {
|
|
console.error("SF CACHE READY");
|
|
loadUniversal(Team, "team", waitFor, clientId);
|
|
})).nThen((function(waitFor) {
|
|
console.error("TEAM CACHE READY");
|
|
loadUniversal(Calendar, "calendar", waitFor);
|
|
})).nThen((function() {
|
|
cb();
|
|
}));
|
|
};
|
|
const loadSyncModules = (waitFor = function() {}) => {
|
|
loadUniversal(Cursor, "cursor", waitFor);
|
|
loadUniversal(Integration, "integration", waitFor);
|
|
loadUniversal(Messenger, "messenger", waitFor);
|
|
loadUniversal(History, "history", waitFor);
|
|
loadOnlyOffice();
|
|
if (store) {
|
|
store.messenger = store.modules["messenger"];
|
|
}
|
|
};
|
|
var onReady = function(clientId, returned, cb) {
|
|
store.ready = true;
|
|
var proxy = store.proxy;
|
|
var manager = store.manager;
|
|
var userObject = store.userObject;
|
|
nThen((function(waitFor) {
|
|
if (!proxy.settings) {
|
|
proxy.settings = NEW_USER_SETTINGS;
|
|
}
|
|
if (!proxy.forms) {
|
|
proxy.forms = {};
|
|
}
|
|
if (!proxy.friends_pending) {
|
|
proxy.friends_pending = {};
|
|
}
|
|
if (!proxy.form_seed) {
|
|
proxy.form_seed = Hash.createChannelId();
|
|
}
|
|
if (!manager) {
|
|
onCacheReady(clientId, waitFor());
|
|
manager = store.manager;
|
|
userObject = store.userObject;
|
|
}
|
|
initAnonRpc(null, null, waitFor());
|
|
initRpc(null, null, waitFor());
|
|
postMessage(clientId, "LOADING_DRIVE", {
|
|
type: "migrate",
|
|
progress: 0
|
|
});
|
|
})).nThen((function(waitFor) {
|
|
if (typeof proxy.version === "undefined") {
|
|
proxy.version = 11;
|
|
}
|
|
Migrate(proxy, waitFor(), (function(version, progress) {
|
|
postMessage(clientId, "LOADING_DRIVE", {
|
|
type: "migrate",
|
|
progress
|
|
});
|
|
}), store);
|
|
})).nThen((function(waitFor) {
|
|
postMessage(clientId, "LOADING_DRIVE", {
|
|
type: "sf",
|
|
progress: 0
|
|
});
|
|
userObject.fixFiles();
|
|
SF.loadSharedFolders(Store, store.network, store, store.drive.proxy, userObject, waitFor, (obj => {
|
|
var data = {
|
|
type: "sf",
|
|
progress: 100 * obj.progress / obj.max
|
|
};
|
|
postMessage(clientId, "LOADING_DRIVE", data);
|
|
}));
|
|
loadSyncModules(waitFor);
|
|
loadUniversal(Profile, "profile", waitFor);
|
|
loadUniversal(Calendar, "calendar", waitFor);
|
|
if (store.modules["team"]) {
|
|
store.modules["team"].onReady(waitFor);
|
|
}
|
|
loadUniversal(Support, "support", waitFor);
|
|
})).nThen((function() {
|
|
console.error("SF & TEAM READY");
|
|
var requestLogin = function() {
|
|
broadcast([], "REQUEST_LOGIN");
|
|
};
|
|
if (store.loggedIn) {
|
|
arePinsSynced((function(err, yes) {
|
|
if (!yes) {
|
|
resetPins((function(err) {
|
|
if (err) {
|
|
return console.error(err);
|
|
}
|
|
console.log("RESET DONE");
|
|
}));
|
|
}
|
|
}));
|
|
if (typeof proxy.loginToken !== "number") {
|
|
proxy[Constants.tokenKey] = store.data.localToken || Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
}
|
|
returned[Constants.tokenKey] = proxy[Constants.tokenKey];
|
|
if (store.data.localToken && store.data.localToken !== proxy[Constants.tokenKey]) {
|
|
return void requestLogin();
|
|
}
|
|
}
|
|
returned.feedback = Util.find(proxy, [ "settings", "general", "allowUserFeedback" ]);
|
|
Feedback.init(returned.feedback);
|
|
store.returned = returned;
|
|
if (typeof cb === "function") {
|
|
cb(returned);
|
|
}
|
|
store.offline = false;
|
|
sendDriveEvent("NETWORK_RECONNECT");
|
|
broadcast([], "UPDATE_METADATA");
|
|
broadcast([], "STORE_READY", returned);
|
|
if (typeof proxy.uid !== "string" || proxy.uid.length !== 32) {
|
|
console.log("generating a persistent identifier");
|
|
proxy.uid = Hash.createChannelId();
|
|
}
|
|
if (store.loggedIn && (!Store.hasSigningKeys() || !Store.hasCurveKeys())) {
|
|
return void requestLogin();
|
|
}
|
|
proxy.on("change", [ Constants.displayNameKey ], (function(o, n) {
|
|
if (typeof n !== "string") {
|
|
return;
|
|
}
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
proxy.on("change", [ "profile" ], (function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
proxy.on("change", [ "friends" ], (function(o, n, p) {
|
|
broadcast([], "UPDATE_METADATA");
|
|
if (!store.messenger) {
|
|
return;
|
|
}
|
|
if (o !== undefined) {
|
|
return;
|
|
}
|
|
var curvePublic = p.slice(-1)[0];
|
|
var friend = proxy.friends && proxy.friends[curvePublic];
|
|
store.messenger.onFriendAdded(friend);
|
|
}));
|
|
proxy.on("remove", [ "friends" ], (function(o, p) {
|
|
broadcast([], "UPDATE_METADATA");
|
|
if (!store.messenger) {
|
|
return;
|
|
}
|
|
var curvePublic = p[1];
|
|
if (!curvePublic) {
|
|
return;
|
|
}
|
|
if (p[2] !== "channel") {
|
|
return;
|
|
}
|
|
store.messenger.onFriendRemoved(curvePublic, o);
|
|
}));
|
|
proxy.on("change", [ "friends_pending" ], (function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
proxy.on("remove", [ "friends_pending" ], (function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
proxy.on("change", [ "settings" ], (function() {
|
|
broadcast([], "UPDATE_METADATA");
|
|
}));
|
|
proxy.on("change", [ Constants.tokenKey ], (function() {
|
|
if (store.isDeleted || proxy[Constants.tokenKey] === "DELETED") {
|
|
return;
|
|
}
|
|
broadcast([], "UPDATE_TOKEN", {
|
|
token: proxy[Constants.tokenKey]
|
|
});
|
|
}));
|
|
loadMailbox();
|
|
onReadyEvt.fire();
|
|
}));
|
|
};
|
|
const loadAccount = (clientId, data, cacheCb, cb) => {
|
|
if (store.accountModule) {
|
|
return store.accountModule;
|
|
}
|
|
const account = Account.init({
|
|
userHash: data.userHash,
|
|
anonHash: data.anonHash,
|
|
cache: data.cache,
|
|
form_seed: data.form_seed,
|
|
store,
|
|
broadcast,
|
|
postMessage
|
|
});
|
|
store.accountModule = account;
|
|
const {channel, onAccountReady, onAccountCacheReady, onDisconnect, onReconnect} = account;
|
|
store.driveChannel = channel;
|
|
onAccountCacheReady((returned => {
|
|
store.returned ||= returned;
|
|
cacheCb(returned);
|
|
}));
|
|
onAccountReady((returned => {
|
|
store.returned ||= returned;
|
|
cb(returned);
|
|
}));
|
|
onDisconnect((() => {
|
|
sendDriveEvent("NETWORK_DISCONNECT");
|
|
}));
|
|
onReconnect((() => {
|
|
sendDriveEvent("NETWORK_RECONNECT");
|
|
}));
|
|
const PING_INTERVAL = 12e4;
|
|
const MAX_PING = 3e4;
|
|
const MAX_FAILED_PING = 2;
|
|
setInterval((function() {
|
|
var clients = [];
|
|
Object.keys(Store.channels).forEach((function(chanId) {
|
|
var c = Store.channels[chanId].clients;
|
|
Array.prototype.push.apply(clients, c);
|
|
}));
|
|
clients = Util.deduplicateString(clients);
|
|
clients.forEach((function(cId) {
|
|
var nb = 0;
|
|
var ping = function() {
|
|
if (nb >= MAX_FAILED_PING) {
|
|
Store._removeClient(cId);
|
|
postMessage(cId, "TIMEOUT");
|
|
console.error("TIMEOUT", cId);
|
|
return;
|
|
}
|
|
nb++;
|
|
var to = setTimeout(ping, MAX_PING);
|
|
postMessage(cId, "PING", null, (function(err) {
|
|
if (err) {
|
|
console.error(err);
|
|
}
|
|
clearTimeout(to);
|
|
}));
|
|
};
|
|
ping();
|
|
}));
|
|
}), PING_INTERVAL);
|
|
return account;
|
|
};
|
|
const loadDrive = (clientId, data, cacheCb, cb) => {
|
|
const startDrive = Util.once((account => {
|
|
const drive = Drive.init({
|
|
account,
|
|
store,
|
|
broadcast,
|
|
postMessage
|
|
});
|
|
const {onDriveReady, onDriveCacheReady, onDisconnect, onReconnect} = drive;
|
|
onDriveCacheReady((() => {
|
|
cacheCb(store.returned);
|
|
}));
|
|
onDriveReady((() => {
|
|
cb(store.returned);
|
|
}));
|
|
onDisconnect((() => {}));
|
|
onReconnect((() => {}));
|
|
}));
|
|
const noop = () => {};
|
|
const account = loadAccount(clientId, data, noop, noop);
|
|
account.onAccountCacheReady((() => {
|
|
startDrive(account);
|
|
}));
|
|
account.onAccountReady((() => {
|
|
startDrive(account);
|
|
}));
|
|
};
|
|
Store.disableCache = function(clientId, disabled, cb) {
|
|
if (disabled) {
|
|
Cache.disable();
|
|
} else {
|
|
Cache.enable();
|
|
}
|
|
cb();
|
|
};
|
|
var initialized = false;
|
|
Store.hasDrive = function(clientId, data, cb) {
|
|
cb({
|
|
state: Boolean(store.proxy)
|
|
});
|
|
};
|
|
const loadHK = cb => {
|
|
if (store?.network?.historyKeeper) {
|
|
return setTimeout(cb);
|
|
}
|
|
const onNetwork = network => {
|
|
store.network ||= network;
|
|
const chan = "0000000000000000000000000000000000";
|
|
network.join(chan).then((function(wc) {
|
|
let hk;
|
|
wc.members.forEach((p => {
|
|
if (p.length === 16) {
|
|
hk = p;
|
|
}
|
|
}));
|
|
network.historyKeeper = hk;
|
|
wc.leave();
|
|
cb();
|
|
}), (function(err) {
|
|
console.error(err);
|
|
cb({
|
|
error: "GET_HK"
|
|
});
|
|
}));
|
|
};
|
|
if (store.network) {
|
|
return onNetwork(store.network);
|
|
}
|
|
store.networkPromise?.then(onNetwork);
|
|
};
|
|
var onNoDrive = function(clientId, cb, initRpc) {
|
|
var andThen = function() {
|
|
loadSyncModules();
|
|
let getAnon = () => {
|
|
initAnonRpc(null, null, (function() {
|
|
Feedback.send("NO_DRIVE", true);
|
|
cb({});
|
|
}));
|
|
};
|
|
if (initRpc) {
|
|
return initTempRpc(clientId, getAnon);
|
|
}
|
|
getAnon();
|
|
};
|
|
if (!store.network) {
|
|
var wsUrl = NetConfig.getWebsocketURL();
|
|
return void Netflux.connect(wsUrl).then((function(network) {
|
|
if (!store.network) {
|
|
store.network = network;
|
|
} else {
|
|
network.disconnect();
|
|
network = store.network;
|
|
}
|
|
loadHK((err => {
|
|
if (err) {
|
|
return void cb(err);
|
|
}
|
|
andThen();
|
|
}));
|
|
}), (function(err) {
|
|
console.error(err);
|
|
cb({
|
|
error: "OFFLINE"
|
|
});
|
|
}));
|
|
}
|
|
andThen();
|
|
};
|
|
const startCacheModules = (clientId, returned, cb) => {
|
|
onCacheReady(clientId, (function() {
|
|
cb(returned);
|
|
onCacheReadyEvt.fire();
|
|
}));
|
|
};
|
|
const startModules = (clientId, returned, cb) => {
|
|
if (store.manager) {
|
|
return void onCacheReadyEvt.reg((function() {
|
|
const onNetwork = _network => {
|
|
store.network ||= _network;
|
|
onReady(clientId, returned, (() => {
|
|
cb(returned);
|
|
}));
|
|
};
|
|
loadHK(onNetwork);
|
|
}));
|
|
}
|
|
onReady(clientId, returned, (() => {
|
|
cb(returned);
|
|
}));
|
|
};
|
|
const start = (clientId, data, cb) => {
|
|
const noDrive = data.neverDrive || data.noDrive && !data.userHash && !data.anonHash;
|
|
if (noDrive) {
|
|
if (data.neverDrive) {
|
|
store.neverCache = true;
|
|
}
|
|
return void onNoDrive(clientId, (obj => {
|
|
if (obj?.error) {
|
|
Feedback.send("NO_DRIVE_ERROR", true);
|
|
}
|
|
cb(obj);
|
|
}), !!data.neverDrive);
|
|
}
|
|
const requires = data.requires;
|
|
initialized = true;
|
|
store.data = data;
|
|
let onInit = ret => {
|
|
if (Object.keys(store.proxy).length === 1) {
|
|
Feedback.send("FIRST_APP_USE", true);
|
|
}
|
|
if (ret && ret.error) {
|
|
initialized = false;
|
|
}
|
|
};
|
|
if (requires === "pad") {
|
|
return void onNoDrive(clientId, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return;
|
|
}
|
|
cb(obj);
|
|
let next = Util.once((() => {
|
|
loadDrive(clientId, data, (ret => {
|
|
startCacheModules(clientId, ret, onInit);
|
|
}), (ret => {
|
|
startModules(clientId, ret, onInit);
|
|
}));
|
|
}));
|
|
onJoinedEvt.reg(next);
|
|
onPadRejectedEvt.reg(next);
|
|
}));
|
|
}
|
|
if (requires === "file") {
|
|
return void onNoDrive(clientId, (function(obj) {
|
|
if (obj && obj.error) {
|
|
return;
|
|
}
|
|
cb(obj);
|
|
loadDrive(clientId, data, (ret => {
|
|
startCacheModules(clientId, ret, onInit);
|
|
}), (ret => {
|
|
startModules(clientId, ret, onInit);
|
|
}));
|
|
}));
|
|
}
|
|
if (requires === "team") {
|
|
const initTeams = Util.once((ret => {
|
|
nThen((w => {
|
|
loadUniversal(Team, "team", w, clientId);
|
|
})).nThen((() => {
|
|
cb(ret);
|
|
loadDrive(clientId, data, (ret => {
|
|
startCacheModules(clientId, ret, onInit);
|
|
}), (ret => {
|
|
startModules(clientId, ret, onInit);
|
|
}));
|
|
}));
|
|
}));
|
|
return void loadAccount(clientId, data, initTeams, initTeams);
|
|
}
|
|
if (requires === "drive") {
|
|
return void loadDrive(clientId, data, (ret => {
|
|
cb(ret);
|
|
startCacheModules(clientId, ret, onInit);
|
|
}), (ret => {
|
|
cb(ret);
|
|
startModules(clientId, ret, onInit);
|
|
}));
|
|
}
|
|
let done = ret => {
|
|
onInit(ret);
|
|
const redirect = Constants.prefersDriveRedirectKey;
|
|
const redirectPreference = Util.find(store, [ "proxy", "settings", "general", redirect ]);
|
|
ret[redirect] = redirectPreference;
|
|
cb(ret);
|
|
};
|
|
loadDrive(clientId, data, (ret => {
|
|
startCacheModules(clientId, ret, done);
|
|
}), (ret => {
|
|
startModules(clientId, ret, done);
|
|
}));
|
|
};
|
|
let subscribeToDrive = function(clientId) {
|
|
onCacheReadyEvt.reg((() => {
|
|
if (driveEventClients.indexOf(clientId) === -1) {
|
|
driveEventClients.push(clientId);
|
|
}
|
|
if (!store.driveEvents) {
|
|
store.driveEvents = true;
|
|
registerProxyEvents(store.proxy);
|
|
Object.keys(store.manager.folders).forEach((function(fId) {
|
|
var proxy = store.manager.folders[fId].proxy;
|
|
registerProxyEvents(proxy, fId);
|
|
}));
|
|
}
|
|
}));
|
|
};
|
|
Store.init = function(clientId, data, _callback) {
|
|
var callback = Util.once((function(obj) {
|
|
if (data.driveEvents) {
|
|
subscribeToDrive(clientId);
|
|
}
|
|
_callback(obj);
|
|
}));
|
|
if (initialized && !store.returned && data.cache) {
|
|
return void onCacheReadyEvt.reg((function() {
|
|
callback({
|
|
state: "ALREADY_INIT",
|
|
returned: store.cacheReturned
|
|
});
|
|
}));
|
|
}
|
|
if (initialized) {
|
|
if (store.networkTimeout) {
|
|
postMessage(clientId, "LOADING_DRIVE", {
|
|
type: "offline"
|
|
});
|
|
}
|
|
return void onReadyEvt.reg((function() {
|
|
callback({
|
|
state: "ALREADY_INIT",
|
|
returned: store.returned
|
|
});
|
|
}));
|
|
}
|
|
if (data.disableCache) {
|
|
Cache.disable();
|
|
}
|
|
if (data.noDrive && !data.requires) {
|
|
data.requires = "pad";
|
|
}
|
|
start(clientId, data, Util.once((obj => {
|
|
if (obj.error === "GET_HK") {
|
|
return void callback({
|
|
error: "ERROR"
|
|
});
|
|
}
|
|
callback(obj);
|
|
})));
|
|
};
|
|
onReadyEvt.reg((function() {
|
|
var inactiveTime = +new Date - CACHE_MAX_AGE * (24 * 3600 * 1e3);
|
|
Cache.getKeys((function(err, keys) {
|
|
if (err) {
|
|
return void console.error(err);
|
|
}
|
|
var next = function() {
|
|
if (!keys.length) {
|
|
return;
|
|
}
|
|
var key = keys.pop();
|
|
Cache.getTime(key, (function(err, atime) {
|
|
if (err) {
|
|
return void next();
|
|
}
|
|
if (!atime || atime < inactiveTime) {
|
|
Cache.clearChannel(key, next());
|
|
return;
|
|
}
|
|
next();
|
|
}));
|
|
};
|
|
next();
|
|
}));
|
|
}));
|
|
Store.disconnect = function() {
|
|
if (globalThis.accountDeletion) {
|
|
return;
|
|
}
|
|
if (!store.network) {
|
|
return;
|
|
}
|
|
store.network.disconnect();
|
|
};
|
|
return Store;
|
|
};
|
|
return {
|
|
setCustomize,
|
|
create
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(undefined, requireJSON_sortify(), requireUserObject(), requireProxyManager(), requireMigrateUserObject(), requireCommonHash(), requireCommonUtil(), requireCommonConstants(), requireCommonFeedback(), requireCommonRealtime(), requireCommonMessaging(), requirePinpad(), requireRpc(), requireMergeDrive(), requireCacheStore(), requireSharedfolder(), require$$15, require$$16, requireCursor(), requireSupport(), requireIntegration(), requireOnlyoffice(), requireMailbox(), requireProfile(), requireTeam(), requireMessenger(), requireHistory(), requireCalendar(), requireLoginBlock(), requireNetworkConfig(), undefined, requireCrypto(), requireChainpad_dist(), requireChainpadNetflux(), requireChainpadListmap(), requireNetfluxClient(), requireNthen());
|
|
}
|
|
})();
|
|
})(asyncStore$1);
|
|
return asyncStore$1.exports;
|
|
}
|
|
var asyncStoreExports = requireAsyncStore();
|
|
var asyncStore = getDefaultExportFromCjs(asyncStoreExports);
|
|
var Store = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: asyncStore
|
|
}, [ asyncStoreExports ]);
|
|
var _interface = {
|
|
exports: {}
|
|
};
|
|
var storeRpc = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredStoreRpc;
|
|
function requireStoreRpc() {
|
|
if (hasRequiredStoreRpc) return storeRpc.exports;
|
|
hasRequiredStoreRpc = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = AStore => {
|
|
var create = function(config) {
|
|
var Store = AStore.create(config);
|
|
var Rpc = {};
|
|
var queries = Rpc.queries = {
|
|
CONNECT: Store.init,
|
|
DISCONNECT: Store.disconnect,
|
|
MIGRATE_ANON_DRIVE: Store.migrateAnonDrive,
|
|
PING: function(cId, data, cb) {
|
|
cb();
|
|
},
|
|
CACHE_DISABLE: Store.disableCache,
|
|
HAS_DRIVE: Store.hasDrive,
|
|
UPDATE_PIN_LIMIT: Store.updatePinLimit,
|
|
GET_PIN_LIMIT: Store.getPinLimit,
|
|
CLEAR_OWNED_CHANNEL: Store.clearOwnedChannel,
|
|
REMOVE_OWNED_CHANNEL: Store.removeOwnedChannel,
|
|
UPLOAD_CHUNK: Store.uploadChunk,
|
|
UPLOAD_COMPLETE: Store.uploadComplete,
|
|
UPLOAD_STATUS: Store.uploadStatus,
|
|
UPLOAD_CANCEL: Store.uploadCancel,
|
|
PIN_PADS: Store.pinPads,
|
|
UNPIN_PADS: Store.unpinPads,
|
|
GET_DELETED_PADS: Store.getDeletedPads,
|
|
GET_PINNED_USAGE: Store.getPinnedUsage,
|
|
ANON_RPC_MESSAGE: Store.anonRpcMsg,
|
|
GET_FILE_SIZE: Store.getFileSize,
|
|
GET_MULTIPLE_FILE_SIZE: Store.getMultipleFileSize,
|
|
GET: Store.get,
|
|
SET: Store.set,
|
|
GET_DRIVE: Store.drive.get,
|
|
SET_DRIVE: Store.drive.set,
|
|
ADD_PAD: Store.addPad,
|
|
SET_PAD_TITLE: Store.setPadTitle,
|
|
MOVE_TO_TRASH: Store.moveToTrash,
|
|
RESET_DRIVE: Store.resetDrive,
|
|
GET_METADATA: Store.getMetadata,
|
|
IS_ONLY_IN_SHARED_FOLDER: Store.isOnlyInSharedFolder,
|
|
SET_DISPLAY_NAME: Store.setDisplayName,
|
|
SET_PAD_ATTRIBUTE: Store.setPadAttribute,
|
|
GET_PAD_ATTRIBUTE: Store.getPadAttribute,
|
|
SET_ATTRIBUTE: Store.setAttribute,
|
|
GET_ATTRIBUTE: Store.getAttribute,
|
|
LIST_ALL_TAGS: Store.listAllTags,
|
|
GET_TEMPLATES: Store.getTemplates,
|
|
GET_SECURE_FILES_LIST: Store.getSecureFilesList,
|
|
GET_PAD_DATA: Store.getPadData,
|
|
GET_PAD_DATA_FROM_CHANNEL: Store.getPadDataFromChannel,
|
|
GET_STRONGER_HASH: Store.getStrongerHash,
|
|
INCREMENT_TEMPLATE_USE: Store.incrementTemplateUse,
|
|
GET_SHARED_FOLDER: Store.getSharedFolder,
|
|
ADD_SHARED_FOLDER: Store.addSharedFolder,
|
|
LOAD_SHARED_FOLDER: Store.loadSharedFolderAnon,
|
|
RESTORE_SHARED_FOLDER: Store.restoreSharedFolder,
|
|
UPDATE_SHARED_FOLDER_PASSWORD: Store.updateSharedFolderPassword,
|
|
ANSWER_FRIEND_REQUEST: Store.answerFriendRequest,
|
|
SEND_FRIEND_REQUEST: Store.sendFriendRequest,
|
|
ANON_GET_PREVIEW_CONTENT: Store.anonGetPreviewContent,
|
|
OO_COMMAND: Store.onlyoffice.execCommand,
|
|
MAILBOX_COMMAND: Store.mailbox.execCommand,
|
|
UNIVERSAL_COMMAND: Store.universal.execCommand,
|
|
SEND_PAD_MSG: Store.sendPadMsg,
|
|
JOIN_PAD: Store.joinPad,
|
|
LEAVE_PAD: Store.leavePad,
|
|
GET_FULL_HISTORY: Store.getFullHistory,
|
|
GET_HISTORY: Store.getHistory,
|
|
GET_HISTORY_RANGE: Store.getHistoryRange,
|
|
IS_NEW_CHANNEL: Store.isNewChannel,
|
|
CONTACT_PAD_OWNER: Store.contactPadOwner,
|
|
GIVE_PAD_ACCESS: Store.givePadAccess,
|
|
BURN_PAD: Store.burnPad,
|
|
GET_PAD_METADATA: Store.getPadMetadata,
|
|
SET_PAD_METADATA: Store.setPadMetadata,
|
|
CHANGE_PAD_PASSWORD_PIN: Store.changePadPasswordPin,
|
|
GET_LAST_HASH: Store.getLastHash,
|
|
GET_SNAPSHOT: Store.getSnapshot,
|
|
CORRUPTED_CACHE: Store.corruptedCache,
|
|
DELETE_MAILBOX_MESSAGE: Store.deleteMailboxMessage,
|
|
DRIVE_USEROBJECT: Store.userObjectCommand,
|
|
DELETE_ACCOUNT: Store.deleteAccount,
|
|
REMOVE_OWNED_PADS: Store.removeOwnedPads,
|
|
ADMIN_RPC: Store.adminRpc,
|
|
ADMIN_ADD_MAILBOX: Store.addAdminMailbox
|
|
};
|
|
Rpc.query = function(cmd, data, cb) {
|
|
if (queries[cmd]) {
|
|
queries[cmd]("0", data, cb);
|
|
} else {
|
|
console.error("UNHANDLED_STORE_RPC");
|
|
}
|
|
};
|
|
Rpc._removeClient = Store._removeClient;
|
|
return Rpc;
|
|
};
|
|
return {
|
|
create
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireAsyncStore());
|
|
}
|
|
})();
|
|
})(storeRpc);
|
|
return storeRpc.exports;
|
|
}
|
|
var workerChannel = {
|
|
exports: {}
|
|
};
|
|
var hasRequiredWorkerChannel;
|
|
function requireWorkerChannel() {
|
|
if (hasRequiredWorkerChannel) return workerChannel.exports;
|
|
hasRequiredWorkerChannel = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (Util, ApiConfig = {}) => {
|
|
var mkTxid = function() {
|
|
return Math.random().toString(16).replace("0.", "") + Math.random().toString(16).replace("0.", "");
|
|
};
|
|
var create = function(onMsg, postMsg, cb) {
|
|
var chanLoaded;
|
|
var waitingData = [];
|
|
var evReady = Util.mkEvent(true);
|
|
onMsg.reg((function(msg) {
|
|
if (chanLoaded) {
|
|
return;
|
|
}
|
|
var data = msg.data;
|
|
if (data === "_READY") {
|
|
postMsg("_READY");
|
|
chanLoaded = true;
|
|
evReady.fire();
|
|
waitingData.forEach((function(d) {
|
|
onMsg.fire(d);
|
|
}));
|
|
return;
|
|
}
|
|
waitingData.push(data);
|
|
}));
|
|
var handlers = {};
|
|
var queries = {};
|
|
var acks = {};
|
|
var insideHandlers = [];
|
|
var callWhenRegistered = {};
|
|
var chan = {};
|
|
chan.query = function(q, content, cb, opts) {
|
|
var txid = mkTxid();
|
|
opts = opts || {};
|
|
var to = opts.timeout || 3e4;
|
|
var timeout;
|
|
if (to > 0) {
|
|
timeout = setTimeout((function() {
|
|
delete queries[txid];
|
|
cb("TIMEOUT");
|
|
}), to);
|
|
}
|
|
acks[txid] = function(err) {
|
|
clearTimeout(timeout);
|
|
delete acks[txid];
|
|
if (err) {
|
|
delete queries[txid];
|
|
cb("UNHANDLED");
|
|
}
|
|
};
|
|
queries[txid] = function(data, msg) {
|
|
delete queries[txid];
|
|
cb(undefined, data.content, msg);
|
|
};
|
|
evReady.reg((function() {
|
|
var toSend = {
|
|
txid,
|
|
content,
|
|
q,
|
|
raw: opts.raw
|
|
};
|
|
postMsg(opts.raw ? toSend : JSON.stringify(toSend));
|
|
}));
|
|
};
|
|
var event = chan.event = function(e, content, opts) {
|
|
opts = opts || {};
|
|
evReady.reg((function() {
|
|
var toSend = {
|
|
content,
|
|
q: e,
|
|
raw: opts.raw
|
|
};
|
|
postMsg(opts.raw ? toSend : JSON.stringify(toSend));
|
|
}));
|
|
};
|
|
chan.on = function(queryType, handler, quiet) {
|
|
var h = function(data, msg, raw) {
|
|
handler(data.content, (function(replyContent) {
|
|
var toSend = {
|
|
txid: data.txid,
|
|
content: replyContent
|
|
};
|
|
postMsg(raw ? toSend : JSON.stringify(toSend));
|
|
}), msg);
|
|
};
|
|
(handlers[queryType] = handlers[queryType] || []).push(h);
|
|
if (!quiet) {
|
|
event("EV_REGISTER_HANDLER", queryType);
|
|
}
|
|
return {
|
|
stop: function() {
|
|
var idx = handlers[queryType].indexOf(h);
|
|
if (idx === -1) {
|
|
return;
|
|
}
|
|
handlers[queryType].splice(idx, 1);
|
|
}
|
|
};
|
|
};
|
|
chan.whenReg = function(queryType, cb, always) {
|
|
var reg = always;
|
|
if (insideHandlers.indexOf(queryType) > -1) {
|
|
cb();
|
|
} else {
|
|
reg = true;
|
|
}
|
|
if (reg) {
|
|
(callWhenRegistered[queryType] = callWhenRegistered[queryType] || []).push(cb);
|
|
}
|
|
};
|
|
chan.onReg = function(queryType, cb) {
|
|
chan.whenReg(queryType, cb, true);
|
|
};
|
|
chan.on("EV_REGISTER_HANDLER", (function(content) {
|
|
if (callWhenRegistered[content]) {
|
|
callWhenRegistered[content].forEach((function(f) {
|
|
f();
|
|
}));
|
|
delete callWhenRegistered[content];
|
|
}
|
|
insideHandlers.push(content);
|
|
}));
|
|
var isReady = false;
|
|
chan.onReady = function(h) {
|
|
if (isReady) {
|
|
return void h();
|
|
}
|
|
if (typeof h !== "function") {
|
|
return;
|
|
}
|
|
chan.on("EV_RPC_READY", (function() {
|
|
isReady = true;
|
|
h();
|
|
}));
|
|
};
|
|
chan.ready = function() {
|
|
chan.whenReg("EV_RPC_READY", (function() {
|
|
chan.event("EV_RPC_READY");
|
|
}));
|
|
};
|
|
var trusted = [ "" ];
|
|
if (ApiConfig.httpUnsafeOrigin) {
|
|
trusted.push(ApiConfig.httpUnsafeOrigin);
|
|
trusted.push(ApiConfig.httpSafeOrigin);
|
|
} else if (globalThis.location) {
|
|
trusted.push(globalThis.location.origin);
|
|
}
|
|
onMsg.reg((function(msg) {
|
|
if (!chanLoaded) {
|
|
return;
|
|
}
|
|
if (!msg.data || msg.data === "_READY") {
|
|
return;
|
|
}
|
|
if (!trusted.includes(msg.origin)) {
|
|
return;
|
|
}
|
|
var data;
|
|
try {
|
|
data = typeof msg.data === "object" ? msg.data : JSON.parse(msg.data);
|
|
} catch (err) {
|
|
console.warn(err);
|
|
return;
|
|
}
|
|
if (typeof data.ack !== "undefined") {
|
|
if (acks[data.txid]) {
|
|
acks[data.txid](!data.ack);
|
|
}
|
|
} else if (typeof data.q === "string") {
|
|
if (handlers[data.q]) {
|
|
if (data.txid) {
|
|
postMsg(JSON.stringify({
|
|
txid: data.txid,
|
|
ack: true
|
|
}));
|
|
}
|
|
handlers[data.q].forEach((function(f) {
|
|
f(data || JSON.parse(msg.data), msg, data && data.raw);
|
|
data = undefined;
|
|
}));
|
|
} else {
|
|
if (data.txid) {
|
|
postMsg(JSON.stringify({
|
|
txid: data.txid,
|
|
ack: false
|
|
}));
|
|
}
|
|
}
|
|
} else if (typeof data.q === "undefined" && queries[data.txid]) {
|
|
queries[data.txid](data, msg);
|
|
} else ;
|
|
}));
|
|
postMsg("_READY");
|
|
cb(chan);
|
|
};
|
|
return {
|
|
create
|
|
};
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireCommonUtil());
|
|
}
|
|
})();
|
|
})(workerChannel);
|
|
return workerChannel.exports;
|
|
}
|
|
var hasRequired_interface;
|
|
function require_interface() {
|
|
if (hasRequired_interface) return _interface.exports;
|
|
hasRequired_interface = 1;
|
|
(function(module) {
|
|
(() => {
|
|
const factory = (SRpc, Channel, Util) => {
|
|
const Interface = {};
|
|
let store;
|
|
let Rpc;
|
|
let clients = {};
|
|
let closeStore = () => {};
|
|
Interface.init = _closeStore => {
|
|
if (Rpc) {
|
|
return;
|
|
}
|
|
const query = (cId, cmd, data, cb) => {
|
|
cb = cb || function() {};
|
|
clients[cId].chan.query(cmd, data, (function(err, res) {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
};
|
|
const broadcast = (excludes, cmd, data, cb) => {
|
|
cb = cb || function() {};
|
|
Object.keys(clients).forEach((cId => {
|
|
if (excludes.indexOf(+cId) !== -1) {
|
|
return;
|
|
}
|
|
clients[cId].chan.query(cmd, data, ((err, res) => {
|
|
if (err) {
|
|
return void cb({
|
|
error: err
|
|
});
|
|
}
|
|
cb(res);
|
|
}));
|
|
}));
|
|
};
|
|
Rpc = SRpc.create({
|
|
query,
|
|
broadcast
|
|
});
|
|
closeStore = _closeStore;
|
|
};
|
|
Interface.initClient = (cfg, cb) => {
|
|
if (!Rpc) {
|
|
console.error("Not initialized");
|
|
return void cb("NOT_INIT");
|
|
}
|
|
const {postMsg} = cfg;
|
|
const onMsg = Util.mkEvent();
|
|
const clientId = Number(Math.floor(Math.random() * Number.MAX_SAFE_INTEGER));
|
|
const onClose = () => {
|
|
Rpc._removeClient(clientId);
|
|
};
|
|
Channel.create(onMsg, postMsg, (function(chan) {
|
|
let client = clients[clientId] = {
|
|
chan
|
|
};
|
|
console.debug("SharedW Channel created");
|
|
Object.keys(Rpc.queries).forEach((function(q) {
|
|
if (q === "CONNECT") {
|
|
return;
|
|
}
|
|
if (q === "JOIN_PAD") {
|
|
return;
|
|
}
|
|
if (q === "SEND_PAD_MSG") {
|
|
return;
|
|
}
|
|
if (q === "STOPWORKER") {
|
|
return;
|
|
}
|
|
chan.on(q, (function(data, cb) {
|
|
try {
|
|
Rpc.queries[q](clientId, data, cb);
|
|
} catch (e) {
|
|
console.error("Error in webworker when executing query " + q);
|
|
console.error(e);
|
|
console.log(data);
|
|
}
|
|
if (q === "DISCONNECT") {
|
|
onClose();
|
|
if (globalThis.accountDeletion && globalThis.accountDeletion === client.id) {
|
|
Rpc = undefined;
|
|
store = undefined;
|
|
}
|
|
}
|
|
}));
|
|
}));
|
|
chan.on("STOPWORKER", (function(data, cb) {
|
|
closeStore();
|
|
Rpc.queries["DISCONNECT"](clientId, data, cb);
|
|
}));
|
|
chan.on("CONNECT", (function(cfg, cb) {
|
|
console.debug("Connecting to store...");
|
|
Rpc.queries["CONNECT"](clientId, cfg, (function(data) {
|
|
if (data && data.state === "ALREADY_INIT") {
|
|
console.debug("Store already exists!");
|
|
store = store || data.returned;
|
|
return void cb(data);
|
|
}
|
|
store = data;
|
|
cb(data);
|
|
}));
|
|
}));
|
|
chan.on("JOIN_PAD", (function(data, cb) {
|
|
client.channelId = data.channel;
|
|
try {
|
|
Rpc.queries["JOIN_PAD"](clientId, data, cb);
|
|
} catch (e) {
|
|
console.error("Error in webworker when executing query JOIN_PAD");
|
|
console.error(e);
|
|
console.log(data);
|
|
}
|
|
}));
|
|
chan.on("SEND_PAD_MSG", (function(msg, cb) {
|
|
var data = {
|
|
msg,
|
|
channel: client.channelId
|
|
};
|
|
try {
|
|
Rpc.queries["SEND_PAD_MSG"](clientId, data, cb);
|
|
} catch (e) {
|
|
console.error("Error in webworker when executing query SEND_PAD_MSG");
|
|
console.error(e);
|
|
console.log(data);
|
|
}
|
|
}));
|
|
cb(onMsg, onClose);
|
|
}), true);
|
|
};
|
|
return Interface;
|
|
};
|
|
if (module.exports) {
|
|
module.exports = factory(requireStoreRpc(), requireWorkerChannel(), requireCommonUtil());
|
|
}
|
|
})();
|
|
})(_interface);
|
|
return _interface.exports;
|
|
}
|
|
var swConnector;
|
|
var hasRequiredSwConnector;
|
|
function requireSwConnector() {
|
|
if (hasRequiredSwConnector) return swConnector;
|
|
hasRequiredSwConnector = 1;
|
|
const Interface = require_interface();
|
|
let start = setConfig => {
|
|
let ready = false;
|
|
let closeStore = () => {
|
|
globalThis.close();
|
|
};
|
|
let initBuild = cfg => {
|
|
if (ready) {
|
|
return;
|
|
}
|
|
setConfig(cfg);
|
|
Interface.init(closeStore);
|
|
ready = true;
|
|
};
|
|
globalThis.window = globalThis;
|
|
addEventListener("connect", (e => {
|
|
console.error("TEST");
|
|
console.debug("New SharedWorker client");
|
|
const port = e.ports[0];
|
|
const postMsg = data => {
|
|
port.postMessage(data);
|
|
};
|
|
console.error(port);
|
|
let connected = false;
|
|
let onMsg;
|
|
let onClose = () => {};
|
|
port.onmessage = function(e) {
|
|
if (e.data?.type === "INIT") {
|
|
let cfg = e.data.cfg;
|
|
initBuild(cfg);
|
|
if (connected) {
|
|
return;
|
|
}
|
|
connected = true;
|
|
Interface.initClient({
|
|
postMsg
|
|
}, (function(_onMsg, _onClose) {
|
|
onMsg = _onMsg;
|
|
onClose = _onClose;
|
|
postMsg("SW_READY");
|
|
}));
|
|
} else if (e.data === "CLOSE") {
|
|
console.debug("leave");
|
|
onClose();
|
|
} else if (onMsg) {
|
|
onMsg.fire(e);
|
|
}
|
|
};
|
|
}));
|
|
};
|
|
swConnector = {
|
|
start
|
|
};
|
|
return swConnector;
|
|
}
|
|
var swConnectorExports = requireSwConnector();
|
|
var wwConnector;
|
|
var hasRequiredWwConnector;
|
|
function requireWwConnector() {
|
|
if (hasRequiredWwConnector) return wwConnector;
|
|
hasRequiredWwConnector = 1;
|
|
const Interface = require_interface();
|
|
let start = setConfig => {
|
|
let onMsg;
|
|
let ready = false;
|
|
const closeStore = () => {
|
|
globalThis.close();
|
|
};
|
|
const postMsg = data => {
|
|
postMessage(data);
|
|
};
|
|
globalThis.window = globalThis;
|
|
onmessage = function(e) {
|
|
if (e.data?.type === "INIT") {
|
|
let cfg = e.data.cfg;
|
|
if (ready) {
|
|
return;
|
|
}
|
|
setConfig(cfg);
|
|
Interface.init(closeStore);
|
|
ready = true;
|
|
Interface.initClient({
|
|
postMsg
|
|
}, (function(_onMsg) {
|
|
onMsg = _onMsg;
|
|
postMsg("WW_READY");
|
|
}));
|
|
return;
|
|
}
|
|
if (!onMsg) {
|
|
return;
|
|
}
|
|
onMsg.fire(e);
|
|
};
|
|
};
|
|
wwConnector = {
|
|
start
|
|
};
|
|
return wwConnector;
|
|
}
|
|
var wwConnectorExports = requireWwConnector();
|
|
var asyncConnector;
|
|
var hasRequiredAsyncConnector;
|
|
function requireAsyncConnector() {
|
|
if (hasRequiredAsyncConnector) return asyncConnector;
|
|
hasRequiredAsyncConnector = 1;
|
|
const Interface = require_interface();
|
|
const Util = requireCommonUtil();
|
|
let start = setConfig => {
|
|
let ready = false;
|
|
let closed = false;
|
|
let onMsg;
|
|
const sendMsgEv = Util.mkEvent();
|
|
const closeStore = () => {
|
|
closed = true;
|
|
};
|
|
const onMessage = f => {
|
|
sendMsgEv.reg((data => {
|
|
setTimeout((() => {
|
|
f(data);
|
|
}));
|
|
}));
|
|
};
|
|
const postMsg = data => {
|
|
if (closed) {
|
|
return;
|
|
}
|
|
sendMsgEv.fire(data);
|
|
};
|
|
const query = data => {
|
|
if (!onMsg || closed) {
|
|
return;
|
|
}
|
|
onMsg.fire({
|
|
data,
|
|
origin: ""
|
|
});
|
|
};
|
|
let init = cfg => {
|
|
if (ready) {
|
|
return;
|
|
}
|
|
setConfig(cfg);
|
|
Interface.init(closeStore);
|
|
ready = true;
|
|
Interface.initClient({
|
|
postMsg
|
|
}, (function(_onMsg) {
|
|
onMsg = _onMsg;
|
|
postMsg("STORE_READY");
|
|
}));
|
|
};
|
|
return {
|
|
init,
|
|
onMessage,
|
|
query
|
|
};
|
|
};
|
|
asyncConnector = {
|
|
start
|
|
};
|
|
return asyncConnector;
|
|
}
|
|
var asyncConnectorExports = requireAsyncConnector();
|
|
var migrateUserObjectExports = requireMigrateUserObject();
|
|
var migrateUserObject = getDefaultExportFromCjs(migrateUserObjectExports);
|
|
var Migrate = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: migrateUserObject
|
|
}, [ migrateUserObjectExports ]);
|
|
var mailboxExports = requireMailbox();
|
|
var mailbox = getDefaultExportFromCjs(mailboxExports);
|
|
var Mailbox = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: mailbox
|
|
}, [ mailboxExports ]);
|
|
var cursorExports = requireCursor();
|
|
var cursor = getDefaultExportFromCjs(cursorExports);
|
|
var Cursor = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: cursor
|
|
}, [ cursorExports ]);
|
|
var supportExports = requireSupport();
|
|
var support = getDefaultExportFromCjs(supportExports);
|
|
var Support = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: support
|
|
}, [ supportExports ]);
|
|
var calendarExports = requireCalendar();
|
|
var calendar = getDefaultExportFromCjs(calendarExports);
|
|
var Calendar = _mergeNamespaces({
|
|
__proto__: null,
|
|
default: calendar
|
|
}, [ calendarExports ]);
|
|
let start = cfg => {
|
|
[ Feedback, UO, Constants, ProxyManager, NetworkConfig, Migrate, LoginBlock, PadTypes, Credential, Cursor, Support, Calendar, Store, Account, Mailbox ].forEach((dep => {
|
|
if (typeof dep.setCustomize === "function") {
|
|
dep.setCustomize(cfg);
|
|
}
|
|
}));
|
|
};
|
|
let inWorker = typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope;
|
|
let inSharedWorker = typeof SharedWorkerGlobalScope !== "undefined" && self instanceof SharedWorkerGlobalScope;
|
|
exports.store = {};
|
|
if (inSharedWorker) {
|
|
console.error("SHAREDWORKER");
|
|
swConnectorExports.start(start);
|
|
} else if (inWorker) {
|
|
console.error("WEBWORKER");
|
|
wwConnectorExports.start(start);
|
|
} else if (typeof module !== "undefined" && typeof module.exports) {
|
|
console.error("NODEJS");
|
|
exports.store = asyncConnectorExports.start(start);
|
|
} else {
|
|
console.error("BROWSER");
|
|
exports.store = asyncConnectorExports.start(start);
|
|
}
|
|
exports.start = start;
|
|
}));
|