diff --git a/lib/storage/file.js b/lib/storage/file.js index 380943942..298c55832 100644 --- a/lib/storage/file.js +++ b/lib/storage/file.js @@ -920,6 +920,13 @@ var trimChannel = function (env, channelName, hash, _cb, deleteOneLine) { } // if there were no errors just fall through to the next block })); + }).nThen(function (w) { + // If we want to delete a signle line of the file, make sure this pad allows it + if (!deleteOneLine) { return; } + if (!metadataReference.meta || !metadataReference.meta.deleteLines) { + w.abort(); + return void cb("EFORBIDDEN"); + } }).nThen(function (w) { // create temp buffer writeStream tempStream = Fs.createWriteStream(tempChannelPath, { diff --git a/www/common/cryptpad-common.js b/www/common/cryptpad-common.js index a0471107c..c859feeae 100644 --- a/www/common/cryptpad-common.js +++ b/www/common/cryptpad-common.js @@ -137,13 +137,9 @@ define([ key: ['forms', data.channel], }, function (obj) { if (!obj || obj.error) { return void cb(obj); } - if (!Array.isArray(obj)) { - obj.deletable = false; - obj = [obj]; - } + if (!Array.isArray(obj)) { obj = [obj]; } var last = obj[obj.length - 1]; - if (obj.length && obj[0].deletable === false) { last.deletable === false; } if (onlyLast) { return void cb(last); } return void cb(obj); }); @@ -176,16 +172,14 @@ define([ console.error(obj.error); } }); - cb({ - deletable: answers[0].deletable !== false - }); + cb(); }); }; - common.deleteFormAnswers = function (data, cb) { + common.deleteFormAnswers = function (data, _cb) { + var cb = Util.once(_cb); common.getFormAnswer(data, false, function (obj) { if (!obj || obj.error) { return void cb(); } if (!obj.length) { return void cb(); } - if (obj[0].deletable === false) { return void cb({error: 'EINVAL'}); } var n = Nthen; var nacl, theirs; n = n(function (waitFor) { @@ -213,6 +207,10 @@ define([ proof: proof }; postMessage("DELETE_PAD_LINE", lineData, waitFor(function (obj) { + if (obj && obj.error === 'EFORBIDDEN') { + waitFor.abort(); + return void cb(obj); + } if (obj || obj.error) { return; } toDelete.push(hash); })); diff --git a/www/form/inner.js b/www/form/inner.js index 0e1d6eb41..f66eb3aba 100644 --- a/www/form/inner.js +++ b/www/form/inner.js @@ -2927,6 +2927,10 @@ define([ channel: content.answers.channel, hash: answers._hash }, function (err, obj) { + if (obj && obj.error) { + console.error(obj.error); + return void UI.warn(Messages.error); + } framework._.sfCommon.gotoURL(); }); }); diff --git a/www/form/main.js b/www/form/main.js index 8d762650d..cb83191ee 100644 --- a/www/form/main.js +++ b/www/form/main.js @@ -122,6 +122,8 @@ define([ return false; } }; + + var deleteLines = false; // "false" to support old forms sframeChan.on('Q_FORM_FETCH_ANSWERS', function (data, _cb) { var cb = Utils.Util.once(_cb); var myKeys = {}; @@ -183,6 +185,9 @@ define([ validateKey: keys.secondaryValidateKey, owners: [myKeys.edPublic], crypto: crypto, + metadata: { + deleteLines: true + } //Cache: Utils.Cache // TODO enable cache for form responses when the cache stops evicting old answers }; var results = {}; @@ -206,7 +211,10 @@ define([ cb(); }); }; - config.onReady = function () { + config.onReady = function (obj) { + if (obj && obj.metadata && obj.metadata.deleteLines) { + deleteLines = true; + } var myKey; // If we have submitted an anonymous answer, retrieve it if (myFormKeys.curvePublic && results[myFormKeys.curvePublic]) { @@ -261,6 +269,9 @@ define([ } answer = obj; })); + Cryptpad.getPadMetadata({channel: data.channel}, w(function (md) { + if (md && md.deleteLines) { deleteLines = true; } + })); }).nThen(function () { if (answer.anonymous) { if (!myKeys.formSeed) { return void cb({ error: "ANONYMOUS_ERROR" }); } @@ -285,7 +296,7 @@ define([ var parsed = JSON.parse(res.content); parsed._isAnon = answer.anonymous; parsed._time = messages[0].time; - if (answer.deletable !== false) { parsed._hash = answer.hash; } + if (deleteLines) { parsed._hash = answer.hash; } cb(parsed); } catch (e) { cb({error: e}); @@ -348,11 +359,11 @@ define([ hash: hash, curvePrivate: ephemeral_private, anonymous: Boolean(data.anonymous) - }, function (obj) { + }, function () { var res = data.results; res._isAnon = data.anonymous; res._time = +new Date(); - if (obj.deletable !== false) { res._hash = hash; } + if (deleteLines) { res._hash = hash; } cb({ error: err, response: response, @@ -363,6 +374,9 @@ define([ }); }); sframeChan.on("Q_FORM_DELETE_ANSWER", function (data, cb) { + if (!deleteLines) { + return void cb({error: 'EFORBIDDEN'}); + } Cryptpad.deleteFormAnswers(data, cb); }); };