@aarconada/urserver
Version:
Basic Server definitions to develope REST API with a node + express Server
320 lines (305 loc) • 11.7 kB
JavaScript
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;
}
};