aboutsummaryrefslogtreecommitdiff
path: root/lib/serve.js
diff options
context:
space:
mode:
authorcel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519>2017-09-29 08:41:55 -1000
committercel <cel@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519>2017-09-29 08:41:55 -1000
commit440274d19f302e0f293d56cba685e39e372cda12 (patch)
tree5bcd754ab04f5080aaf353dfc2b60b5fac7edb31 /lib/serve.js
parent7fffac329b2718f0ea918cd9fd53d3e2b47cc93e (diff)
parent62e7e74bd278473cc4358700b7f2b5c0a78ac681 (diff)
downloadpatchfoo-440274d19f302e0f293d56cba685e39e372cda12.tar.gz
patchfoo-440274d19f302e0f293d56cba685e39e372cda12.zip
Merge branch 'secretblobs' into master
Diffstat (limited to 'lib/serve.js')
-rw-r--r--lib/serve.js97
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',