Added tests for sending messages to LMTP

This commit is contained in:
Andris Reinman 2017-10-19 11:19:16 +03:00
parent 34e551497c
commit b53ae97938
7 changed files with 684 additions and 1 deletions

View file

@ -1,6 +1,7 @@
# If enabled then Wild Duck exposes a LMTP interface for pushing messages to mail store
enabled=true
port=2424
# by default bind to localhost only
host="127.0.0.1"

View file

@ -20,7 +20,9 @@
"grunt-shell-spawn": "^0.3.10",
"grunt-wait": "^0.1.0",
"icedfrisby": "^1.4.0",
"mocha": "^4.0.1"
"mailparser": "^2.1.0",
"mocha": "^4.0.1",
"request": "^2.83.0"
},
"dependencies": {
"addressparser": "1.0.1",

502
test/filtering-test.js Normal file
View file

@ -0,0 +1,502 @@
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0, no-console: 0 */
/* global before */
'use strict';
const crypto = require('crypto');
//const util = require('util');
const chai = require('chai');
const request = require('request');
const fs = require('fs');
//const nodemailer = require('nodemailer');
//const imapHandler = require('../lib/handler/imap-handler');
//const userHandler = require('../lib/handler/user-handler');
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,
encryptMessages: true
}
},
(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,
encryptMessages: true
}
},
(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,
encryptMessages: false
}
},
(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',
content: new Buffer(
'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++];
request(URL + attachment.url, { encoding: null }, (err, meta, raw) => {
expect(err).to.not.exist;
attachment.raw = raw;
setImmediate(getAttachments);
});
};
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()));
}
);
});
it('Send should send mail to spam', 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': 'Yes'
},
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',
content: new Buffer(
'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.specialUse === '\\Junk');
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++];
request(URL + attachment.url, { encoding: null }, (err, meta, raw) => {
expect(err).to.not.exist;
attachment.raw = raw;
setImmediate(getAttachments);
});
};
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()));
}
);
});
});

58
test/fixtures/user2-private.key vendored Normal file
View file

@ -0,0 +1,58 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
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
=mnIh
-----END PGP PRIVATE KEY BLOCK-----

31
test/fixtures/user2-public.key vendored Normal file
View file

@ -0,0 +1,31 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mQENBFnoTuIBCADI/rapDP+RIQVyV4bcvPlw5vaLUwQIVYx80aK+hcjbM7fDYq7M
YLinidRMKqs8a8uQKLCVNWUT1e68CBr1e65hYlLoqCcI09djaQ48HHF31ts+RSv/
sYoJdQPL0zaRRzHWH6MROMRAQSY4lFsTxf/szR5PYYGWu+u/0OhYxkqFhjmUllSD
tNE1F46E3RpC3A++kGSCq0kwhDTiePHkf3Pdzw/2xPCQpjJQxSYe5oRX0S7a7YXj
PWqbsHyHfOnzv/njkQRTKpLSzgFP7M1YKxBIkqZnCx3hZdSdfnKpKJyByL42+f2X
B8p2FBOMVd4alT3YxGlYcb1/HHQSt5l7AfIHABEBAAG0GlVzZXIgMiA8dXNlcjJA
ZXhhbXBsZS5jb20+iQFUBBMBCgA+FiEE8I/FYdl9ZWL/l03tHKPFXpk104kFAlno
TuICGwMFCQPCZwAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQHKPFXpk104mC
sAgAh7aRBmivj02ZoK4k5ZkAkV6PPTPYGIUbBNsumwF/PQqoi51ZREMbocfxlRAO
XXUh+8lsfNYENklZhMhVwpb8IW5XUpHXXy5ZtziVv9KHnW5fJW3U4qxpzqj0HS46
VvjtMj7UOEFCbj8zEs0L3M1joJVUjXmX8UXUPMZw3J8vfLRQcC7vdJZFUqaN+4+O
q+MWNIvvyB/izOLMavDhBVNF3hhKADtKXjMlwpmNpNHmPFcItPMX8nd366p+iPaW
TxrV78afgYDaZmQXToQIMQetThcOj38EU/iIlgNIQ/yJlbG4HY8KYr3IBWX0YggW
TJ5xLp04BMwakMrhL1DYaaRhVrkBDQRZ6E7iAQgAwrJrUbqPu2cOfisuFEb0JJFm
2Ly4CgTaGusglzo5i3UeVDErfOTNT1CTUEQuJEDed2P6+5Pp7VtH7ps9O00GiIJX
B2JWb/Xh3Y+bBQ5PKggi0BiNsPsfwwkw/ELho/lZAv4vap0LO9vLubqFcvbhA+pJ
2hmOEkxSp0XrQUXsZfCAqiiOA0Rj3AkTawXsjl2It/e6kV8KXNmgZ4ckzBF8i6gp
d0Za5HHsFZnaJ6x3Fa3cV2TCkzAwa3tK+G55eCUQVOMPORtxt/67CZXtVVdNZQmV
4lNtyO+XZpP0n/nulAg0xZAcAVXzZFvVAtSalzXxrhYpTsldSCooyUomWNuEwwAR
AQABiQE8BBgBCgAmFiEE8I/FYdl9ZWL/l03tHKPFXpk104kFAlnoTuICGwwFCQPC
ZwAACgkQHKPFXpk104m/cggAkv+vSSfDkN77p+xPdXjnk7dhdDLm+T+Ye8CcoIik
iDxV2E50Mw7WBBZ/cusmpSbEXcFJUTSdx3MoH2ZuiLUQWJTBY8G6vktdzA5ZohPI
l8czQ/W4Q5HFiELlykGFFDphqjadmkk8FPJ34UfdaFiwDfjbzNW5yOhO5RS3ogxZ
hvJKTX1ASYWqahtazt6HgSfcfdYMwzNzd/a7CQIcUwskDxHPRqHPug1gwIGk05gW
mOwn9E06Bs18T8ctubf+3VPVLrv1wQe4Gu/D0K6md8mMSA6HbcNbQ9Tn70OS3TNL
wngkXBeL1+lbN4rfrStVzWfVvRdpFBYPtnAn2dG7Q1p3qQ==
=kMxV
-----END PGP PUBLIC KEY BLOCK-----

58
test/fixtures/user3-private.key vendored Normal file
View file

@ -0,0 +1,58 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
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
=MWu4
-----END PGP PRIVATE KEY BLOCK-----

31
test/fixtures/user3-public.key vendored Normal file
View file

@ -0,0 +1,31 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: GPGTools - https://gpgtools.org
mQENBFnoT18BCAC60OTvCb8If9FnmhfrRnLOhC1awBVPekAeVpFqBgKjh8RDq8bb
39JuRA3UXDr89DJog7OuTzr8B4pIjnRdqTfhVoqGyjXjh5G5199jQ8gpwhr7qFTm
PUfJAZxiw/GGG5Zx4b33gvyVaxJFnWyxP4W82nxvrNkwbCnejzHxLZs/sApn1xz3
PUmTgdlzC0e6elWu2jWXQgUupYzw2VhnnIo/v0Se2An2ZWT36Q3drkR6XbpCBMJS
DJWTbyqsPIR20BiYLNEnKczBorTYP1O2pkw94g852193QIs4n+P9G4ulX1JwmbyV
6yZkbig7RjfBjFVByBwJCqq9GQzKSrWMxFQFABEBAAG0GlVzZXIgMyA8dXNlcjNA
ZXhhbXBsZS5jb20+iQFUBBMBCgA+FiEEsyqWn3YKjtDW1nzadq7TCKLpcAEFAlno
T18CGwMFCQPCZwAFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AACgkQdq7TCKLpcAGj
QQf/StaNTrH2IbNSPym4eUkr/p0e8iED0w0+gn1Z5YN+FZ8l5vhzrVvqWXL5B9Up
xfgKja3ipR89Z95h4IUPjW5xsMkcwd8AZ46axHSyPov872/3xf9RJvEYzPXUUTaB
23RVp+xxunZUp4pJSOlrMonVHcPrB2ufB8VKYNaYow6oXbxC7iUzoWYsKN4Oqtve
2BYf0JpWXLAo6pVR9X+Dz+r6gaAiEFvxPdJIVsb9JeR2NLLnDXmK1tvTpgp9dKZn
WFBxBF1QQWnPxBsMaXkaXkcmKpmiWg+kn5nmDVbOazZJAqyKkkwFVOL4tCIJxDEN
PTXXTTsu1KevRKRKfCeC18zrPrkBDQRZ6E9fAQgAzCn7g8lIRlJk5Lrc0sKEueQN
Gn3MNxpZ6Xz/wTLc7KfWeTEzO/mKKhfuSGqUBoESYM56AtD6HaKNWIt9EPxWbI5t
we1BuOt7Kjic8QNB0gcOxqba76jzpUcVr/z8Y4jShd03ayZm9bTWOTssW76abibv
91A3109HRN9QkRCjcEaLDLSTrdBvCg8bgQ1XS7hZSYEZLNoa8myplf5TasuynOtG
jkU4NvO62S6FhHW4flWx/dn7wCWqHnKwSjCu4tvDdYC4WXpIwkHxFM5xsS3J5RW3
1qzYpHOoLq/z+Nbiqs5hXHwpgdtOtdJ4EF+EuEDQQZuEsEfIAh8FU+4pvyLxFwAR
AQABiQE8BBgBCgAmFiEEsyqWn3YKjtDW1nzadq7TCKLpcAEFAlnoT18CGwwFCQPC
ZwAACgkQdq7TCKLpcAEb5Af9Hw+GoxL7gt6lZQP717cnwF8ENEQV+ucY4hiM5zcZ
TH6ZDAui8u976T2y1tLjV5K7wxR13dIyKEyaUP2DNwXXCKuj1FE/qq6nxElyfYb1
QZbaC3pTRM9zz03azO5UKlXrubu7IT2mn7HXhhMxJSLXLylK2b5Z8BjbrCthXKEI
+IettPZ4OA1+aQSTYt021fBNeiGK09Ck8OKzunxaRhsuxZ1PrqaRoYz01rV0KYzU
jAEP8UJ+oEfzy6jJviZcKua+ghTX6Ptvth6Ga6JwRBudLzw4zrZfq+D4Kvfzvylb
dbM2iBdJljBORA0PGgBm/MLzx/slcVZKEN6quSrJuuY51Q==
=NKWL
-----END PGP PUBLIC KEY BLOCK-----