From ebd9e50e143f6ae5a6f9bdfa70da01f4b424bb9b Mon Sep 17 00:00:00 2001 From: Andris Reinman Date: Thu, 30 Aug 2018 13:18:56 +0300 Subject: [PATCH] messages --- lib/api/messages.js | 240 ++++++++++++++++++++++++-------------------- 1 file changed, 130 insertions(+), 110 deletions(-) diff --git a/lib/api/messages.js b/lib/api/messages.js index 58518f3f..689acf0f 100644 --- a/lib/api/messages.js +++ b/lib/api/messages.js @@ -24,6 +24,7 @@ module.exports = (db, server, messageHandler) => { }); const updateMessage = util.promisify(messageHandler.update.bind(messageHandler)); + const getAttachmentData = util.promisify(messageHandler.attachmentStorage.get.bind(messageHandler.attachmentStorage)); /** * @api {get} /users/:user/mailboxes/:mailbox/messages List messages in a Mailbox @@ -1493,121 +1494,140 @@ module.exports = (db, server, messageHandler) => { * "error": "This attachment does not exist" * } */ - server.get({ name: 'attachment', path: '/users/:user/mailboxes/:mailbox/messages/:message/attachments/:attachment' }, (req, res, next) => { - const schema = Joi.object().keys({ - user: Joi.string() - .hex() - .lowercase() - .length(24) - .required(), - mailbox: Joi.string() - .hex() - .lowercase() - .length(24) - .required(), - message: Joi.number() - .min(1) - .required(), - attachment: Joi.string() - .regex(/^ATT\d+$/i) - .uppercase() - .required() - }); - - const result = Joi.validate(req.params, schema, { - abortEarly: false, - convert: true - }); - - if (result.error) { - res.json({ - error: result.error.message, - code: 'InputValidationError' + server.get( + { name: 'attachment', path: '/users/:user/mailboxes/:mailbox/messages/:message/attachments/:attachment' }, + tools.asyncifyJson(async (req, res, next) => { + const schema = Joi.object().keys({ + user: Joi.string() + .hex() + .lowercase() + .length(24) + .required(), + mailbox: Joi.string() + .hex() + .lowercase() + .length(24) + .required(), + message: Joi.number() + .min(1) + .required(), + attachment: Joi.string() + .regex(/^ATT\d+$/i) + .uppercase() + .required() }); - return next(); - } - let user = new ObjectID(result.value.user); - let mailbox = new ObjectID(result.value.mailbox); - let message = result.value.message; - let attachment = result.value.attachment; + const result = Joi.validate(req.params, schema, { + abortEarly: false, + convert: true + }); - db.database.collection('messages').findOne( - { - mailbox, - uid: message, - user - }, - { - projection: { - _id: true, - user: true, - attachments: true, - 'mimeTree.attachmentMap': true - } - }, - (err, messageData) => { - if (err) { - res.json({ - error: 'MongoDB Error: ' + err.message, - code: 'InternalDatabaseError' - }); - return next(); - } - if (!messageData || messageData.user.toString() !== user.toString()) { - res.json({ - error: 'This message does not exist', - code: 'NoSuchMessage' - }); - return next(); - } - - let attachmentId = messageData.mimeTree.attachmentMap && messageData.mimeTree.attachmentMap[attachment]; - if (!attachmentId) { - res.json({ - error: 'This attachment does not exist' - }); - return next(); - } - - messageHandler.attachmentStorage.get(attachmentId, (err, attachmentData) => { - if (err) { - res.json({ - error: err.message - }); - return next(); - } - - res.writeHead(200, { - 'Content-Type': attachmentData.contentType || 'application/octet-stream' - }); - - let decode = true; - - if (attachmentData.metadata.decoded) { - attachmentData.metadata.decoded = false; - decode = false; - } - - let attachmentStream = messageHandler.attachmentStorage.createReadStream(attachmentId, attachmentData); - - attachmentStream.once('error', err => res.emit('error', err)); - - if (!decode) { - return attachmentStream.pipe(res); - } - - if (attachmentData.transferEncoding === 'base64') { - attachmentStream.pipe(new libbase64.Decoder()).pipe(res); - } else if (attachmentData.transferEncoding === 'quoted-printable') { - attachmentStream.pipe(new libqp.Decoder()).pipe(res); - } else { - attachmentStream.pipe(res); - } + if (result.error) { + res.json({ + error: result.error.message, + code: 'InputValidationError' }); + return next(); } - ); - }); + + // permissions check + if (req.user && req.user === result.value.user) { + req.validate(roles.can(req.role).readOwn('messages')); + } else { + req.validate(roles.can(req.role).readAny('messages')); + } + + let user = new ObjectID(result.value.user); + let mailbox = new ObjectID(result.value.mailbox); + let message = result.value.message; + let attachment = result.value.attachment; + + let messageData; + try { + messageData = await db.database.collection('messages').findOne( + { + mailbox, + uid: message, + user + }, + { + projection: { + _id: true, + user: true, + attachments: true, + 'mimeTree.attachmentMap': true + } + } + ); + } catch (err) { + res.json({ + error: 'MongoDB Error: ' + err.message, + code: 'InternalDatabaseError' + }); + return next(); + } + if (!messageData || messageData.user.toString() !== user.toString()) { + res.json({ + error: 'This message does not exist', + code: 'NoSuchMessage' + }); + return next(); + } + + let attachmentId = messageData.mimeTree.attachmentMap && messageData.mimeTree.attachmentMap[attachment]; + if (!attachmentId) { + res.json({ + error: 'This attachment does not exist' + }); + return next(); + } + + let attachmentData; + try { + attachmentData = await getAttachmentData(attachmentId); + } catch (err) { + res.json({ + error: err.message + }); + return next(); + } + + res.writeHead(200, { + 'Content-Type': attachmentData.contentType || 'application/octet-stream' + }); + + let decode = true; + + if (attachmentData.metadata.decoded) { + attachmentData.metadata.decoded = false; + decode = false; + } + + let attachmentStream = messageHandler.attachmentStorage.createReadStream(attachmentId, attachmentData); + + attachmentStream.once('error', err => { + log.error('API', 'message=%s attachment=%s error=%s', messageData._id, attachmentId, err.message); + try { + res.end(); + } catch (err) { + //ignore + } + }); + + if (!decode) { + attachmentStream.pipe(res); + return; + } + + if (attachmentData.transferEncoding === 'base64') { + attachmentStream.pipe(new libbase64.Decoder()).pipe(res); + } else if (attachmentData.transferEncoding === 'quoted-printable') { + attachmentStream.pipe(new libqp.Decoder()).pipe(res); + } else { + attachmentStream.pipe(res); + } + }) + ); /** * @api {put} /users/:user/mailboxes/:mailbox/messages/:message Update Message information