UNPKG

@aarconada/urserver

Version:

Basic Server definitions to develope REST API with a node + express Server

320 lines (305 loc) 11.7 kB
const Imap = require('imap'); const promise = require('bluebird'); const inspect = require('util').inspect; const _ = require('lodash'); const server = require('./server')(); class imapMessage { constructor({seqno, msg}) { server.debug('Message #%d', seqno); this.seqno = seqno; this.headers = null; this.attributes = null; this.msg = msg; this.msgBuffer = null; this.info = null; this.prefix = '(#' + this.seqno + ') '; this.msg.on('body', this.onBody.bind(this)); this.msg.once('attributes', this.onAttributes.bind(this)); this.msg.once('end', this.onEnd.bind(this)); } onBody(stream, info) { this.info = info; this.msgBuffer = ''; stream.on('data', this.onStreamData.bind(this)) .once('end', this.onStreamEnd.bind(this)); }; onAttributes(attrs) { this.attributes = attrs; server.debug('-->', this.attributes.date); server.debug(this.prefix + 'Attributes: %s', inspect(attrs, false, 8)); }; onEnd() { server.debug(this.prefix + 'Finished'); }; onStreamData(chunk) { this.msgBuffer += chunk.toString('utf8'); } onStreamEnd() { this.headers = Imap.parseHeader(this.msgBuffer); server.debug(this.prefix + 'Parsed header:', this.headers); } } class imapAccount { constructor({user, password, host, port, tls}) { this.user = user; this.password = password; this.host = host; this.port = port; this.tls = tls; } } class imapBox { constructor({name, attribs, delimiter, children, parent, imapMonitor, readonly}) { this.name = name; this.attribs = attribs; this.delimiter = delimiter; this.children = children; this.parentName = _.isNull(parent) ? '' : parent.parentName + parent.name + delimiter; this.opened = false; this.readonly = readonly || false; this.totalMessages = null; this.newMessages = null; this.unseenMessages = null; this.canBeOpened = _.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\NOSELECT')); this.hasChildren = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\HASCHILDREN')); this.isDrafts = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\DRAFTS')); this.isFlagged = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\FLAGGED')); this.isAll = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\ALL')); this.isTrash = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\TRASH')); this.isImportant = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\IMPORTANT')); this.isSent = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\SENT')); this.isJunk = !_.isUndefined(this.attribs.find(currentAttrib => currentAttrib.toUpperCase() === '\\JUNK')); } onOpen(err, box) { this.opened = _.isUndefined(err); if(this.opened) { this.totalMessages = box.messages.total || 0; this.newMessages = box.messages.new || 0; this.unseenMessages = box.messages.unseen || 0; } server.debug(this); } open(imapMonitor) { return new promise((resolve, reject) => { if (this.canBeOpened && !this.opened && imapMonitor.ready) { imapMonitor.imap.openBox(this.parentName + this.name, this.readonly, (err, box) => { this.opened = _.isUndefined(err); if (this.opened) { this.totalMessages = box.messages.total || 0; this.newMessages = box.messages.new || 0; this.unseenMessages = box.messages.unseen || 0; resolve(this); } else { resolve(null); } } ); } else { resolve(null); } }); } } class imapMonitor { constructor(account) { server.debug('Connecting to inbox', account); //account.debug = this.onDebug.bind(this); this.managedBoxes = []; this.availableBoxes = []; this.ready = false; this.account = account; this.imap = new Imap(account); this.imap .on('alert', this.onAlert.bind(this)) .on('close', this.onClose.bind(this)) .on('end', this.onEnd.bind(this)) .on('error', this.onError.bind(this)) .on('expunge', this.onExpunge.bind(this)) .on('mail', this.onMail.bind(this)) .on('message', this.onMessage.bind(this)) // .on('ready', this.onReady.bind(this)) .on('uidvalidity', this.onUidvalidity.bind(this)) .on('update', this.onUpdate.bind(this)) } onAlert(message) { server.debug('Alert'); } onClose(hadError) { server.debug('CLOSE', hadError) } onDebug(message) { server.debug(message); } onEnd () { server.debug('Connection ended'); this.ready = false; } onError (err) { server.debug('ERROR', err); this.ready = false; } onExpunge(seqno) { server.debug('EXPUNGE', seqno); } onGetBoxes(err, boxes) { if(err && err !== null) { server.debug('Get Boxes Error', err); return; } this.availableBoxes = this.processBoxes(boxes, null); } onMail(numNewMsgs) { server.debug('MAIL:', numNewMsgs); } onMessage(emitter, seqno) { server.debug('MESSAGE:', emitter, seqno); } onReady() { server.debug('READY'); this.ready = true; this.imap.getBoxes(this.onGetBoxes.bind(this)); } onUidvalidity(uidvalidity) { server.debug('UIDVALIDITY', uidvalidity); } onUpdate(seqno, info) { server.debug('UPDATE:', seqno, info); } connect() { return new promise((resolve, reject) => { this.imap .once('ready', () => { this.ready = true; this.imap.getBoxes((err, boxes) => { if(err && err !== null) { server.debug('Get Boxes Error', err); return; } this.processBoxes(boxes, null) .then(result => { this.availableBoxes = result; resolve(this); }) .catch(err => { reject(err); }); }); }) .once('error', (err) => { reject(err); }); this.imap.connect(); }); } processBoxes(boxesToProcess, parent) { return new promise((resolve, reject) => { const resultBoxes = []; const boxesToProcessArray = Object.keys(boxesToProcess); var currentBoxIndex = 1; if(boxesToProcessArray.length > 0) { boxesToProcessArray.forEach(currentKey => { const currentBox = boxesToProcess[currentKey]; const newBox = new imapBox({ name: currentKey, attribs: currentBox.attribs, delimiter: currentBox.delimiter, currentBoxes: null, parent: parent, imapMonitor: this }); if (currentBox.children !== null) { this.processBoxes(currentBox.children, newBox) .then(childrenBox => { newBox.children = childrenBox; newBox.open(this).then(result => { resultBoxes.push(newBox); if (currentBoxIndex === boxesToProcessArray.length) { resolve(resultBoxes); } else { currentBoxIndex++; } }); }) } else { newBox.open(this).then(result => { resultBoxes.push(newBox); if (currentBoxIndex === boxesToProcessArray.length) { resolve(resultBoxes); } else { currentBoxIndex++; } }); } }); } else { resolve([]); } }); }; fetchBox(boxToRead) { return new promise((resolve, reject) => { if (boxToRead.totalMessages > 0) { boxToRead.open(this).then(result => { this.fetch('1:' + boxToRead.totalMessages, { bodies: 'HEADER.FIELDS (FROM TO SUBJECT DATE)', struct: true }).then(newBoxMessages => { server.debug(boxToRead.name + ' MESSAGES', newBoxMessages); resolve(newBoxMessages); }).catch(err => { reject(err); }) }); } else { resolve([]); } }); } fetch(source, options) { return new promise((resolve, reject) => { const messages = []; this.imap.seq.fetch(source, options) .on('message', function (msg, seqno) { const newMessage = new imapMessage({ msg: msg, seqno: seqno }); messages.push(newMessage); }) .once('error', function (err) { server.debug('Fetch error: ' + err); reject(err); }) .once('end', function () { server.debug('Done fetching all messages!'); resolve(messages); }); }); } } module.exports.gmailAccount = function({user, password}) { if(!_.isUndefined(user) && !_.isEmpty(user) && !_.isUndefined(password) && !_.isEmpty(password)) { return new imapMonitor(new imapAccount({ user: user, password: password, host: 'imap.gmail.com', port: 993, tls: true })); } else { return null; } }; module.exports.outlookAccount = function({user, password}) { if(!_.isUndefined(user) && !_.isEmpty(user) && !_.isUndefined(password) && !_.isEmpty(password)) { return new imapMonitor(new imapAccount({ user: user, password: password, host: 'Outlook.Office365.com', port: 993, tls: true })); } else { return null; } };