diff --git a/lib/pins.js b/lib/pins.js index 4e0791d05..23b1364a3 100644 --- a/lib/pins.js +++ b/lib/pins.js @@ -2,20 +2,44 @@ var Pins = module.exports; -/* - takes contents of a pinFile (UTF8 string) - and the pin file's name - returns an array of of channel ids which are pinned - - throw errors on pin logs with invalid pin data +/* Accepts a reference to an object, and... + either a string describing which log is being processed (backwards compatibility), + or a function which will log the error with all relevant data */ -Pins.calculateFromLog = function (pinFile, fileName) { - var pins = {}; - pinFile.split('\n').filter((x)=>(x)).map((l) => JSON.parse(l)).forEach((l) => { +var createLineHandler = Pins.createLineHandler = function (ref, errorHandler) { + var fileName; + if (typeof(errorHandler) === 'string') { + fileName = errorHandler; + errorHandler = function (label, data) { + console.error(label, { + log: fileName, + data: data, + }); + }; + } + + // passing the reference to an object allows us to overwrite accumulated pins + // make sure to get ref.pins as the result + // it's a weird API but it's faster than unpinning manually + var pins = ref.pins = {}; + return function (line) { + if (!Boolean(line)) { return; } + + var l; + try { + l = JSON.parse(line); + } catch (e) { + return void errorHandler('PIN_LINE_PARSE_ERROR', line); + } + + if (!Array.isArray(l)) { + return void errorHandler('PIN_LINE_NOT_FORMAT_ERROR', l); + } + switch (l[0]) { case 'RESET': { - pins = {}; - if (l[1] && l[1].length) { l[1].forEach((x) => { pins[x] = 1; }); } + pins = ref.pins = {}; + if (l[1] && l[1].length) { l[1].forEach((x) => { ref.pins[x] = 1; }); } //jshint -W086 // fallthrough } @@ -28,16 +52,25 @@ Pins.calculateFromLog = function (pinFile, fileName) { break; } default: - // FIXME logging - // TODO write to the error log - /* Log.error('CORRUPTED_PIN_LOG', { - line: JSON.stringify(l), - fileName: fileName, - }); */ - console.error(new Error (JSON.stringify(l) + ' ' + fileName)); + errorHandler("PIN_LINE_UNSUPPORTED_COMMAND", l); } - }); - return Object.keys(pins); + }; +}; + +/* + takes contents of a pinFile (UTF8 string) + and the pin file's name + returns an array of of channel ids which are pinned + + throw errors on pin logs with invalid pin data +*/ +Pins.calculateFromLog = function (pinFile, fileName) { + var ref = {}; + var handler = createLineHandler(ref, fileName); + + pinFile.split('\n').forEach(handler); + return Object.keys(ref.pins); }; // TODO refactor to include a streaming version for use in rpc.js as well + diff --git a/rpc.js b/rpc.js index 9943b0334..33dcedc53 100644 --- a/rpc.js +++ b/rpc.js @@ -16,7 +16,7 @@ const Pinned = require('./scripts/pinned'); const Saferphore = require("saferphore"); const nThen = require("nthen"); const getFolderSize = require("get-folder-size"); - +const Pins = require("./lib/pins"); var RPC = module.exports; @@ -237,52 +237,21 @@ var loadUserPins = function (Env, publicKey, cb) { return cb(session.channels); } - // if channels aren't in memory. load them from disk - var pins = {}; - - var pin = function (channel) { - pins[channel] = true; - }; - - var unpin = function (channel) { - // TODO delete? - pins[channel] = false; - }; + var ref = {}; + var lineHandler = Pins.createLineHandler(ref, function (label, data) { + Log.error(label, { + log: publicKey, + data: data, + }); + }); - Env.pinStore.getMessages(publicKey, function (msg) { - // handle messages... - var parsed; - try { - parsed = JSON.parse(msg); - session.hasPinned = true; - - switch (parsed[0]) { - case 'PIN': - parsed[1].forEach(pin); - break; - case 'UNPIN': - parsed[1].forEach(unpin); - break; - case 'RESET': - // TODO just wipe out the object? - Object.keys(pins).forEach(unpin); - - if (parsed[1] && parsed[1].length) { - parsed[1].forEach(pin); - } - break; - default: - Log.warn('INVALID_STORED_MESSAGE', msg); - } - } catch (e) { - Log.warn('STORED_PARSE_ERROR', e); - } - }, function () { + // if channels aren't in memory. load them from disk + Env.pinStore.getMessages(publicKey, lineHandler, function () { // no more messages // only put this into the cache if it completes - session.channels = pins; - cb(pins); + session.channels = ref.pins; + cb(ref.pins); }); }; @@ -851,7 +820,7 @@ var clearOwnedChannel = function (Env, channelId, unsafeKey, cb) { }); }; -var removeOwnedBlob = function (Env, blobId, unsafeKey, cb) { // FIXME deletion +var removeOwnedBlob = function (Env, blobId, unsafeKey, cb) { var safeKey = escapeKeyCharacters(unsafeKey); var safeKeyPrefix = safeKey.slice(0,3); var blobPrefix = blobId.slice(0,2); @@ -889,6 +858,11 @@ var removeOwnedBlob = function (Env, blobId, unsafeKey, cb) { // FIXME deletion // Delete the blob /*:: if (typeof(blobPath) !== 'string') { throw new Error('should never happen'); } */ Fs.unlink(blobPath, w(function (e) { // TODO move to cold storage + Log.info('DELETION_OWNED_FILE_BY_OWNER_RPC', { + safeKey: safeKey, + blobPath: blobPath, + status: e? String(e): 'SUCCESS', + }); if (e) { w.abort(); return void cb(e.code); @@ -897,12 +871,17 @@ var removeOwnedBlob = function (Env, blobId, unsafeKey, cb) { // FIXME deletion }).nThen(function () { // Delete the proof of ownership Fs.unlink(ownPath, function (e) { + Log.info('DELETION_OWNED_FILE_PROOF_BY_OWNER_RPC', { + safeKey: safeKey, + proofPath: ownPath, + status: e? String(e): 'SUCCESS', + }); cb(e && e.code); }); }); }; -var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { // FIXME deletion +var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { if (typeof(channelId) !== 'string' || !isValidId(channelId)) { return cb('INVALID_ARGUMENTS'); } @@ -922,6 +901,11 @@ var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { // FIXME del return void cb('INSUFFICIENT_PERMISSIONS'); } return void Env.msgStore.removeChannel(channelId, function (e) { + Log.info('DELETION_CHANNEL_BY_OWNER_RPC', { + unsafeKey: unsafeKey, + channelId: channelId, + status: e? String(e): 'SUCCESS', + }); cb(e); }); }); @@ -929,11 +913,16 @@ var removeOwnedChannel = function (Env, channelId, unsafeKey, cb) { // FIXME del /* Users should be able to clear their own pin log with an authenticated RPC */ -var removePins = function (Env, safeKey, cb) { // FIXME deletion +var removePins = function (Env, safeKey, cb) { if (typeof(Env.pinStore.removeChannel) !== 'function') { return void cb("E_NOT_IMPLEMENTED"); } Env.pinStore.removeChannel(safeKey, function (err) { + Log.info('DELETION_PIN_BY_OWNER_RPC', { + safeKey: safeKey, + status: err? String(err): 'SUCCESS', + }); + cb(err); }); }; @@ -1431,7 +1420,6 @@ var removeLoginBlock = function (Env, msg, cb) { var signature = msg[1]; var block = Nacl.util.decodeUTF8('DELETE_BLOCK'); // clients and the server will have to agree on this constant - // FIXME deletion validateLoginBlock(Env, publicKey, signature, block, function (e /*::, validatedBlock */) { if (e) { return void cb(e); } // derive the filepath @@ -1443,6 +1431,12 @@ var removeLoginBlock = function (Env, msg, cb) { } Fs.unlink(path, function (err) { + Log.info('DELETION_BLOCK_BY_OWNER_RPC', { + publicKey: publicKey, + path: path, + status: err? String(err): 'SUCCESS', + }); + if (err) { return void cb(err); } cb(); }); diff --git a/scripts/pinneddata.js b/scripts/pinneddata.js index e87aaf971..a65b081d1 100644 --- a/scripts/pinneddata.js +++ b/scripts/pinneddata.js @@ -202,6 +202,12 @@ if (!module.parent) { // if no parent, it is being invoked directly let config = {}; // build the config from command line arguments... + var Config = require("../lib/load-config"); + + config.filePath = Config.filePath; + config.blobPath = Config.blobPath; + config.pinPath = Config.pinPath; + // --unpinned gets the list of unpinned files // if you don't pass this, it will list the size of pinned data per user if (process.argv.indexOf('--unpinned') > -1) { config.unpinned = true; }