mirror of
https://github.com/nodemailer/wildduck.git
synced 2025-03-03 03:13:17 +08:00
allow uploading structured messages
This commit is contained in:
parent
e59f9e2776
commit
436a3aaa9b
6 changed files with 183 additions and 8 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1 +1 @@
|
|||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-10-12T08:13:42.322Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
define({
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-12T11:25:20.064Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
});
|
||||
|
|
|
@ -1 +1 @@
|
|||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-10-12T08:13:42.322Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
{
"name": "wildduck",
"version": "1.0.0",
"description": "WildDuck API docs",
"title": "WildDuck API",
"url": "https://api.wildduck.email",
"sampleUrl": false,
"defaultVersion": "0.0.0",
"apidoc": "0.3.0",
"generator": {
"name": "apidoc",
"time": "2018-11-12T11:25:20.064Z",
"url": "http://apidocjs.com",
"version": "0.17.6"
}
}
|
||||
|
|
|
@ -7,6 +7,8 @@ const log = require('npmlog');
|
|||
const Joi = require('../joi');
|
||||
const MongoPaging = require('mongo-cursor-pagination');
|
||||
const addressparser = require('nodemailer/lib/addressparser');
|
||||
const MailComposer = require('nodemailer/lib/mail-composer');
|
||||
const htmlToText = require('html-to-text');
|
||||
const ObjectID = require('mongodb').ObjectID;
|
||||
const tools = require('../tools');
|
||||
const consts = require('../consts');
|
||||
|
@ -1865,7 +1867,30 @@ module.exports = (db, server, messageHandler) => {
|
|||
* @apiParam {Boolean} [unseen=false] Is the message unseen or not
|
||||
* @apiParam {Boolean} [draft=false] Is the message a draft or not
|
||||
* @apiParam {Boolean} [flagged=false] Is the message flagged or not
|
||||
* @apiParam {String} raw base64 encoded message source. Alternatively, you can provide this value as POST body by using message/rfc822 MIME type
|
||||
* @apiParam {String} [raw] base64 encoded message source. Alternatively, you can provide this value as POST body by using message/rfc822 MIME type. If raw message is provided then it overrides any other mail configuration
|
||||
* @apiParam {Object} [from] Address for the From: header
|
||||
* @apiParam {String} from.name Name of the sender
|
||||
* @apiParam {String} from.address Address of the sender
|
||||
* @apiParam {Object[]} [to] Addresses for the To: header
|
||||
* @apiParam {String} [to.name] Name of the recipient
|
||||
* @apiParam {String} to.address Address of the recipient
|
||||
* @apiParam {Object[]} [cc] Addresses for the Cc: header
|
||||
* @apiParam {String} [cc.name] Name of the recipient
|
||||
* @apiParam {String} cc.address Address of the recipient
|
||||
* @apiParam {Object[]} [bcc] Addresses for the Bcc: header
|
||||
* @apiParam {String} [bcc.name] Name of the recipient
|
||||
* @apiParam {String} bcc.address Address of the recipient
|
||||
* @apiParam {String} subject Message subject. If not then resolved from Reference message
|
||||
* @apiParam {String} text Plaintext message
|
||||
* @apiParam {String} html HTML formatted message
|
||||
* @apiParam {Object[]} [headers] Custom headers for the message. If reference message is set then In-Reply-To and References headers are set automaticall y
|
||||
* @apiParam {String} headers.key Header key ('X-Mailer')
|
||||
* @apiParam {String} headers.value Header value ('My Awesome Mailing Service')
|
||||
* @apiParam {Object[]} [attachments] Attachments for the message
|
||||
* @apiParam {String} attachments.content Base64 encoded attachment content
|
||||
* @apiParam {String} [attachments.filename] Attachment filename
|
||||
* @apiParam {String} [attachments.contentType] MIME type for the attachment file
|
||||
* @apiParam {String} [attachments.cid] Content-ID value if you want to reference to this attachment from HTML formatted message
|
||||
* @apiParam {String} [sess] Session identifier for the logs
|
||||
* @apiParam {String} [ip] IP address for the logs
|
||||
*
|
||||
|
@ -1930,9 +1955,102 @@ module.exports = (db, server, messageHandler) => {
|
|||
.truthy(['Y', 'true', 'yes', 'on', '1', 1])
|
||||
.falsy(['N', 'false', 'no', 'off', '0', 0, ''])
|
||||
.default(false),
|
||||
|
||||
raw: Joi.binary()
|
||||
.max(consts.MAX_ALLOWE_MESSAGE_SIZE)
|
||||
.required(),
|
||||
.empty(''),
|
||||
|
||||
time: Joi.date(),
|
||||
|
||||
from: Joi.object().keys({
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
}),
|
||||
|
||||
replyTo: Joi.object().keys({
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
}),
|
||||
|
||||
to: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
})
|
||||
),
|
||||
|
||||
cc: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
})
|
||||
),
|
||||
|
||||
bcc: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
name: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
address: Joi.string()
|
||||
.email()
|
||||
.required()
|
||||
})
|
||||
),
|
||||
|
||||
headers: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
key: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
value: Joi.string()
|
||||
.empty('')
|
||||
.max(100 * 1024)
|
||||
})
|
||||
),
|
||||
|
||||
subject: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
text: Joi.string()
|
||||
.empty('')
|
||||
.max(1024 * 1024),
|
||||
html: Joi.string()
|
||||
.empty('')
|
||||
.max(1024 * 1024),
|
||||
|
||||
attachments: Joi.array().items(
|
||||
Joi.object().keys({
|
||||
filename: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
contentType: Joi.string()
|
||||
.empty('')
|
||||
.max(255),
|
||||
encoding: Joi.string()
|
||||
.empty('')
|
||||
.default('base64'),
|
||||
content: Joi.string().required(),
|
||||
cid: Joi.string()
|
||||
.empty('')
|
||||
.max(255)
|
||||
})
|
||||
),
|
||||
sess: Joi.string().max(255),
|
||||
ip: Joi.string().ip({
|
||||
version: ['ipv4', 'ipv6'],
|
||||
|
@ -1945,8 +2063,8 @@ module.exports = (db, server, messageHandler) => {
|
|||
req.params[key] = req.query[key];
|
||||
}
|
||||
});
|
||||
req.params.raw = req.params.raw || req.body;
|
||||
|
||||
req.params.raw = req.params.raw || req.body;
|
||||
const result = Joi.validate(req.params, schema, {
|
||||
abortEarly: false,
|
||||
convert: true
|
||||
|
@ -2022,6 +2140,42 @@ module.exports = (db, server, messageHandler) => {
|
|||
return next();
|
||||
}
|
||||
|
||||
if (!req.params.raw) {
|
||||
let data = {
|
||||
from: result.from,
|
||||
date,
|
||||
to: result.to || [],
|
||||
cc: result.cc || [],
|
||||
bcc: result.bcc || [],
|
||||
subject: result.subject,
|
||||
text: result.text || '',
|
||||
html: result.html || '',
|
||||
headers: result.headers || [],
|
||||
attachments: result.attachments || [],
|
||||
disableFileAccess: true,
|
||||
disableUrlAccess: true
|
||||
};
|
||||
// ensure plaintext content if html is provided
|
||||
if (data.html && !data.text) {
|
||||
try {
|
||||
// might explode on long or strange strings
|
||||
data.text = htmlToText.fromString(data.html);
|
||||
} catch (E) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
raw = await getCompiledMessage(data);
|
||||
}
|
||||
|
||||
if (!raw || !raw.length) {
|
||||
res.json({
|
||||
error: 'Empty message provided',
|
||||
code: 'EmptyMessage'
|
||||
});
|
||||
return next();
|
||||
}
|
||||
|
||||
if (userData.encryptMessages) {
|
||||
try {
|
||||
let encrypted = await encryptMessage(userData.pubKey, raw);
|
||||
|
@ -2925,3 +3079,22 @@ function formatMessageListing(messageData) {
|
|||
|
||||
return response;
|
||||
}
|
||||
|
||||
async function getCompiledMessage(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let compiler = new MailComposer(data);
|
||||
let compiled = compiler.compile();
|
||||
let stream = compiled.createReadStream();
|
||||
let chunks = [];
|
||||
let chunklen;
|
||||
stream.once('error', err => reject(err));
|
||||
stream.once('readable', () => {
|
||||
let chunk;
|
||||
while ((chunk = stream.read()) !== null) {
|
||||
chunks.push(chunk);
|
||||
chunklen += chunk.length;
|
||||
}
|
||||
});
|
||||
stream.once('end', () => resolve(Buffer.concat(chunks, chunklen)));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -321,7 +321,9 @@ module.exports = (db, server, messageHandler, userHandler) => {
|
|||
text: options.text || '',
|
||||
html: options.html || '',
|
||||
headers: extraHeaders.concat(options.headers || []),
|
||||
attachments: options.attachments || []
|
||||
attachments: options.attachments || [],
|
||||
disableFileAccess: true,
|
||||
disableUrlAccess: true
|
||||
};
|
||||
|
||||
// ensure plaintext content if html is provided
|
||||
|
|
Loading…
Reference in a new issue