From b8634a9269571a5a41fe7009513243c5016b1b00 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sun, 19 May 2019 15:25:14 +0300 Subject: [PATCH] Implement attachment downloading --- Syncer.js | 6 +++--- SyncerWeb.js | 41 +++++++++++++++++++++++++++++++++++++++++ operetta.js | 2 +- package.json | 1 + 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/Syncer.js b/Syncer.js index 1bdcce9..02bdcb9 100644 --- a/Syncer.js +++ b/Syncer.js @@ -431,7 +431,7 @@ class Syncer { byid[a.contentId||''] = a; } - msg.html = msg.html.replace(/(]*src=["']?)cid:([^'"\s]{1,256})/g, (m, m1, m2) => + msg.html = (msg.html||'').replace(/(]*src=["']?)cid:([^'"\s]{1,256})/g, (m, m1, m2) => { if (!byid[m2]) { @@ -618,12 +618,12 @@ class Syncer body_html: obj.html, body_html_text: obj.html.replace(/]*>.*<\/style\s*>|<\/?[^>]*>/g, ''), }; - /*await SQL.update( + await SQL.update( this.pg, 'messages m', { ...upd, 'props = props || ?': [ { attachments: obj.attachments } ] }, { folder_id: boxId, uid: msg[0].uid } - );*/ + ); if (messages.length == 1) { upd.props = { attachments: obj.attachments }; diff --git a/SyncerWeb.js b/SyncerWeb.js index 82e51bc..12b2a3e 100644 --- a/SyncerWeb.js +++ b/SyncerWeb.js @@ -6,6 +6,7 @@ const express_session = require('express-session'); const morgan = require('morgan'); const bodyparser = require('body-parser'); const multer = require('multer'); +const rawurlencode = require('rawurlencode'); const SQL = require('./select-builder-pgsql.js'); @@ -35,6 +36,7 @@ class SyncerWeb this.app.get('/groups', wrapAsync(this, 'get_groups')); this.app.get('/messages', wrapAsync(this, 'get_messages')); this.app.get('/message', wrapAsync(this, 'get_message')); + this.app.get('/attachment', wrapAsync(this, 'get_attachment')); this.app.post('/sync', wrapAsync(this, 'post_sync')); this.syncer.events.on('sync', (params) => this.syncer_sync(params)); } @@ -256,6 +258,45 @@ class SyncerWeb return res.send({ msg: msg }); } + async get_attachment(req, res) + { + if (this.cfg.login && (!req.session || !req.session.auth)) + { + return res.sendStatus(401); + } + let msgId = req.query.msgId; + let sha1 = (req.query.sha1||'').replace(/[^0-9a-fA-F]+/g, '').toLowerCase(); + if (!msgId || !sha1) + { + return res.sendStatus(404); + } + let msg = await SQL.select( + this.pg, 'messages', '*', { id: msgId }, null, SQL.MS_ROW + ); + let a = ((msg.props||{}).attachments||[]).filter(a => a.sha1 == sha1)[0]; + if (!a) + { + return res.sendStatus(404); + } + if (new RegExp( + // HTML may contain cookie-stealing JavaScript and web bugs + '^text/(html|(x-)?javascript)$|^application/x-shellscript$'+ + // PHP/Perl/Bash/etc scripts may execute arbitrary code on the server + '|php|perl|python|bash|x-c?sh(e|$)'+ + // Client-side hazards on Internet Explorer + '|^text/scriptlet$|^application/x-msdownload$'+ + // Windows metafile, client-side vulnerability on some systems + '|^application/x-msmetafile$', 'is' + ).exec(a.mimetype)) + { + a.mimetype = 'application/octet-stream'; + } + res.set('Content-Type', a.mimetype); + res.set('Content-Disposition', 'attachment; filename*=UTF-8\'\''+rawurlencode(a.name)); + await new Promise((r, j) => res.sendFile(this.syncer.files_path+'/'+sha1.substr(0, 2)+'/'+sha1.substr(2, 2)+'/'+sha1+'.bin', r)); + res.end(); + } + syncer_sync(params) { this.io.emit('sync', params); diff --git a/operetta.js b/operetta.js index 9f9c420..882469f 100644 --- a/operetta.js +++ b/operetta.js @@ -1,8 +1,8 @@ /** * TODO: + * + скачивание вложений * - пометка прочитанным, просмотренным (seen) * - фоновая индексация всех текстов сообщений в ящике - * - скачивание вложений * - написание сообщений * - ответ, пересылка * - подсказка адресов diff --git a/package.json b/package.json index 6581405..4ee31ed 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "nodemailer": "latest", "pg": "^7.10.0", "pg-escape": "^0.2.0", + "rawurlencode": "^1.0.2", "socket.io": "latest" }, "peerDependencies": {},