2017-10-19 16:19:16 +08:00
|
|
|
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0, no-console: 0 */
|
|
|
|
/* global before */
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2017-10-20 04:33:36 +08:00
|
|
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
|
|
|
|
|
2017-10-19 16:19:16 +08:00
|
|
|
const crypto = require('crypto');
|
|
|
|
//const util = require('util');
|
|
|
|
const chai = require('chai');
|
|
|
|
const request = require('request');
|
|
|
|
const fs = require('fs');
|
2017-10-20 04:33:36 +08:00
|
|
|
const BrowserBox = require('browserbox');
|
2017-10-19 16:19:16 +08:00
|
|
|
const simpleParser = require('mailparser').simpleParser;
|
|
|
|
const nodemailer = require('nodemailer');
|
|
|
|
|
|
|
|
const transporter = nodemailer.createTransport({
|
|
|
|
lmtp: true,
|
|
|
|
host: 'localhost',
|
|
|
|
port: 2424,
|
|
|
|
logger: false,
|
|
|
|
debug: false,
|
|
|
|
tls: {
|
|
|
|
rejectUnauthorized: false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const expect = chai.expect;
|
|
|
|
chai.config.includeStack = true;
|
|
|
|
|
|
|
|
const URL = 'http://localhost:8080';
|
|
|
|
const user2PubKey = fs.readFileSync(__dirname + '/fixtures/user2-public.key', 'utf-8');
|
|
|
|
const user3PubKey = fs.readFileSync(__dirname + '/fixtures/user3-public.key', 'utf-8');
|
|
|
|
|
|
|
|
describe('Send multiple messages', function() {
|
|
|
|
this.timeout(100 * 1000); // eslint-disable-line
|
|
|
|
|
|
|
|
let userIds = [];
|
|
|
|
|
|
|
|
before(done => {
|
|
|
|
request.post(
|
|
|
|
URL + '/users',
|
|
|
|
{
|
|
|
|
json: {
|
|
|
|
username: 'user1',
|
|
|
|
password: 'secretpass',
|
|
|
|
address: 'user1@example.com',
|
|
|
|
name: 'user1'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
userIds.push(response.id);
|
|
|
|
request.post(
|
|
|
|
URL + '/users',
|
|
|
|
{
|
|
|
|
json: {
|
|
|
|
username: 'user2',
|
|
|
|
password: 'secretpass',
|
|
|
|
address: 'user2@example.com',
|
|
|
|
name: 'user2',
|
|
|
|
pubKey: user2PubKey,
|
2017-10-30 19:41:53 +08:00
|
|
|
encryptMessages: true,
|
|
|
|
encryptForwarded: true
|
2017-10-19 16:19:16 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
(err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
userIds.push(response.id);
|
|
|
|
request.post(
|
|
|
|
URL + '/users',
|
|
|
|
{
|
|
|
|
json: {
|
|
|
|
username: 'user3',
|
|
|
|
password: 'secretpass',
|
|
|
|
address: 'user3@example.com',
|
|
|
|
name: 'user3',
|
|
|
|
pubKey: user3PubKey,
|
2017-10-30 19:41:53 +08:00
|
|
|
encryptMessages: true,
|
|
|
|
encryptForwarded: true
|
2017-10-19 16:19:16 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
(err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
userIds.push(response.id);
|
|
|
|
request.post(
|
|
|
|
URL + '/users',
|
|
|
|
{
|
|
|
|
json: {
|
|
|
|
username: 'user4',
|
|
|
|
password: 'secretpass',
|
|
|
|
address: 'user4@example.com',
|
|
|
|
name: 'user4',
|
|
|
|
pubKey: user2PubKey,
|
2017-10-30 19:41:53 +08:00
|
|
|
encryptMessages: false,
|
|
|
|
encryptForwarded: true
|
2017-10-19 16:19:16 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
(err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
userIds.push(response.id);
|
|
|
|
request.post(
|
|
|
|
URL + '/users',
|
|
|
|
{
|
|
|
|
json: {
|
|
|
|
username: 'user5',
|
|
|
|
password: 'secretpass',
|
|
|
|
address: 'user5@example.com',
|
|
|
|
name: 'user5'
|
|
|
|
}
|
|
|
|
},
|
|
|
|
(err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
userIds.push(response.id);
|
|
|
|
done();
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Should have users set', done => {
|
|
|
|
expect(userIds.length).to.equal(5);
|
|
|
|
done();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('Send mail to all users', done => {
|
|
|
|
let recipients = ['user1@example.com', 'user2@example.com', 'user3@example.com', 'user4@example.com', 'user5@example.com'];
|
|
|
|
let subject = 'Test ööö message [' + Date.now() + ']';
|
|
|
|
transporter.sendMail(
|
|
|
|
{
|
|
|
|
envelope: {
|
|
|
|
from: 'andris@kreata.ee',
|
|
|
|
to: recipients
|
|
|
|
},
|
|
|
|
|
|
|
|
headers: {
|
|
|
|
// set to Yes to send this message to Junk folder
|
|
|
|
'x-rspamd-spam': 'No'
|
|
|
|
},
|
|
|
|
|
|
|
|
from: 'Kärbes 🐧 <andris@kreata.ee>',
|
|
|
|
to: recipients.map((rcpt, i) => ({ name: 'User #' + (i + 1), address: rcpt })),
|
|
|
|
subject,
|
|
|
|
text: 'Hello world! Current time is ' + new Date().toString(),
|
|
|
|
html:
|
|
|
|
'<p>Hello world! Current time is <em>' +
|
|
|
|
new Date().toString() +
|
|
|
|
'</em> <img src="cid:note@example.com"/> <img src="http://www.neti.ee/img/neti-logo-2015-1.png"></p>',
|
|
|
|
attachments: [
|
|
|
|
// attachment as plaintext
|
|
|
|
{
|
|
|
|
filename: 'notes.txt',
|
|
|
|
content: 'Some notes about this e-mail',
|
|
|
|
contentType: 'text/plain' // optional, would be detected from the filename
|
|
|
|
},
|
|
|
|
|
|
|
|
// Small Binary Buffer attachment, should be kept with message
|
|
|
|
{
|
|
|
|
filename: 'image.png',
|
2018-05-11 19:39:23 +08:00
|
|
|
content: Buffer.from(
|
2017-10-19 16:19:16 +08:00
|
|
|
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/' +
|
|
|
|
'//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U' +
|
|
|
|
'g9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
|
|
|
|
'base64'
|
|
|
|
),
|
|
|
|
|
|
|
|
cid: 'note@example.com' // should be as unique as possible
|
|
|
|
},
|
|
|
|
|
|
|
|
// Large Binary Buffer attachment, should be kept separately
|
|
|
|
{
|
|
|
|
path: __dirname + '/../examples/swan.jpg',
|
|
|
|
filename: 'swän.jpg'
|
|
|
|
}
|
|
|
|
]
|
|
|
|
},
|
|
|
|
(err, info) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(info.accepted).to.deep.equal(['user1@example.com', 'user2@example.com', 'user3@example.com', 'user4@example.com', 'user5@example.com']);
|
|
|
|
|
|
|
|
let getFirstMessage = (userId, callback) => {
|
|
|
|
request(URL + '/users/' + userId + '/mailboxes', { json: true }, (err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
let inbox = response.results.find(mbox => mbox.path === 'INBOX');
|
|
|
|
request(URL + '/users/' + userId + '/mailboxes/' + inbox.id + '/messages', { json: true }, (err, meta, response) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(response.success).to.be.true;
|
|
|
|
|
|
|
|
let message = response.results[0];
|
|
|
|
expect(message).to.exist;
|
|
|
|
|
|
|
|
request(URL + '/users/' + userId + '/mailboxes/' + inbox.id + '/messages/' + message.id, { json: true }, (err, meta, message) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
|
|
|
|
let processAttachments = next => {
|
|
|
|
let pos = 0;
|
|
|
|
let getAttachments = () => {
|
|
|
|
if (pos >= message.attachments.length) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
let attachment = message.attachments[pos++];
|
2017-11-30 19:57:25 +08:00
|
|
|
request(
|
|
|
|
URL +
|
|
|
|
'/users/' +
|
|
|
|
message.user +
|
|
|
|
'/mailboxes/' +
|
|
|
|
message.mailbox +
|
|
|
|
'/messages/' +
|
|
|
|
message.id +
|
|
|
|
'/attachments/' +
|
|
|
|
attachment.id,
|
|
|
|
{ encoding: null },
|
|
|
|
(err, meta, raw) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
attachment.raw = raw;
|
|
|
|
setImmediate(getAttachments);
|
|
|
|
}
|
|
|
|
);
|
2017-10-19 16:19:16 +08:00
|
|
|
};
|
|
|
|
setImmediate(getAttachments);
|
|
|
|
};
|
|
|
|
|
|
|
|
processAttachments(() => {
|
|
|
|
request(
|
|
|
|
URL + '/users/' + userId + '/mailboxes/' + inbox.id + '/messages/' + message.id + '/message.eml',
|
|
|
|
(err, meta, raw) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
|
|
|
|
message.raw = raw;
|
|
|
|
|
|
|
|
simpleParser(raw, (err, parsed) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
message.parsed = parsed;
|
|
|
|
callback(null, message);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
let checkNormalUsers = next => {
|
|
|
|
let npos = 0;
|
|
|
|
let nusers = [1, 4, 5];
|
|
|
|
let checkUser = () => {
|
|
|
|
if (npos >= nusers.length) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
let user = nusers[npos++];
|
|
|
|
getFirstMessage(userIds[user - 1], (err, message) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(message.subject).to.equal(subject);
|
|
|
|
expect(message.attachments.length).to.equal(3);
|
|
|
|
expect(message.parsed.attachments.length).to.equal(3);
|
|
|
|
for (let i = 0; i < message.attachments.length; i++) {
|
|
|
|
let hashA = crypto
|
|
|
|
.createHash('md5')
|
|
|
|
.update(message.attachments[i].raw)
|
|
|
|
.digest('hex');
|
|
|
|
let hashB = crypto
|
|
|
|
.createHash('md5')
|
|
|
|
.update(message.parsed.attachments[i].content)
|
|
|
|
.digest('hex');
|
|
|
|
expect(hashA).equal(hashB);
|
|
|
|
}
|
|
|
|
expect(message.parsed.to.value).deep.equal([
|
|
|
|
{ address: 'user1@example.com', name: 'User #1' },
|
|
|
|
{ address: 'user2@example.com', name: 'User #2' },
|
|
|
|
{ address: 'user3@example.com', name: 'User #3' },
|
|
|
|
{ address: 'user4@example.com', name: 'User #4' },
|
|
|
|
{ address: 'user5@example.com', name: 'User #5' }
|
|
|
|
]);
|
|
|
|
expect(message.parsed.headers.get('delivered-to').value[0].address).equal('user' + user + '@example.com');
|
|
|
|
|
|
|
|
setImmediate(checkUser);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
setImmediate(checkUser);
|
|
|
|
};
|
|
|
|
|
|
|
|
let checkEncryptedUsers = next => {
|
|
|
|
let npos = 0;
|
|
|
|
let nusers = [2, 3];
|
|
|
|
let checkUser = () => {
|
|
|
|
if (npos >= nusers.length) {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
let user = nusers[npos++];
|
|
|
|
getFirstMessage(userIds[user - 1], (err, message) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
|
|
|
|
expect(message.subject).to.equal(subject);
|
|
|
|
expect(message.parsed.to.value).deep.equal([
|
|
|
|
{ address: 'user1@example.com', name: 'User #1' },
|
|
|
|
{ address: 'user2@example.com', name: 'User #2' },
|
|
|
|
{ address: 'user3@example.com', name: 'User #3' },
|
|
|
|
{ address: 'user4@example.com', name: 'User #4' },
|
|
|
|
{ address: 'user5@example.com', name: 'User #5' }
|
|
|
|
]);
|
|
|
|
expect(message.parsed.headers.get('delivered-to').value[0].address).equal('user' + user + '@example.com');
|
|
|
|
expect(message.parsed.attachments.length).equal(2);
|
|
|
|
expect(message.parsed.attachments[0].contentType).equal('application/pgp-encrypted');
|
|
|
|
expect(message.parsed.attachments[0].content.toString()).equal('Version: 1\r\n');
|
|
|
|
expect(message.parsed.attachments[1].contentType).equal('application/octet-stream');
|
|
|
|
expect(message.parsed.attachments[1].filename).equal('encrypted.asc');
|
|
|
|
expect(message.parsed.attachments[1].size).gte(1000000);
|
|
|
|
setImmediate(checkUser);
|
|
|
|
});
|
|
|
|
};
|
|
|
|
setImmediate(checkUser);
|
|
|
|
};
|
|
|
|
|
|
|
|
checkNormalUsers(() => checkEncryptedUsers(() => done()));
|
|
|
|
}
|
|
|
|
);
|
|
|
|
});
|
2017-10-20 04:33:36 +08:00
|
|
|
|
|
|
|
it('should fetch messages from IMAP', done => {
|
2018-05-11 19:39:23 +08:00
|
|
|
let imagePng = Buffer.from(
|
2017-10-20 04:33:36 +08:00
|
|
|
'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD/' +
|
|
|
|
'//+l2Z/dAAAAM0lEQVR4nGP4/5/h/1+G/58ZDrAz3D/McH8yw83NDDeNGe4U' +
|
|
|
|
'g9C9zwz3gVLMDA/A6P9/AFGGFyjOXZtQAAAAAElFTkSuQmCC',
|
|
|
|
'base64'
|
|
|
|
);
|
|
|
|
let textTxt = 'Some notes about this e-mail';
|
|
|
|
let swanJpg = fs.readFileSync(__dirname + '/../examples/swan.jpg');
|
|
|
|
|
|
|
|
let checksums = [
|
|
|
|
crypto
|
|
|
|
.createHash('md5')
|
|
|
|
.update(imagePng)
|
|
|
|
.digest('hex'),
|
|
|
|
crypto
|
|
|
|
.createHash('md5')
|
|
|
|
.update(Buffer.from(textTxt))
|
|
|
|
.digest('hex'),
|
|
|
|
crypto
|
|
|
|
.createHash('md5')
|
|
|
|
.update(swanJpg)
|
|
|
|
.digest('hex')
|
|
|
|
];
|
|
|
|
|
|
|
|
const client = new BrowserBox('localhost', 9993, {
|
|
|
|
useSecureTransport: true,
|
|
|
|
auth: {
|
|
|
|
user: 'user4',
|
|
|
|
pass: 'secretpass'
|
|
|
|
},
|
|
|
|
id: {
|
|
|
|
name: 'My Client',
|
|
|
|
version: '0.1'
|
|
|
|
},
|
|
|
|
tls: {
|
|
|
|
rejectUnauthorized: false
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
client.onerror = err => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
};
|
|
|
|
|
|
|
|
client.onclose = done;
|
|
|
|
|
|
|
|
client.onauth = () => {
|
|
|
|
client.listMailboxes((err, result) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
let folders = result.children.map(mbox => ({ name: mbox.name, specialUse: mbox.specialUse || false }));
|
|
|
|
expect(folders).to.deep.equal([
|
|
|
|
{ name: 'INBOX', specialUse: false },
|
|
|
|
{ name: 'Drafts', specialUse: '\\Drafts' },
|
|
|
|
{ name: 'Junk', specialUse: '\\Junk' },
|
|
|
|
{ name: 'Sent Mail', specialUse: '\\Sent' },
|
|
|
|
{ name: 'Trash', specialUse: '\\Trash' }
|
|
|
|
]);
|
|
|
|
client.selectMailbox('INBOX', { condstore: true }, (err, result) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(result.exists).gte(1);
|
|
|
|
|
|
|
|
client.listMessages(result.exists, ['uid', 'flags', 'body.peek[]'], (err, messages) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
expect(messages.length).equal(1);
|
|
|
|
|
|
|
|
let messageInfo = messages[0];
|
|
|
|
simpleParser(messageInfo['body[]'], (err, parsed) => {
|
|
|
|
expect(err).to.not.exist;
|
|
|
|
checksums.forEach((checksum, i) => {
|
|
|
|
expect(checksum).to.equal(parsed.attachments[i].checksum);
|
|
|
|
});
|
|
|
|
client.close();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
client.connect();
|
|
|
|
});
|
2017-10-19 16:19:16 +08:00
|
|
|
});
|