aboutsummaryrefslogtreecommitdiff
path: root/lib/serve.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/serve.js')
-rw-r--r--lib/serve.js71
1 files changed, 46 insertions, 25 deletions
diff --git a/lib/serve.js b/lib/serve.js
index 8d3eeba..1ce18c6 100644
--- a/lib/serve.js
+++ b/lib/serve.js
@@ -24,7 +24,7 @@ module.exports = Serve
var emojiDir = path.join(require.resolve('emoji-named-characters'), '../pngs')
-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') {
@@ -75,19 +75,18 @@ Serve.prototype.go = function () {
gotData(err, data)
})
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
+ data[fieldname] = link
+ cb()
+ })
)
- done(function (err, size, id) {
- if (err) return cb(err)
- if (size === 0 && !filename) return cb()
- data[fieldname] = {link: id, name: filename, type: mimetype, size: size}
- cb()
- })
})
busboy.on('field', function (fieldname, val, fieldnameTruncated, valTruncated, encoding, mimetype) {
if (!(fieldname in data)) data[fieldname] = val
@@ -197,7 +196,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])
}
}
@@ -809,7 +808,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)
@@ -845,16 +844,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 = {
@@ -874,9 +871,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)
)
})
}
@@ -905,10 +900,27 @@ Serve.prototype.emoji = function (emoji) {
serveEmoji(this.req, this.res, emoji)
}
-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')
+ }
+ }
var done = multicb({pluck: 1, spread: true})
blobs.want(id, function (err, has) {
if (err) {
@@ -918,7 +930,7 @@ Serve.prototype.blob = function (id) {
if (!has) return self.respond(404, 'Not found')
blobs.size(id, done())
pull(
- blobs.get(id),
+ self.app.getBlob(id, key),
pull.map(Buffer),
ident(done().bind(self, null)),
self.respondSink()
@@ -927,11 +939,14 @@ Serve.prototype.blob = function (id) {
if (err) console.trace(err)
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)
})
})
@@ -1290,6 +1305,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, [
@@ -1428,6 +1444,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]) {
@@ -1443,11 +1460,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
@@ -1509,6 +1528,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',