142 lines
5.1 KiB
JavaScript
142 lines
5.1 KiB
JavaScript
|
const gen = require('gen-thread');
|
||
|
const MailParser = require('mailparser').MailParser;
|
||
|
const htmlawed = require('htmlawed');
|
||
|
|
||
|
const express = require('express');
|
||
|
const express_session = require('express-session');
|
||
|
const bodyparser = require('body-parser');
|
||
|
const multer = require('multer');
|
||
|
|
||
|
module.exports = SyncerWeb;
|
||
|
|
||
|
function SyncerWeb(syncer, pg, cfg)
|
||
|
{
|
||
|
this.syncer = syncer;
|
||
|
this.pg = pg;
|
||
|
this.cfg = cfg;
|
||
|
this.app = express();
|
||
|
this.app.use(bodyparser.urlencoded({ extended: false }));
|
||
|
this.app.use(express_session({
|
||
|
secret: this.cfg.sessionSecret || '1083581xm1l3s1l39k',
|
||
|
resave: false,
|
||
|
saveUninitialized: false
|
||
|
}));
|
||
|
this.app.get('/auth', this.get_auth);
|
||
|
this.app.post('/auth', this.post_auth);
|
||
|
this.app.get('/folders', genRequest(this.get_folders.bind(this)));
|
||
|
this.app.get('/messages', genRequest(this.get_messages.bind(this)));
|
||
|
this.app.get('/message', genRequest(this.get_message.bind(this)));
|
||
|
this.app.post('/sync', genRequest(this.post_sync.bind(this)));
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.get_auth = function(req, res)
|
||
|
{
|
||
|
return res.type('html').send('<form action="/auth" method="post"><input name="login" /> <input name="password" type="password" /> <input type="submit" /></form>');
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.post_auth = function(req, res)
|
||
|
{
|
||
|
if (!req.body)
|
||
|
return res.sendStatus(400);
|
||
|
if (req.body.login == this.cfg.login && req.body.password == this.cfg.password)
|
||
|
{
|
||
|
req.session.auth = true;
|
||
|
return res.send({ ok: true });
|
||
|
}
|
||
|
return res.send({ ok: false });
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.get_folders = function*(req, res)
|
||
|
{
|
||
|
if (!req.session || !req.session.auth)
|
||
|
return res.sendStatus(401);
|
||
|
var [ accounts ] = yield this.pg.select('id, name, email').from('accounts').rows(gen.ef());
|
||
|
var [ folders ] = yield this.pg.select(
|
||
|
'id, account_id, name,'+
|
||
|
' (select count(*) from messages m where m.folder_id=f.id) total_count,'+
|
||
|
' (select count(*) from messages m where m.folder_id=f.id and (flags @> array[\'unread\']::varchar(255)[])) unread_count'
|
||
|
).from('folders f').orderBy('account_id, name').rows(gen.ef());
|
||
|
var fh = {};
|
||
|
for (let i = 0; i < folders.length; i++)
|
||
|
{
|
||
|
fh[folders[i].account_id] = fh[folders[i].account_id] || [];
|
||
|
fh[folders[i].account_id].push(folders[i]);
|
||
|
}
|
||
|
for (let i = 0; i < accounts.length; i++)
|
||
|
{
|
||
|
accounts[i].folders = fh[accounts[i].id] || [];
|
||
|
}
|
||
|
return res.send({ accounts: accounts });
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.get_messages = function*(req, res)
|
||
|
{
|
||
|
if (!req.session || !req.session.auth)
|
||
|
return res.sendStatus(401);
|
||
|
var folderId = req.query.folderId;
|
||
|
if (!folderId)
|
||
|
return res.status(500).send('Need `folderId` query parameter');
|
||
|
var limit = req.query.limit || 50;
|
||
|
var offset = req.query.offset || 0;
|
||
|
var [ msgs ] = yield this.pg.select('*').from('messages').where({ folder_id: folderId })
|
||
|
.orderBy('time desc').limit(limit).offset(offset).rows(gen.ef());
|
||
|
return res.send({ messages: msgs });
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.get_message = function*(req, res)
|
||
|
{
|
||
|
if (!req.session || !req.session.auth)
|
||
|
return res.sendStatus(401);
|
||
|
var msgId = req.query.msgId;
|
||
|
var [ msg ] = yield this.pg.select('m.*, f.name folder_name, f.account_id')
|
||
|
.from('messages m').join('folders f', this.pg.sql('f.id=m.folder_id'))
|
||
|
.where({ 'm.id': msgId }).row(gen.ef());
|
||
|
if (!msg)
|
||
|
return res.send({ error: 'not-found' });
|
||
|
delete msg.text_index;
|
||
|
if (!msg.body_html && !msg.body_text)
|
||
|
{
|
||
|
var srv = yield* this.syncer.imap.getConnection(msg.account_id, msg.folder_name);
|
||
|
var [ upd ] = yield* this.syncer.imap.runFetch(
|
||
|
srv, msg.uid, { bodies: '' },
|
||
|
(messages, state) => getBody(this.pg, messages, msg.folder_id)
|
||
|
);
|
||
|
this.syncer.imap.releaseConnection(msg.account_id);
|
||
|
return res.send({ msg: { ...msg, ...upd } });
|
||
|
}
|
||
|
return res.send({ msg: msg });
|
||
|
}
|
||
|
|
||
|
SyncerWeb.prototype.post_sync = function*(req, res)
|
||
|
{
|
||
|
if (!req.session || !req.session.auth)
|
||
|
return res.sendStatus(401);
|
||
|
if (self.syncer.syncInProgress)
|
||
|
return res.send({ error: 'already-running' });
|
||
|
gen.run(self.syncer.syncAll());
|
||
|
return res.send({ status: 'started' });
|
||
|
}
|
||
|
|
||
|
function* getBody(pg, messages, boxId)
|
||
|
{
|
||
|
var p = new MailParser({ streamAttachments: false, defaultCharset: 'windows-1251' });
|
||
|
for (var i = 0; i < messages.length; i++)
|
||
|
{
|
||
|
let msg = messages[i];
|
||
|
p.on('end', gen.cb());
|
||
|
p.write(msg[0].headers);
|
||
|
let [ obj ] = yield p.end();
|
||
|
obj.html = htmlawed.sanitize(obj.html||'', { safe: 1, elements: '* +style' });
|
||
|
let upd = { body_text: obj.text||'', body_html: obj.html };
|
||
|
upd.body_html_text = obj.html.replace(/<style[^>]*>.*<\/style\s*>|<\/?[^>]*>/g, '');
|
||
|
yield pg.update('messages m', upd).where({ folder_id: boxId, uid: msg[0].uid }).run(gen.ef());
|
||
|
if (messages.length == 1)
|
||
|
return [ upd ];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function genRequest(fn)
|
||
|
{
|
||
|
return (req, res) => gen.run(fn(req, res), null, e => res.status(500).send('Internal Error: '+e.stack));
|
||
|
}
|