/* ssb-exec * © 2019 cel @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 * * Usage of the works is permitted provided that this instrument is * retained with the works, so that any entity that uses the works is * notified of this instrument. * * DISCLAIMER: THE WORKS ARE WITHOUT WARRANTY. */ var url = require('url') var path = require('path') var fs = require('fs') var blobsDir = path.resolve(__dirname, '../../blobs') exports.requireBlob = function (id) { var m = /^\&([0-9a-zA-Z\/+=]{44})\.(sha256)$/.exec(id) if (!m) throw new Error('invalid blob id: ' + id) var hex = new Buffer(m[1], 'base64').toString('hex') return require(path.join(blobsDir, m[2], hex.substr(0, 2), hex.substr(2))) } exports.name = 'exec' exports.version = '1.0.0' exports.manifest = { } exports.init = function (sbot, config) { if (!sbot.ws || !sbot.ws.use) return console.trace('missing ssb-ws') var prefix = '/exec' var dir = path.join(config.path, 'exec') blobsDir = path.join(config.path, 'blobs') var mtimes = {} console.log('[exec] init. dir:', dir, 'prefix:', prefix) function getModule(resolved, tried, cb) { try { var module = require(resolved) return cb(null, module) } catch(e) { if (tried || !e || !(e.code === 'ENOENT' || e.code === 'MODULE_NOT_FOUND')) return cb(e) var m = /(sha256)\/([0-9a-f]{2})\/([0-9a-f]{62})/.exec(e.path || e.message) if (!m) return cb(e) var blobId = '&' + new Buffer(m[2] + m[3], 'hex').toString('base64') + '.' + m[1] if (!sbot.blobs) return cb(new Error('Missing ssb-blobs plugin')) if (typeof sbot.blobs.want !== 'function') return cb(new Error('missing blobs.want method')) return sbot.blobs.want(blobId, function (err, has) { if (err) return cb(new Error('Unable to get blob: ' + (err.stack || err))) if (!has) return cb(new Error('Unable to get blob')) getModule(resolved, true, cb) }) } } sbot.ws.use(function (req, res, next) { if (prefix !== req.url.substr(0, prefix.length)) return next() if (prefix === req.url) { res.writeHead(302, {Location: path.basename(prefix) + '/'}) return res.end() } try { req.uri = url.parse(req.url.substr(prefix.length), true) var file = path.normalize(path.join(dir, req.uri.pathname)) if (dir !== file.substr(0, dir.length)) { res.writeHead(403) return res.end('Forbidden') } var resolved = require.resolve(file) var prevMtime = mtimes[resolved] var mtime = fs.statSync(resolved).mtime.getTime() if (mtime !== prevMtime) { delete require.cache[resolved] mtimes[resolved] = mtime } getModule(resolved, false, gotModule) } catch(e) { return gotError(e) } function gotError(err) { try { res.writeHead(err.code === 'ENOENT' || err.code === 'MODULE_NOT_FOUND' ? 404 : 500) } finally { res.end(err.stack || err) } } function gotModule(err, module) { if (err) return gotError(err) try { module(req, res, sbot, config) } catch(e) { gotError(e) } } }) }