mirror of
https://github.com/cryptpad/cryptpad.git
synced 2026-09-14 11:05:41 +05:00
Placeholder on file deletion
This commit is contained in:
parent
dda4b8777c
commit
91af47994b
@ -94,8 +94,13 @@ const init = (cb) => {
|
||||
}).nThen(cb);
|
||||
};
|
||||
|
||||
COMMANDS.start = (edPublic, blockId) => {
|
||||
COMMANDS.start = (edPublic, blockId, reason) => {
|
||||
const safeKey = Util.escapeKeyCharacters(edPublic);
|
||||
const archiveReason = {
|
||||
code: 'MODERATION_ACCOUNT',
|
||||
txt: reason
|
||||
};
|
||||
|
||||
let ref = {};
|
||||
let blobsToArchive = [];
|
||||
let channelsToArchive = [];
|
||||
@ -142,7 +147,7 @@ COMMANDS.start = (edPublic, blockId) => {
|
||||
// Archive the pads
|
||||
channelsToArchive.forEach((chanId) => {
|
||||
n = n((w) => {
|
||||
Env.store.archiveChannel(chanId, w(function (err) {
|
||||
Env.store.archiveChannel(chanId, archiveReason, w(function (err) {
|
||||
if (err) {
|
||||
return Log.error('MODERATION_CHANNEL_ARCHIVAL_ERROR', {
|
||||
error: err,
|
||||
@ -157,7 +162,7 @@ COMMANDS.start = (edPublic, blockId) => {
|
||||
// Archive the blobs
|
||||
blobsToArchive.forEach((blobId) => {
|
||||
n = n((w) => {
|
||||
Env.blobStore.archive.blob(blobId, w(function (err) {
|
||||
Env.blobStore.archive.blob(blobId, archiveReason, w(function (err) {
|
||||
if (err) {
|
||||
return Log.error('MODERATION_BLOB_ARCHIVAL_ERROR', {
|
||||
error: err,
|
||||
@ -171,7 +176,7 @@ COMMANDS.start = (edPublic, blockId) => {
|
||||
});
|
||||
n(waitFor(() => {
|
||||
// Archive the pin log
|
||||
Env.pinStore.archiveChannel(safeKey, waitFor(function (err) {
|
||||
Env.pinStore.archiveChannel(safeKey, undefined, waitFor(function (err) {
|
||||
if (err) {
|
||||
return Log.error('MODERATION_ACCOUNT_PIN_LOG', err, waitFor());
|
||||
}
|
||||
|
||||
@ -175,20 +175,26 @@ var archiveDocument = function (Env, Server, cb, data) {
|
||||
|
||||
if (typeof(id) !== 'string' || id.length < 32) { return void cb("EINVAL"); }
|
||||
|
||||
const archiveReason = {
|
||||
code: 'MODERATION_PAD',
|
||||
txt: reason
|
||||
};
|
||||
const reasonStr = `MODERATION_PAD:${reason}`;
|
||||
|
||||
switch (id.length) {
|
||||
case 32:
|
||||
return void Env.msgStore.archiveChannel(id, Util.both(cb, function (err) {
|
||||
return void Env.msgStore.archiveChannel(id, archiveReason, Util.both(cb, function (err) {
|
||||
Env.Log.info("ARCHIVAL_CHANNEL_BY_ADMIN_RPC", {
|
||||
channelId: id,
|
||||
reason: reason,
|
||||
status: err? String(err): "SUCCESS",
|
||||
});
|
||||
Channel.disconnectChannelMembers(Env, Server, id, 'EDELETED', err => {
|
||||
Channel.disconnectChannelMembers(Env, Server, id, 'EDELETED', reasonStr, err => {
|
||||
if (err) { } // TODO
|
||||
});
|
||||
}));
|
||||
case 48:
|
||||
return void Env.blobStore.archive.blob(id, Util.both(cb, function (err) {
|
||||
return void Env.blobStore.archive.blob(id, archiveReason, Util.both(cb, function (err) {
|
||||
Env.Log.info("ARCHIVAL_BLOB_BY_ADMIN_RPC", {
|
||||
id: id,
|
||||
reason: reason,
|
||||
@ -213,7 +219,7 @@ var removeDocument = function (Env, Server, cb, data) {
|
||||
id = args;
|
||||
} else if (args && typeof(args) === 'object') {
|
||||
id = args.id;
|
||||
reason = args.reason;
|
||||
reason = `MODERATION_DESTROY:${args.reason}`;
|
||||
}
|
||||
|
||||
if (typeof(id) !== 'string' || id.length < 32) { return void cb("EINVAL"); }
|
||||
@ -226,7 +232,7 @@ var removeDocument = function (Env, Server, cb, data) {
|
||||
reason: reason,
|
||||
status: err? String(err): "SUCCESS",
|
||||
});
|
||||
Channel.disconnectChannelMembers(Env, Server, id, 'EDELETED', err => {
|
||||
Channel.disconnectChannelMembers(Env, Server, id, 'EDELETED', reason, err => {
|
||||
if (err) { } // TODO
|
||||
});
|
||||
}));
|
||||
@ -291,17 +297,20 @@ var archiveAccount = function (Env, Server, _cb, data) {
|
||||
return worker.postMessage({
|
||||
command: 'start',
|
||||
content: data && data[1],
|
||||
block: data && data[2]
|
||||
block: data && data[2],
|
||||
reason: data && data[3]
|
||||
});
|
||||
}
|
||||
|
||||
// DONE: disconnect all users from these channels
|
||||
const reason = `MODERATION_ACCOUNT:${data && data[3]}`;
|
||||
var deletedChannels = Util.tryParse(message);
|
||||
if (Array.isArray(deletedChannels)) {
|
||||
let n = nThen;
|
||||
deletedChannels.forEach((chanId) => {
|
||||
n = n((w) => {
|
||||
setTimeout(w(() => {
|
||||
Channel.disconnectChannelMembers(Env, Server, chanId, 'EDELETED', () => {});
|
||||
Channel.disconnectChannelMembers(Env, Server, chanId, 'EDELETED', reason, () => {});
|
||||
}), 10);
|
||||
}).nThen;
|
||||
});
|
||||
@ -645,7 +654,7 @@ var archivePinLog = function (Env, Server, cb, data) {
|
||||
if (!isValidKey(key)) { return void cb("EINVAL"); }
|
||||
var safeKey = Util.escapeKeyCharacters(key);
|
||||
|
||||
Env.pinStore.archiveChannel(safeKey, function (err) {
|
||||
Env.pinStore.archiveChannel(safeKey, undefined, function (err) {
|
||||
Core.expireSession(Env.Sessions, safeKey);
|
||||
if (err) {
|
||||
Env.Log.error('ARCHIVE_PIN_LOG_BY_ADMIN', {
|
||||
|
||||
@ -8,7 +8,7 @@ const Metadata = require("./metadata");
|
||||
const HK = require("../hk-util");
|
||||
const Nacl = require("tweetnacl/nacl-fast");
|
||||
|
||||
Channel.disconnectChannelMembers = function (Env, Server, channelId, code, cb) {
|
||||
Channel.disconnectChannelMembers = function (Env, Server, channelId, code, reason, cb) {
|
||||
var done = Util.once(Util.mkAsync(cb));
|
||||
if (!Core.isValidId(channelId)) { return done('INVALID_ID'); }
|
||||
|
||||
@ -39,6 +39,7 @@ Channel.disconnectChannelMembers = function (Env, Server, channelId, code, cb) {
|
||||
userId,
|
||||
JSON.stringify({
|
||||
error: code, //'EDELETED',
|
||||
message: reason,
|
||||
channel: channelId,
|
||||
})
|
||||
], w());
|
||||
@ -52,6 +53,7 @@ Channel.disconnectChannelMembers = function (Env, Server, channelId, code, cb) {
|
||||
Env.Log.warn('DISCONNECT_CHANNEL_MEMBERS_TIMEOUT', {
|
||||
channelId,
|
||||
code,
|
||||
reason
|
||||
});
|
||||
clear();
|
||||
done();
|
||||
@ -105,9 +107,10 @@ Channel.clearOwnedChannel = function (Env, safeKey, channelId, cb, Server) {
|
||||
});
|
||||
};
|
||||
|
||||
var archiveOwnedChannel = function (Env, safeKey, channelId, __cb, Server) {
|
||||
var archiveOwnedChannel = function (Env, safeKey, channelId, reason, __cb, Server) {
|
||||
var _cb = Util.once(Util.mkAsync(__cb));
|
||||
var unsafeKey = Util.unescapeKeyCharacters(safeKey);
|
||||
reason = reason || 'ARCHIVE_OWNED';
|
||||
nThen(function (w) {
|
||||
// confirm that the channel exists before worrying about whether
|
||||
// we have permission to delete it.
|
||||
@ -130,7 +133,7 @@ var archiveOwnedChannel = function (Env, safeKey, channelId, __cb, Server) {
|
||||
}).nThen(function () {
|
||||
var cb = _cb;
|
||||
// temporarily archive the file
|
||||
return void Env.msgStore.archiveChannel(channelId, function (e) {
|
||||
return void Env.msgStore.archiveChannel(channelId, reason, function (e) {
|
||||
Env.Log.info('ARCHIVAL_CHANNEL_BY_OWNER_RPC', {
|
||||
unsafeKey: unsafeKey,
|
||||
channelId: channelId,
|
||||
@ -141,16 +144,19 @@ var archiveOwnedChannel = function (Env, safeKey, channelId, __cb, Server) {
|
||||
}
|
||||
cb(void 0, 'OK');
|
||||
|
||||
Channel.disconnectChannelMembers(Env, Server, channelId, 'EDELETED', err => {
|
||||
Channel.disconnectChannelMembers(Env, Server, channelId, 'EDELETED', reason, err => {
|
||||
if (err) { } // TODO
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Channel.removeOwnedChannel = function (Env, safeKey, channelId, __cb, Server) {
|
||||
Channel.removeOwnedChannel = function (Env, safeKey, obj, __cb, Server) {
|
||||
var _cb = Util.once(Util.mkAsync(__cb));
|
||||
|
||||
var channelId = obj.channel;
|
||||
var reason = obj.reason;
|
||||
|
||||
if (typeof(channelId) !== 'string' || !Core.isValidId(channelId)) {
|
||||
return _cb('INVALID_ARGUMENTS');
|
||||
}
|
||||
@ -160,9 +166,9 @@ Channel.removeOwnedChannel = function (Env, safeKey, channelId, __cb, Server) {
|
||||
Env.queueDeletes(safeKey, function (next) {
|
||||
var cb = Util.both(_cb, next);
|
||||
if (Env.blobStore.isFileId(channelId)) {
|
||||
return void Env.removeOwnedBlob(channelId, safeKey, cb);
|
||||
return void Env.removeOwnedBlob(channelId, safeKey, reason, cb);
|
||||
}
|
||||
archiveOwnedChannel(Env, safeKey, channelId, cb, Server);
|
||||
archiveOwnedChannel(Env, safeKey, channelId, reason, cb, Server);
|
||||
});
|
||||
};
|
||||
|
||||
@ -258,15 +264,18 @@ Channel.isNewChannel = function (Env, channel, cb) {
|
||||
var msg = msgObj.buff.toString('utf8');
|
||||
if (typeof(msg) === 'string' && ARRAY_LINE.test(msg)) {
|
||||
abort();
|
||||
return void cb(void 0, false);
|
||||
return void cb(void 0, {isNew: false});
|
||||
}
|
||||
} catch (e) {
|
||||
Env.WARN('invalid message read from store', e);
|
||||
}
|
||||
readMore();
|
||||
}, function () {
|
||||
}, function (err, reason) {
|
||||
// no more messages...
|
||||
cb(void 0, true);
|
||||
cb(void 0, {
|
||||
isNew: true,
|
||||
reason: reason
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@ -115,7 +115,7 @@ Pinning.getTotalSize = function (Env, safeKey, cb) {
|
||||
*/
|
||||
Pinning.removePins = function (Env, safeKey, cb) {
|
||||
// FIXME respect the queue
|
||||
Env.pinStore.archiveChannel(safeKey, function (err) {
|
||||
Env.pinStore.archiveChannel(safeKey, undefined, function (err) {
|
||||
Core.expireSession(Env.Sessions, safeKey);
|
||||
Env.Log.info('ARCHIVAL_PIN_BY_OWNER_RPC', {
|
||||
safeKey: safeKey,
|
||||
|
||||
@ -540,7 +540,7 @@ module.exports = function (Env, cb) {
|
||||
}
|
||||
|
||||
// remove the pin logs of inactive accounts if inactive account removal is configured
|
||||
pinStore.archiveChannel(id, function (err) {
|
||||
pinStore.archiveChannel(id, undefined, function (err) {
|
||||
if (err) {
|
||||
return Log.error('EVICT_INACTIVE_ACCOUNT_PIN_LOG', err, next);
|
||||
}
|
||||
@ -599,7 +599,7 @@ module.exports = function (Env, cb) {
|
||||
if (item.mtime > inactiveTime) { return void next(); }
|
||||
|
||||
removed++;
|
||||
blobs.archive.blob(item.blobId, function (err) {
|
||||
blobs.archive.blob(item.blobId, 'INACTIVE', function (err) {
|
||||
if (err) {
|
||||
return Log.error("EVICT_ARCHIVE_BLOB_ERROR", {
|
||||
error: err,
|
||||
@ -724,7 +724,7 @@ module.exports = function (Env, cb) {
|
||||
// else fall through to the archival
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
return void store.archiveChannel(item.channel, w(function (err) {
|
||||
return void store.archiveChannel(item.channel, 'INACTIVE', w(function (err) {
|
||||
if (err) {
|
||||
Log.error('EVICT_CHANNEL_ARCHIVAL_ERROR', {
|
||||
error: err,
|
||||
|
||||
@ -124,7 +124,7 @@ var CHECKPOINT_PATTERN = /^cp\|(([A-Za-z0-9+\/=]+)\|)?/;
|
||||
but for some reason are still present
|
||||
*/
|
||||
const expireChannel = HK.expireChannel = function (Env, channel) {
|
||||
return void Env.store.archiveChannel(channel, function (err) {
|
||||
return void Env.store.archiveChannel(channel, 'EXPIRED', function (err) {
|
||||
Env.Log.info("ARCHIVAL_CHANNEL_BY_HISTORY_KEEPER_EXPIRATION", {
|
||||
channelId: channel,
|
||||
status: err? String(err): "SUCCESS",
|
||||
@ -526,7 +526,12 @@ const getHistoryAsync = (Env, channelName, lastKnownHash, beforeHash, handler, c
|
||||
getHistoryOffset(Env, channelName, lastKnownHash, waitFor((err, os) => {
|
||||
if (err) {
|
||||
waitFor.abort();
|
||||
return void cb(err);
|
||||
var reason;
|
||||
if (err && err.reason) {
|
||||
reason = err.reason;
|
||||
err = err.error;
|
||||
}
|
||||
return void cb(err, reason);
|
||||
}
|
||||
offset = os;
|
||||
}));
|
||||
@ -540,8 +545,8 @@ const getHistoryAsync = (Env, channelName, lastKnownHash, beforeHash, handler, c
|
||||
const parsed = tryParse(Env, msgObj.buff.toString('utf8'));
|
||||
if (!parsed) { return void readMore(); }
|
||||
handler(parsed, readMore);
|
||||
}, waitFor(function (err) {
|
||||
return void cb(err);
|
||||
}, waitFor(function (err, reason) {
|
||||
return void cb(err, reason);
|
||||
}));
|
||||
});
|
||||
};
|
||||
@ -682,7 +687,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
|
||||
if (isMetadataMessage(msg) && metadata_cache[channelName]) { return readMore(); }
|
||||
if (txid) { msg[0] = txid; }
|
||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(msg)], readMore);
|
||||
}, (err) => {
|
||||
}, (err, reason) => {
|
||||
// Any error but ENOENT: abort
|
||||
// ENOENT is allowed in case we want to create a new pad
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
@ -700,10 +705,15 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
|
||||
stack: err && err.stack,
|
||||
}); }
|
||||
// FIXME err.message isn't useful for users
|
||||
const parsedMsg = {error:err.message, channel: channelName, txid: txid};
|
||||
const parsedMsg = {error:err.message, test:'a', channel: channelName, txid: txid};
|
||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg)]);
|
||||
return;
|
||||
}
|
||||
if (err && err.code === 'ENOENT' && reason) {
|
||||
const parsedMsg2 = {error:'EDELETED', message: reason, channel: channelName, txid: txid};
|
||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg2)]);
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're asking for a specific version (lastKnownHash) but we receive an
|
||||
// ENOENT, this is not a pad creation so we need to abort.
|
||||
@ -716,7 +726,7 @@ const handleGetHistory = function (Env, Server, seq, userId, parsed) {
|
||||
is no longer on the server so they don't abuse the data and so that they don't unintentionally continue
|
||||
to edit it in a broken state.
|
||||
*/
|
||||
const parsedMsg2 = {error:'EDELETED', channel: channelName, txid: txid};
|
||||
const parsedMsg2 = {error:'EDELETED', test:'b',channel: channelName, txid: txid};
|
||||
Server.send(userId, [0, HISTORY_KEEPER_ID, 'MSG', userId, JSON.stringify(parsedMsg2)]);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -47,6 +47,10 @@ var makeProofPath = function (Env, safeKey, blobId) {
|
||||
return Path.join(Env.blobPath, safeKey.slice(0, 3), safeKey, blobId.slice(0, 2), blobId);
|
||||
};
|
||||
|
||||
var mkPlaceholderPath = function (Env, blobId) {
|
||||
return makeBlobPath(Env, blobId) + '.placeholder';
|
||||
};
|
||||
|
||||
var parseProofPath = function (path) {
|
||||
var parts = path.split('/');
|
||||
return {
|
||||
@ -55,6 +59,26 @@ var parseProofPath = function (path) {
|
||||
};
|
||||
};
|
||||
|
||||
// Placeholder for deleted files
|
||||
var addPlaceholder = function (Env, blobId, reason, cb) {
|
||||
if (!reason) { return cb(); }
|
||||
var path = mkPlaceholderPath(Env, blobId);
|
||||
var s_data = typeof(reason) === "string" ? reason : `${reason.code}:${reason.txt}`;
|
||||
Fs.writeFile(path, s_data, cb);
|
||||
};
|
||||
var clearPlaceholder = function (Env, blobId, cb) {
|
||||
var path = mkPlaceholderPath(Env, blobId);
|
||||
Fs.unlink(path, cb);
|
||||
};
|
||||
var readPlaceholder = function (Env, blobId, cb) {
|
||||
var path = mkPlaceholderPath(Env, blobId);
|
||||
Fs.readFile(path, function (err, content) {
|
||||
if (err) { return void cb(); }
|
||||
cb(content.toString('utf8'));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// getUploadSize: used by
|
||||
// getFileSize
|
||||
var getUploadSize = function (Env, blobId, cb) {
|
||||
@ -63,7 +87,16 @@ var getUploadSize = function (Env, blobId, cb) {
|
||||
Fs.stat(path, function (err, stats) {
|
||||
if (err) {
|
||||
// if a file was deleted, its size is 0 bytes
|
||||
if (err.code === 'ENOENT') { return cb(void 0, 0); }
|
||||
if (err.code === 'ENOENT') {
|
||||
return readPlaceholder(Env, blobId, (content) => {
|
||||
if (!content) { return cb(void 0, 0); }
|
||||
cb({
|
||||
code: err.code,
|
||||
reason: content
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
return void cb(err.code);
|
||||
}
|
||||
cb(void 0, stats.size);
|
||||
@ -345,6 +378,7 @@ var owned_upload_complete = function (Env, safeKey, id, cb) {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// removeBlob
|
||||
var remove = function (Env, blobId, cb) {
|
||||
var blobPath = makeBlobPath(Env, blobId);
|
||||
@ -366,11 +400,12 @@ var isOwnedBy = function (Env, safeKey, blobId, cb) {
|
||||
|
||||
|
||||
// archiveBlob
|
||||
var archiveBlob = function (Env, blobId, cb) {
|
||||
var archiveBlob = function (Env, blobId, reason, cb) {
|
||||
var blobPath = makeBlobPath(Env, blobId);
|
||||
var archivePath = prependArchive(Env, blobPath);
|
||||
Fse.move(blobPath, archivePath, { overwrite: true }, cb);
|
||||
archiveActivity(Env, blobId, () => {});
|
||||
addPlaceholder(Env, blobId, reason, () => {});
|
||||
};
|
||||
|
||||
var removeArchivedBlob = function (Env, blobId, cb) {
|
||||
@ -385,6 +420,7 @@ var restoreBlob = function (Env, blobId, cb) {
|
||||
var archivePath = prependArchive(Env, blobPath);
|
||||
Fse.move(archivePath, blobPath, cb);
|
||||
restoreActivity(Env, blobId, () => {});
|
||||
clearPlaceholder(Env, blobId, () => {});
|
||||
};
|
||||
|
||||
// archiveProof
|
||||
@ -614,10 +650,10 @@ BlobStore.create = function (config, _cb) {
|
||||
},
|
||||
|
||||
archive: {
|
||||
blob: function (blobId, _cb) {
|
||||
blob: function (blobId, reason, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
if (!isValidId(blobId)) { return void cb("INVALID_ID"); }
|
||||
archiveBlob(Env, blobId, cb);
|
||||
archiveBlob(Env, blobId, reason, cb);
|
||||
},
|
||||
proof: function (safeKey, blobId, _cb) {
|
||||
var cb = Util.once(Util.mkAsync(_cb));
|
||||
|
||||
@ -70,6 +70,10 @@ var mkOffsetPath = function (env, channelId) {
|
||||
return mkPath(env, channelId) + '.offset';
|
||||
};
|
||||
|
||||
var mkPlaceholderPath = function (env, channelId) {
|
||||
return mkPath(env, channelId) + '.placeholder';
|
||||
};
|
||||
|
||||
// pass in the path so we can reuse the same function for archived files
|
||||
var channelExists = function (filepath, cb) {
|
||||
Fs.stat(filepath, function (err, stat) {
|
||||
@ -141,6 +145,25 @@ var isChannelArchived = function (env, channelName, cb) {
|
||||
});
|
||||
};
|
||||
|
||||
var addPlaceholder = function (env, channelId, reason, cb) {
|
||||
if (!reason) { return cb(); }
|
||||
var path = mkPlaceholderPath(env, channelId);
|
||||
var s_data = typeof(reason) === "string" ? reason : `${reason.code}:${reason.txt}`;
|
||||
Fs.writeFile(path, s_data, cb);
|
||||
};
|
||||
var clearPlaceholder = function (env, channelId, cb) {
|
||||
var path = mkPlaceholderPath(env, channelId);
|
||||
Fs.unlink(path, cb);
|
||||
};
|
||||
var readPlaceholder = function (env, channelId, cb) {
|
||||
var path = mkPlaceholderPath(env, channelId);
|
||||
Fs.readFile(path, function (err, content) {
|
||||
if (err) { return void cb(); }
|
||||
cb(content.toString('utf8'));
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const destroyStream = function (stream) {
|
||||
if (!stream) { return; }
|
||||
try {
|
||||
@ -190,7 +213,16 @@ const readMessagesBin = (env, id, start, msgHandler, cb) => {
|
||||
const stream = Fs.createReadStream(mkPath(env, id), { start: start });
|
||||
const collector = createIdleStreamCollector(stream);
|
||||
const handleMessageAndKeepStreamAlive = Util.both(msgHandler, collector.keepAlive);
|
||||
const done = Util.both(cb, collector);
|
||||
const done = Util.both((err) => {
|
||||
if (err && err.code === 'ENOENT') {
|
||||
// If the channel doesn't exists, look for a placeholder.
|
||||
// If a placeholder exists, call back with its content in addition to the original error
|
||||
return readPlaceholder(env, id, (content) => {
|
||||
cb(err, content);
|
||||
});
|
||||
}
|
||||
cb(err);
|
||||
}, collector);
|
||||
return void readFileBin(stream, handleMessageAndKeepStreamAlive, done, {
|
||||
offset: start,
|
||||
});
|
||||
@ -670,7 +702,7 @@ var listChannels = function (root, handler, cb, fast) {
|
||||
|
||||
// move a channel's log file from its current location
|
||||
// to an equivalent location in the cold storage directory
|
||||
var archiveChannel = function (env, channelName, cb) {
|
||||
var archiveChannel = function (env, channelName, reason, cb) {
|
||||
// TODO close channels before archiving them?
|
||||
|
||||
// ctime is the most reliable indicator of when a file was archived
|
||||
@ -700,6 +732,9 @@ var archiveChannel = function (env, channelName, cb) {
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
clearOffset(env, channelName, w());
|
||||
}).nThen(function (w) {
|
||||
if (!reason) { return; }
|
||||
addPlaceholder(env, channelName, reason, w());
|
||||
}).nThen(function (w) {
|
||||
// archive the dedicated metadata channel
|
||||
var metadataPath = mkMetadataPath(env, channelName);
|
||||
@ -775,6 +810,8 @@ var unarchiveChannel = function (env, channelName, cb) {
|
||||
return void CB(err);
|
||||
}
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
clearPlaceholder(env, channelName, w());
|
||||
}).nThen(function (w) {
|
||||
var archiveMetadataPath = mkArchiveMetadataPath(env, channelName);
|
||||
// TODO validate that it's ok to move metadata non-atomically
|
||||
@ -1290,12 +1327,12 @@ module.exports.create = function (conf, _cb) {
|
||||
isChannelArchived(env, channelName, cb);
|
||||
},
|
||||
// move a channel from the database to the archive, along with its metadata
|
||||
archiveChannel: function (channelName, cb) {
|
||||
archiveChannel: function (channelName, reason, cb) {
|
||||
if (!isValidChannelId(channelName)) { return void cb(new Error('EINVAL')); }
|
||||
// again, the semantics around archiving and appending are really muddy.
|
||||
// so I'm calling this 'unordered' again
|
||||
schedule.unordered(channelName, function (next) {
|
||||
archiveChannel(env, channelName, Util.both(cb, next));
|
||||
archiveChannel(env, channelName, reason, Util.both(cb, next));
|
||||
});
|
||||
},
|
||||
// restore a channel from the archive to the database, along with its metadata
|
||||
|
||||
@ -499,8 +499,13 @@ const getHashOffset = function (data, cb) {
|
||||
}
|
||||
offset = msgObj.offset;
|
||||
abort();
|
||||
}, function (err) {
|
||||
if (err) { return void cb(err); }
|
||||
}, function (err, reason) {
|
||||
if (err) {
|
||||
return void cb({
|
||||
error: err,
|
||||
reason: reason
|
||||
});
|
||||
}
|
||||
cb(void 0, offset);
|
||||
});
|
||||
};
|
||||
@ -510,6 +515,8 @@ const removeOwnedBlob = function (data, cb) {
|
||||
const blobId = data.blobId;
|
||||
const safeKey = Util.escapeKeyCharacters(data.safeKey);
|
||||
|
||||
const reason = data.reason || 'ARCHIVE_OWNED';
|
||||
|
||||
nThen(function (w) {
|
||||
// check if you have permissions
|
||||
blobStore.isOwnedBy(safeKey, blobId, w(function (err, owned) {
|
||||
@ -520,7 +527,7 @@ const removeOwnedBlob = function (data, cb) {
|
||||
}));
|
||||
}).nThen(function (w) {
|
||||
// remove the blob
|
||||
blobStore.archive.blob(blobId, w(function (err) {
|
||||
blobStore.archive.blob(blobId, reason, w(function (err) {
|
||||
Env.Log.info('ARCHIVAL_OWNED_FILE_BY_OWNER_RPC', {
|
||||
safeKey: safeKey,
|
||||
blobId: blobId,
|
||||
|
||||
@ -382,11 +382,12 @@ Workers.initialize = function (Env, config, _cb) {
|
||||
});
|
||||
};
|
||||
|
||||
Env.removeOwnedBlob = function (blobId, safeKey, cb) {
|
||||
Env.removeOwnedBlob = function (blobId, safeKey, reason, cb) {
|
||||
sendCommand({
|
||||
command: 'REMOVE_OWNED_BLOB',
|
||||
blobId: blobId,
|
||||
safeKey: safeKey,
|
||||
reason: reason
|
||||
}, cb);
|
||||
};
|
||||
|
||||
|
||||
@ -2876,6 +2876,15 @@ define([
|
||||
delete autoStoreModal[priv.channel];
|
||||
}
|
||||
|
||||
if (err.message && err.message !== "PASSWORD_CHANGE") {
|
||||
UI.errorLoadingScreen(h('div', [
|
||||
h('p', Messages.deletedError),
|
||||
h('p', err.message) // XXX custom message for each reason
|
||||
// XXX reasons: ARCHIVE_OWNED, INACTIVE, MODERATION_ACCOUNT:{reason}, MODERATION_PAD:{reason}
|
||||
]), Boolean(err.loaded), Boolean(err.loaded));
|
||||
return;
|
||||
}
|
||||
|
||||
if (err.ownDeletion) {
|
||||
if (toolbar && typeof toolbar.deleted === "function") { toolbar.deleted(); }
|
||||
(cb || function () {})();
|
||||
@ -2921,6 +2930,12 @@ define([
|
||||
var error;
|
||||
if (isError) { error = setHTML(h('p.cp-password-error'), Messages.password_error); }
|
||||
|
||||
// XXX
|
||||
if (cfg.legacy) {
|
||||
// XXX Legacy mode: we don't know if pad destroyed or password changed
|
||||
}
|
||||
|
||||
|
||||
var info = h('p.cp-password-info', Messages.password_info);
|
||||
var info_loaded = setHTML(h('p.cp-password-info'), Messages.errorCopy);
|
||||
|
||||
|
||||
@ -649,15 +649,23 @@ define([
|
||||
|
||||
}).nThen(function (waitFor) {
|
||||
// If it's not in the cache or it's not a blob, try to get the value from the server
|
||||
postMessage("GET_FILE_SIZE", {channel:channel}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
// If disconnected, try to get the value from the channel cache (next nThen)
|
||||
error = obj.error;
|
||||
return;
|
||||
}
|
||||
waitFor.abort();
|
||||
cb(undefined, obj.size);
|
||||
}));
|
||||
var getSize = () => {
|
||||
postMessage("GET_FILE_SIZE", {channel:channel}, waitFor(function (obj) {
|
||||
if (obj && obj.error === "ANON_RPC_NOT_READY") { return void setTimeout(waitFor(getSize), 100); }
|
||||
|
||||
if (obj && obj.error && obj.error.code === 'ENOENT' && obj.error.reason) {
|
||||
waitFor.abort();
|
||||
cb(obj.error.reason);
|
||||
} else if (obj && obj.error) {
|
||||
// If disconnected, try to get the value from the channel cache (next nThen)
|
||||
error = obj.error;
|
||||
return;
|
||||
}
|
||||
waitFor.abort();
|
||||
cb(undefined, obj.size);
|
||||
}));
|
||||
};
|
||||
getSize();
|
||||
}).nThen(function () {
|
||||
Cache.getChannelCache(channel, function(err, data) {
|
||||
if (err) { return void cb(error); }
|
||||
@ -681,7 +689,7 @@ define([
|
||||
var error = obj && obj.error;
|
||||
if (error) { return void cb(error); }
|
||||
if (!obj) { return void cb('ERROR'); }
|
||||
cb (null, obj.isNew);
|
||||
cb (null, obj.isNew, obj.reason);
|
||||
}, {timeout: -1});
|
||||
};
|
||||
// This function is used when we want to open a pad. We first need
|
||||
@ -711,7 +719,7 @@ define([
|
||||
} else if (error) {
|
||||
return void cb(error);
|
||||
}
|
||||
cb(undefined, obj.isNew);
|
||||
cb(undefined, obj.isNew, obj.reason);
|
||||
}, {timeout: -1});
|
||||
};
|
||||
isNew();
|
||||
@ -1484,7 +1492,8 @@ define([
|
||||
// delete the old pad
|
||||
common.removeOwnedChannel({
|
||||
channel: oldChannel,
|
||||
teamId: teamId
|
||||
teamId: teamId,
|
||||
reason: 'PASSWORD_CHANGE',
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
waitFor.abort();
|
||||
@ -1620,7 +1629,8 @@ define([
|
||||
// delete the old pad
|
||||
common.removeOwnedChannel({
|
||||
channel: oldChannel,
|
||||
teamId: teamId
|
||||
teamId: teamId,
|
||||
reason: 'PASSWORD_CHANGE'
|
||||
}, waitFor(function (obj) {
|
||||
if (obj && obj.error) {
|
||||
waitFor.abort();
|
||||
|
||||
@ -1152,10 +1152,36 @@ define([
|
||||
|
||||
var FILTER_BY = "filterBy";
|
||||
|
||||
var refreshDeprecated = function () {
|
||||
if (!APP.passwordModal) { return; }
|
||||
var deprecated = files.sharedFoldersTemp;
|
||||
if (JSONSortify(deprecated) === APP.deprecatedSF) { return; }
|
||||
APP.deprecatedSF = JSONSortify(deprecated);
|
||||
if (typeof (deprecated) === "object" && Object.keys(deprecated).length) {
|
||||
var nt = nThen;
|
||||
Object.keys(deprecated).forEach(function (fId) {
|
||||
var data = deprecated[fId];
|
||||
var sfId = manager.user.userObject.getSFIdFromHref(data.href);
|
||||
if (folders[fId] || sfId) { // This shared folder is already stored in the drive...
|
||||
return void manager.delete([['sharedFoldersTemp', fId]], function () { });
|
||||
}
|
||||
nt = nt(function (waitFor) {
|
||||
UI.openCustomModal(APP.passwordModal(fId, data, waitFor()));
|
||||
}).nThen;
|
||||
});
|
||||
nt(function () {
|
||||
APP.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
var refresh = APP.refresh = function (cb) {
|
||||
var type = APP.store[FILTER_BY];
|
||||
var path = type ? [FILTER, type, currentPath] : currentPath;
|
||||
APP.displayDirectory(path, undefined, cb);
|
||||
APP.displayDirectory(path, undefined, () => {
|
||||
refreshDeprecated();
|
||||
if (typeof(cb) === "function") { cb(); }
|
||||
});
|
||||
};
|
||||
|
||||
// `app`: true (force open wiht the app), false (force open in preview),
|
||||
@ -5370,9 +5396,13 @@ define([
|
||||
});
|
||||
}
|
||||
*/
|
||||
var nt = nThen;
|
||||
var passwordModal = function (fId, data, cb) {
|
||||
APP.passwordModal = function (fId, data, cb) {
|
||||
var content = [];
|
||||
|
||||
var legacy = data.legacy; // XXX in legacy mode, it's either deletion or password change
|
||||
legacy = legacy;
|
||||
// XXX when legacy is false, we know it's a password change
|
||||
|
||||
var folderName = '<b>'+ (data.lastTitle || Messages.fm_newFolder) +'</b>';
|
||||
content.push(UI.setHTML(h('p'), Messages._getKey('drive_sfPassword', [folderName])));
|
||||
var newPassword = UI.passwordInput({
|
||||
@ -5425,24 +5455,7 @@ define([
|
||||
onClose: cb
|
||||
});
|
||||
};
|
||||
onConnectEvt.reg(function () {
|
||||
var deprecated = files.sharedFoldersTemp;
|
||||
if (typeof (deprecated) === "object" && Object.keys(deprecated).length) {
|
||||
Object.keys(deprecated).forEach(function (fId) {
|
||||
var data = deprecated[fId];
|
||||
var sfId = manager.user.userObject.getSFIdFromHref(data.href);
|
||||
if (folders[fId] || sfId) { // This shared folder is already stored in the drive...
|
||||
return void manager.delete([['sharedFoldersTemp', fId]], function () { });
|
||||
}
|
||||
nt = nt(function (waitFor) {
|
||||
UI.openCustomModal(passwordModal(fId, data, waitFor()));
|
||||
}).nThen;
|
||||
});
|
||||
nt(function () {
|
||||
refresh();
|
||||
});
|
||||
}
|
||||
});
|
||||
onConnectEvt.reg(refreshDeprecated);
|
||||
|
||||
return {
|
||||
refresh: refresh,
|
||||
|
||||
@ -365,10 +365,12 @@ define([
|
||||
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) {
|
||||
@ -385,7 +387,7 @@ define([
|
||||
s.rpc.removeOwnedChannel(channel, function (err) {
|
||||
if (err) { delete myDeletions[channel]; }
|
||||
cb({error:err});
|
||||
});
|
||||
}, reason);
|
||||
};
|
||||
|
||||
var arePinsSynced = function (cb) {
|
||||
@ -512,11 +514,9 @@ define([
|
||||
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]) === 'boolean') {
|
||||
if (response[0]) { Cache.clearChannel(channelId); }
|
||||
return void cb({
|
||||
isNew: response[0]
|
||||
});
|
||||
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'});
|
||||
}
|
||||
|
||||
@ -251,8 +251,8 @@ define([
|
||||
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]) === 'boolean') {
|
||||
isNew = response[0];
|
||||
if (response && response.length && typeof(response[0]) === 'object') {
|
||||
isNew = response[0].isNew;
|
||||
} else {
|
||||
cb({error: 'INVALID_RESPONSE'});
|
||||
}
|
||||
|
||||
@ -120,7 +120,7 @@ define([
|
||||
}).nThen(function (waitFor) {
|
||||
isNewChannel(null, { channel: secret.channel }, waitFor(function (obj) {
|
||||
if (obj.isNew && !isNew) {
|
||||
store.manager.deprecateProxy(id, secret.channel);
|
||||
store.manager.deprecateProxy(id, secret.channel, obj.reason);
|
||||
waitFor.abort();
|
||||
return void cb(null);
|
||||
}
|
||||
@ -245,7 +245,7 @@ define([
|
||||
// Deprecate the shared folder from each team
|
||||
// We can only hide it
|
||||
sf.teams.forEach(function (obj) {
|
||||
obj.store.manager.deprecateProxy(obj.id, secret.channel);
|
||||
obj.store.manager.deprecateProxy(obj.id, secret.channel, info.message);
|
||||
if (obj.store.handleSharedFolder) {
|
||||
obj.store.handleSharedFolder(obj.id, null);
|
||||
}
|
||||
|
||||
@ -423,7 +423,7 @@ define([
|
||||
};
|
||||
if (channel) {
|
||||
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", channel, waitFor(function (e, res) {
|
||||
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
|
||||
if (res && res.length && typeof(res[0]) === 'object' && res[0].isNew) {
|
||||
// Channel is empty: remove this team
|
||||
close();
|
||||
}
|
||||
@ -431,7 +431,7 @@ define([
|
||||
}
|
||||
if (roster) {
|
||||
ctx.store.anon_rpc.send("IS_NEW_CHANNEL", roster, waitFor(function (e, res) {
|
||||
if (res && res.length && typeof(res[0]) === 'boolean' && res[0]) {
|
||||
if (res && res.length && typeof(res[0]) === 'object' && res[0].isNew) {
|
||||
// Channel is empty: remove this team
|
||||
close();
|
||||
}
|
||||
|
||||
@ -118,13 +118,14 @@ define([
|
||||
cb(null, id);
|
||||
};
|
||||
|
||||
exp.deprecateSharedFolder = function (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) {
|
||||
files[SHARED_FOLDERS_TEMP][id] = JSON.parse(JSON.stringify(data));
|
||||
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);
|
||||
|
||||
@ -131,12 +131,15 @@ var factory = function (Util, Rpc) {
|
||||
});
|
||||
};
|
||||
|
||||
exp.removeOwnedChannel = function (channel, 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, function (e, response) {
|
||||
rpc.send('REMOVE_OWNED_CHANNEL', {
|
||||
channel: channel,
|
||||
reason: reason
|
||||
}, function (e, response) {
|
||||
if (e) { return void cb(e); }
|
||||
if (response && response.length && response[0] === "OK") {
|
||||
cb();
|
||||
|
||||
@ -71,7 +71,7 @@ define([
|
||||
};
|
||||
|
||||
// Password may have changed
|
||||
var deprecateProxy = function (Env, id, channel) {
|
||||
var deprecateProxy = function (Env, id, channel, reason) {
|
||||
if (Env.folders[id] && Env.folders[id].deleting) {
|
||||
// Folder is being deleted by its owner, don't deprecate it
|
||||
return;
|
||||
@ -85,11 +85,19 @@ define([
|
||||
return void Env.Store.refreshDriveUI();
|
||||
}
|
||||
if (channel) { Env.unpinPads([channel], function () {}); }
|
||||
Env.user.userObject.deprecateSharedFolder(id);
|
||||
removeProxy(Env, id);
|
||||
if (Env.Store && Env.Store.refreshDriveUI) {
|
||||
Env.Store.refreshDriveUI();
|
||||
|
||||
// If it's explicitely a deletion, no need to deprecate, just delete
|
||||
if (reason && reason !== "PASSWORD_CHANGE") {
|
||||
var temp = Util.find(Env, ['user', 'proxy', UserObject.SHARED_FOLDERS]);
|
||||
delete temp[id];
|
||||
if (Env.Store && Env.Store.refreshDriveUI) { Env.Store.refreshDriveUI(); }
|
||||
return;
|
||||
}
|
||||
|
||||
// It's explicitely a password change, better message in drive: provide the "reason" to the UI
|
||||
Env.user.userObject.deprecateSharedFolder(id, reason);
|
||||
removeProxy(Env, id);
|
||||
if (Env.Store && Env.Store.refreshDriveUI) { Env.Store.refreshDriveUI(); }
|
||||
};
|
||||
|
||||
var restrictedProxy = function (Env, id) {
|
||||
@ -632,17 +640,19 @@ define([
|
||||
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);
|
||||
data.password = newPassword;
|
||||
data.channel = secret.channel;
|
||||
newData.password = newPassword;
|
||||
newData.channel = secret.channel;
|
||||
if (secret.keys.editKeyStr) {
|
||||
data.href = '/drive/#'+Hash.getEditHashFromKeys(secret);
|
||||
newData.href = '/drive/#'+Hash.getEditHashFromKeys(secret);
|
||||
}
|
||||
data.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
|
||||
newData.roHref = '/drive/#'+Hash.getViewHashFromKeys(secret);
|
||||
delete newData.legacy;
|
||||
_addSharedFolder(Env, {
|
||||
path: ['root'],
|
||||
folderData: data,
|
||||
folderData: newData,
|
||||
}, function () {
|
||||
delete temp[fId];
|
||||
Env.onSync(cb);
|
||||
|
||||
@ -469,12 +469,20 @@ define([
|
||||
// `hasChannelHistory` doesn't work for files (not a channel)
|
||||
// `getFileSize` is not adapted to channels because of metadata
|
||||
Cryptpad.getFileSize(currentPad.href, password, function (e, size) {
|
||||
if (e && e !== "PASSWORD_CHANGE") {
|
||||
return sframeChan.event("EV_DELETED_ERROR", e);
|
||||
}
|
||||
next(e, size === 0);
|
||||
});
|
||||
return;
|
||||
}
|
||||
// Not a file, so we can use `hasChannelHistory`
|
||||
Cryptpad.hasChannelHistory(currentPad.href, password, next);
|
||||
Cryptpad.hasChannelHistory(currentPad.href, password, (e, isNew, reason) => {
|
||||
if (isNew && reason && reason !== "PASSWORD_CHANGE") {
|
||||
return sframeChan.event("EV_DELETED_ERROR", reason);
|
||||
}
|
||||
next();
|
||||
});
|
||||
});
|
||||
sframeChan.event("EV_PAD_PASSWORD", cfg);
|
||||
};
|
||||
@ -564,20 +572,32 @@ define([
|
||||
// `hasChannelHistory` doesn't work for files (not a channel)
|
||||
// `getFileSize` is not adapted to channels because of metadata
|
||||
Cryptpad.getFileSize(currentPad.href, password, w(function (e, size) {
|
||||
if (size !== 0) { return void todo(); }
|
||||
if (e && e !== "PASSWORD_CHANGE") {
|
||||
sframeChan.event("EV_DELETED_ERROR", e);
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
if (!e && size !== 0) { return void todo(); }
|
||||
// Wrong password or deleted file?
|
||||
passwordCfg.legacy = !e; // Legacy means we don't know if it's a deletion or pw change
|
||||
askPassword(true, passwordCfg);
|
||||
}));
|
||||
return;
|
||||
}
|
||||
// Not a file, so we can use `hasChannelHistory`
|
||||
Cryptpad.hasChannelHistory(currentPad.href, password, w(function(e, isNew) {
|
||||
Cryptpad.hasChannelHistory(currentPad.href, password, w(function(e, isNew, reason) {
|
||||
if (isNew && expire && expire < (+new Date())) {
|
||||
sframeChan.event("EV_EXPIRED_ERROR");
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
if (!e && !isNew) { return void todo(); }
|
||||
// NOTE: Legacy mode ==> no reason may indicate a password change
|
||||
if (isNew && reason && reason !== "PASSWORD_CHANGE") {
|
||||
sframeChan.event("EV_DELETED_ERROR", reason);
|
||||
waitFor.abort();
|
||||
return;
|
||||
}
|
||||
if (parsed.hashData.mode === 'view' && (password || !parsed.hashData.password)) {
|
||||
// Error, wrong password stored, the view seed has changed with the password
|
||||
// password will never work
|
||||
@ -586,6 +606,7 @@ define([
|
||||
return;
|
||||
}
|
||||
// Wrong password or deleted file?
|
||||
passwordCfg.legacy = !reason; // Legacy means we don't know if it's a deletion or pw change
|
||||
askPassword(true, passwordCfg);
|
||||
}));
|
||||
}).nThen(done);
|
||||
@ -1915,6 +1936,8 @@ define([
|
||||
// redirect
|
||||
window.location.href = url;
|
||||
document.location.reload();
|
||||
// XXX XXX XXX TEST TEST TEST
|
||||
// XXX XXX XXX TEST TEST TEST
|
||||
});
|
||||
|
||||
return;
|
||||
|
||||
@ -844,6 +844,13 @@ define([
|
||||
UI.errorLoadingScreen(Messages.restrictedError);
|
||||
});
|
||||
|
||||
ctx.sframeChan.on("EV_DELETED_ERROR", function (reason) {
|
||||
funcs.onServerError({
|
||||
type: 'EDELETED',
|
||||
message: reason
|
||||
});
|
||||
});
|
||||
|
||||
ctx.sframeChan.on("EV_PAD_PASSWORD_ERROR", function () {
|
||||
UI.errorLoadingScreen(Messages.password_error_seed);
|
||||
});
|
||||
|
||||
@ -20,6 +20,7 @@ define([
|
||||
};
|
||||
SFCommonO.start({
|
||||
cache: true,
|
||||
noDrive: true,
|
||||
hash: hash,
|
||||
href: href,
|
||||
noRealtime: true,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user