diff options
Diffstat (limited to 'lib/serve.js')
-rw-r--r-- | lib/serve.js | 97 |
1 files changed, 66 insertions, 31 deletions
diff --git a/lib/serve.js b/lib/serve.js index 30689eb..839725a 100644 --- a/lib/serve.js +++ b/lib/serve.js @@ -27,7 +27,7 @@ module.exports = Serve var emojiDir = path.join(require.resolve('emoji-named-characters'), '../pngs') var hlCssDir = path.join(require.resolve('highlight.js'), '../../styles') -var urlIdRegex = /^(?:\/+(([%&@]|%25)(?:[A-Za-z0-9\/+]|%2[Ff]|%2[Bb]){43}(?:=|%3D)\.(?:sha256|ed25519))(?:\.([^?]*))?|(\/.*?))(?:\?(.*))?$/ +var urlIdRegex = /^(?:\/+(([%&@]|%25)(?:[A-Za-z0-9\/+]|%2[Ff]|%2[Bb]){43}(?:=|%3D)\.(?:sha256|ed25519))([^?]*)?|(\/.*?))(?:\?(.*))?$/ function ctype(name) { switch (name && /[^.\/]*$/.exec(name)[0] || 'html') { @@ -83,20 +83,18 @@ Serve.prototype.go = function () { else data[name] = [data[name], value] } busboy.on('file', function (fieldname, file, filename, encoding, mimetype) { - var done = multicb({pluck: 1, spread: true}) var cb = filesCb() pull( toPull(file), - u.pullLength(done()), - self.app.addBlob(done()) + self.app.addBlob(!!data.private, function (err, link) { + if (err) return cb(err) + if (link.size === 0 && !filename) return cb() + link.name = filename + link.type = mimetype + addField(fieldname, link) + cb() + }) ) - done(function (err, size, id) { - if (err) return cb(err) - if (size === 0 && !filename) return cb() - addField(fieldname, - {link: id, name: filename, type: mimetype, size: size}) - cb() - }) }) busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) { addField(fieldname, val) @@ -236,7 +234,7 @@ Serve.prototype.handle = function () { case '%25': m[2] = '%'; m[1] = decodeURIComponent(m[1]) case '%': return this.id(m[1], m[3]) case '@': return this.userFeed(m[1], m[3]) - case '&': return this.blob(m[1]) + case '&': return this.blob(m[1], m[3]) default: return this.path(m[4]) } } @@ -970,7 +968,7 @@ function threadHeads(msgs, rootId) { } -Serve.prototype.id = function (id, ext) { +Serve.prototype.id = function (id, path) { var self = this if (self.query.raw != null) return self.rawId(id) @@ -1006,16 +1004,14 @@ Serve.prototype.id = function (id, ext) { channel: channel, }), self.wrapPage(id), - self.respondSink(200, { - 'Content-Type': ctype(ext), - }) + self.respondSink(200) ) }) ) }) } -Serve.prototype.userFeed = function (id, ext) { +Serve.prototype.userFeed = function (id, path) { var self = this var q = self.query var opts = { @@ -1035,9 +1031,7 @@ Serve.prototype.userFeed = function (id, ext) { self.wrapMessages(), self.wrapUserFeed(isScrolled, id), self.wrapPage(about.name || id), - self.respondSink(200, { - 'Content-Type': ctype(ext) - }) + self.respondSink(200) ) }) } @@ -1070,17 +1064,33 @@ Serve.prototype.highlight = function (dirs) { this.file(path.join(hlCssDir, dirs)) } -Serve.prototype.blob = function (id) { +Serve.prototype.blob = function (id, path) { var self = this - var blobs = self.app.sbot.blobs - if (self.req.headers['if-none-match'] === id) return self.respond(304) + var etag = id + (path || '') + if (self.req.headers['if-none-match'] === etag) return self.respond(304) + var key + if (path) { + path = decodeURIComponent(path) + if (path[0] === '#') { + try { + key = new Buffer(path.substr(1), 'base64') + } catch(err) { + return self.respond(400, err.message) + } + if (key.length !== 32) { + return self.respond(400, 'Bad blob key') + } + } else { + return self.respond(400, 'Bad blob request') + } + } self.app.wantSizeBlob(id, function (err, size) { if (err) { if (/^invalid/.test(err.message)) return self.respond(400, err.message) else return self.respond(500, err.message || err) } pull( - blobs.get(id), + self.app.getBlob(id, key), pull.map(Buffer), ident(gotType), self.respondSink() @@ -1088,22 +1098,41 @@ Serve.prototype.blob = function (id) { function gotType(type) { type = type && mime.lookup(type) if (type) self.res.setHeader('Content-Type', type) - if (typeof size === 'number') self.res.setHeader('Content-Length', size) + // don't serve size for encrypted blob, because it refers to the size of + // the ciphertext + if (typeof size === 'number' && !key) + self.res.setHeader('Content-Length', size) if (self.query.name) self.res.setHeader('Content-Disposition', 'inline; filename='+encodeDispositionFilename(self.query.name)) self.res.setHeader('Cache-Control', 'public, max-age=315360000') - self.res.setHeader('etag', id) + self.res.setHeader('etag', etag) self.res.writeHead(200) } }) } Serve.prototype.image = function (path) { - var id = path.substr(1) - var etag = 'image-' + id var self = this - var blobs = self.app.sbot.blobs + var id, key + var m = urlIdRegex.exec(path) + if (m && m[2] === '&') id = m[1], path = m[3] + var etag = 'image-' + id + (path || '') if (self.req.headers['if-none-match'] === etag) return self.respond(304) + if (path) { + path = decodeURIComponent(path) + if (path[0] === '#') { + try { + key = new Buffer(path.substr(1), 'base64') + } catch(err) { + return self.respond(400, err.message) + } + if (key.length !== 32) { + return self.respond(400, 'Bad blob key') + } + } else { + return self.respond(400, 'Bad blob request') + } + } self.app.wantSizeBlob(id, function (err, size) { if (err) { if (/^invalid/.test(err.message)) return self.respond(400, err.message) @@ -1115,7 +1144,7 @@ Serve.prototype.image = function (path) { var heresTheType = done().bind(self, null) pull( - blobs.get(id), + self.app.getBlob(id, key), pull.map(Buffer), ident(heresTheType), pull.collect(onFullBuffer) @@ -2076,6 +2105,7 @@ Serve.prototype.wrapThread = function (opts) { branches: opts.branches, postBranches: opts.postBranches, recps: recps, + private: opts.recps != null, }, function (err, composer) { if (err) return cb(err) cb(null, [ @@ -2215,6 +2245,7 @@ Serve.prototype.composer = function (opts, cb) { blobs[data.upload.link] = { type: data.upload.type, size: data.upload.size, + key: data.upload.key, } } if (data.blob_type && blobs[data.blob_link]) { @@ -2243,11 +2274,13 @@ Serve.prototype.composer = function (opts, cb) { } if (data.upload) { + var href = data.upload.link + + (data.upload.key ? '#' + data.upload.key : '') // TODO: be able to change the content-type var isImage = /^image\//.test(data.upload.type) data.text = (data.text ? data.text + '\n' : '') + (isImage ? '!' : '') - + '[' + data.upload.name + '](' + data.upload.link + ')' + + '[' + data.upload.name + '](' + href + ')' } // get bare feed names @@ -2342,6 +2375,8 @@ Serve.prototype.composer = function (opts, cb) { h('table.ssb-msgs', h('tr.msg-row', h('td.msg-left', {colspan: 2}, + opts.private ? + h('input', {type: 'hidden', name: 'private', value: '1'}) : '', h('input', {type: 'file', name: 'upload'}) ), h('td.msg-right', |