diff --git a/config/lmtp.toml b/config/lmtp.toml index c94d7906..83cdf1ff 100644 --- a/config/lmtp.toml +++ b/config/lmtp.toml @@ -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" diff --git a/package.json b/package.json index 7d931577..db27656f 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/filtering-test.js b/test/filtering-test.js new file mode 100644 index 00000000..e8a28e74 --- /dev/null +++ b/test/filtering-test.js @@ -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 🐧 ', + to: recipients.map((rcpt, i) => ({ name: 'User #' + (i + 1), address: rcpt })), + subject, + text: 'Hello world! Current time is ' + new Date().toString(), + html: + '

Hello world! Current time is ' + + new Date().toString() + + '

', + 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 🐧 ', + to: recipients.map((rcpt, i) => ({ name: 'User #' + (i + 1), address: rcpt })), + subject, + text: 'Hello world! Current time is ' + new Date().toString(), + html: + '

Hello world! Current time is ' + + new Date().toString() + + '

', + 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())); + } + ); + }); +}); diff --git a/test/fixtures/user2-private.key b/test/fixtures/user2-private.key new file mode 100644 index 00000000..18ca4bfc --- /dev/null +++ b/test/fixtures/user2-private.key @@ -0,0 +1,58 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: GPGTools - https://gpgtools.org + +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 +=mnIh +-----END PGP PRIVATE KEY BLOCK----- diff --git a/test/fixtures/user2-public.key b/test/fixtures/user2-public.key new file mode 100644 index 00000000..6dc7c351 --- /dev/null +++ b/test/fixtures/user2-public.key @@ -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----- diff --git a/test/fixtures/user3-private.key b/test/fixtures/user3-private.key new file mode 100644 index 00000000..4a8fc972 --- /dev/null +++ b/test/fixtures/user3-private.key @@ -0,0 +1,58 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- +Comment: GPGTools - https://gpgtools.org + +lQOYBFnoT18BCAC60OTvCb8If9FnmhfrRnLOhC1awBVPekAeVpFqBgKjh8RDq8bb +39JuRA3UXDr89DJog7OuTzr8B4pIjnRdqTfhVoqGyjXjh5G5199jQ8gpwhr7qFTm +PUfJAZxiw/GGG5Zx4b33gvyVaxJFnWyxP4W82nxvrNkwbCnejzHxLZs/sApn1xz3 +PUmTgdlzC0e6elWu2jWXQgUupYzw2VhnnIo/v0Se2An2ZWT36Q3drkR6XbpCBMJS +DJWTbyqsPIR20BiYLNEnKczBorTYP1O2pkw94g852193QIs4n+P9G4ulX1JwmbyV +6yZkbig7RjfBjFVByBwJCqq9GQzKSrWMxFQFABEBAAEAB/wJzNeVqp845OupUa4b +i5COQOTYM+dqSfF/Je9u7m3JAmBLZIGEG1PAsRVApK37gxv5257bcBCaFBMvBzoX +h78EtTODKqx1CTGd3DM909p2RlaACS6a/W/8nXcEv162OFBGHR+IEnkJpZpJyJ5h +FgIADD4m5ZxnK+VuZXirPEc+qfhzlKoTB1tkgVtIR6wOeAFvRLe+PaZmMyhd7PMa +9Wrrz3LwD9k9LXM6EoQCDZTwGFI24O1M/OCM7HuCkKtf40tIY+AxNSQc9Fqhnzj3 +6MZcUgmEvoxlRnOrEY9862btqP/wvaG/Vq1Ly6OYwMvgv+l8vfPvxfrD7Usv0bGF +eu2pBADGPDZZ+u70HptP/+umwAyKe9a18r2pNThCU+g3kUV8g5EK80J+6iAhNG6m +IViZrBkemenyUEvClPZ0jLfXwz/aYgwzzOiBkIq7RsBn32eZafmqifPpHoegIAC4 +2zZ93Z3ieQ2ynqp014ZWVqad29to1aDaMlNYUNuXI7b5iVwNrwQA8UDXSx/w7V6B +lRfDH0FP3gSJwKalq6jkY2cVF1Ae6xfj20n7AycsqYOb6Dbt8UVQM3g3GD3Bmmr1 +xm0+PPeSY8OEzAixyCG4P2pW9zlBggxR/gU7hPRI236xJZYjULeHPOfK/1Hqa19D +5mpT5qB1CdxVq75yoMOBSTBEG/tU+osD/23S9negJyuTTML1GPalgWQ26+D4n+fs +is8UxsyQlyu/ncVA9T9riaiGAE4j3LRMF0HMDiQleoNhAx9uv+GCx3Kf4X1ZetRe +Gkvn94dA5VNbbOXtvtZrk7vflAbt3I4Z702c12eyTwSxEvCSFAyT1+uf4iImee33 +mW/Rx/szXku6TGW0GlVzZXIgMyA8dXNlcjNAZXhhbXBsZS5jb20+iQFUBBMBCgA+ +FiEEsyqWn3YKjtDW1nzadq7TCKLpcAEFAlnoT18CGwMFCQPCZwAFCwkIBwMFFQoJ +CAsFFgIDAQACHgECF4AACgkQdq7TCKLpcAGjQQf/StaNTrH2IbNSPym4eUkr/p0e +8iED0w0+gn1Z5YN+FZ8l5vhzrVvqWXL5B9UpxfgKja3ipR89Z95h4IUPjW5xsMkc +wd8AZ46axHSyPov872/3xf9RJvEYzPXUUTaB23RVp+xxunZUp4pJSOlrMonVHcPr +B2ufB8VKYNaYow6oXbxC7iUzoWYsKN4Oqtve2BYf0JpWXLAo6pVR9X+Dz+r6gaAi +EFvxPdJIVsb9JeR2NLLnDXmK1tvTpgp9dKZnWFBxBF1QQWnPxBsMaXkaXkcmKpmi +Wg+kn5nmDVbOazZJAqyKkkwFVOL4tCIJxDENPTXXTTsu1KevRKRKfCeC18zrPp0D +mARZ6E9fAQgAzCn7g8lIRlJk5Lrc0sKEueQNGn3MNxpZ6Xz/wTLc7KfWeTEzO/mK +KhfuSGqUBoESYM56AtD6HaKNWIt9EPxWbI5twe1BuOt7Kjic8QNB0gcOxqba76jz +pUcVr/z8Y4jShd03ayZm9bTWOTssW76abibv91A3109HRN9QkRCjcEaLDLSTrdBv +Cg8bgQ1XS7hZSYEZLNoa8myplf5TasuynOtGjkU4NvO62S6FhHW4flWx/dn7wCWq +HnKwSjCu4tvDdYC4WXpIwkHxFM5xsS3J5RW31qzYpHOoLq/z+Nbiqs5hXHwpgdtO +tdJ4EF+EuEDQQZuEsEfIAh8FU+4pvyLxFwARAQABAAf8CC8KoO65kfANkUmYPm6D +qXvltpEiVXcd8YkMK29hga78nuEE/7tx+Vv6NjbndhdIaE7+fwxnajaw2cLFoZ+r +yzAuYB6liscNqdSZHeQWiFe2FOptTjwEgDSIPwSyDT9XyPvkE8x3RhQYnjSsA4Vx +ZUb0SYKSqnPIFI+rCBWVp5Np/TC27KP61y3DTfMB5a0PeOgP/DbGLqtHejZkXT63 +uv+FzidvPw8Zyeha6+FrUjFE5KxxJrvQ+utsqq4yMImArDkxkqkqN8EP1xH0o0PB +90Kv6qM+BUHiU3xXD1CDVYqVkfg1Lbe9Q7FhnQkTx/jxYZI8UncF4sGl2jNfRpFs +KQQA4jI+bGsJgwe/ik+OnuGWKCR5NcmBUR8P9vWnqnEfZQc7ep1ab514U+55SIoK +qB3OxGlwhNcRmokoHHzgh0PlSJ2xAY5DCcJQ4Ty6ZOoQJPiTl22Xklc7wHsZSv3u +dwPS3Cbket8GrPEmK2ldzRs5C06W52QKj4wyjm1LuGAMvl0EAOcQky6h5atJfNWI +laLPxw/q5hVzQSq+qGbcL23b2HM6Wg9oXe4RzGW/Ik0Izrw37jdayV82jc2isy5Y +xtxqd61yLzwFhCVH7fuovNxr3U4YMxPnwIbcrGXc15sUF2zp8DSQiNr7nXopX3q0 +lRQvhiCAdGZEmrzqhND1hb8LaC4DA/9RaD8W/TZli9zPlhIp/ZsmefRNlUdf0w3y +wPMYrupZSWI3a4kqmf3uZNFcbT6+OP7u/mQTAfbNWq+MjL7k0SimRyYXXgZzsY6X +A4MHFS/kHFM56yOPYLbF2xNXLCr1M50wbR1geSPmZh6hu8wPQqLPMx9/BBMqdy5B +Y7nvhzxRjDaYiQE8BBgBCgAmFiEEsyqWn3YKjtDW1nzadq7TCKLpcAEFAlnoT18C +GwwFCQPCZwAACgkQdq7TCKLpcAEb5Af9Hw+GoxL7gt6lZQP717cnwF8ENEQV+ucY +4hiM5zcZTH6ZDAui8u976T2y1tLjV5K7wxR13dIyKEyaUP2DNwXXCKuj1FE/qq6n +xElyfYb1QZbaC3pTRM9zz03azO5UKlXrubu7IT2mn7HXhhMxJSLXLylK2b5Z8Bjb +rCthXKEI+IettPZ4OA1+aQSTYt021fBNeiGK09Ck8OKzunxaRhsuxZ1PrqaRoYz0 +1rV0KYzUjAEP8UJ+oEfzy6jJviZcKua+ghTX6Ptvth6Ga6JwRBudLzw4zrZfq+D4 +KvfzvylbdbM2iBdJljBORA0PGgBm/MLzx/slcVZKEN6quSrJuuY51Q== +=MWu4 +-----END PGP PRIVATE KEY BLOCK----- diff --git a/test/fixtures/user3-public.key b/test/fixtures/user3-public.key new file mode 100644 index 00000000..1f8ee79b --- /dev/null +++ b/test/fixtures/user3-public.key @@ -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-----