Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZMS-150 #676

Merged
merged 1 commit into from Apr 29, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
209 changes: 105 additions & 104 deletions lib/api/submit.js
Expand Up @@ -12,8 +12,11 @@ const tools = require('../tools');
const Maildropper = require('../maildropper');
const roles = require('../roles');
const Transform = require('stream').Transform;
const { sessSchema, sessIPSchema, booleanSchema } = require('../schemas');
const { sessSchema, sessIPSchema, booleanSchema, metaDataSchema } = require('../schemas');
const { preprocessAttachments } = require('../data-url');
const { userId, mailboxId } = require('../schemas/request/general-schemas');
const { AddressOptionalName, AddressOptionalNameArray, Header, Attachment, ReferenceWithAttachments } = require('../schemas/request/messages-schemas');
const { successRes } = require('../schemas/response/general-schemas');

class StreamCollect extends Transform {
constructor() {
Expand Down Expand Up @@ -620,113 +623,111 @@ module.exports = (db, server, messageHandler, userHandler, settingsHandler) => {
const submitMessageWrapper = util.promisify(submitMessage);

server.post(
{ name: 'send', path: '/users/:user/submit' },
{
name: 'send',
path: '/users/:user/submit',
tags: ['Submission'],
summary: 'Submit a Message for Delivery',
description: 'Use this method to send emails from a user account',
validationObjs: {
requestBody: {
mailbox: mailboxId,
from: AddressOptionalName.description('Addres for the From: header'),
replyTo: AddressOptionalName.description('Address for the Reply-To: header'),
to: Joi.array()
.items(
Joi.object({
name: Joi.string().empty('').max(255).description('Name of the sender'),
address: Joi.string().email({ tlds: false }).failover('').required().description('Address of the sender')
}).$_setFlag('objectName', 'AddressOptionalName')
)
.description('Addresses for the To: header'),

cc: AddressOptionalNameArray.description('Addresses for the Cc: header'),

bcc: AddressOptionalNameArray.description('Addresses for the Bcc: header'),

headers: Joi.array()
.items(Header)
.description(
'Custom headers for the message. If reference message is set then In-Reply-To and References headers are set automatically'
),
subject: Joi.string()
.empty('')
.max(2 * 1024)
.description('Message subject. If not then resolved from Reference message'),
text: Joi.string()
.empty('')
.max(1024 * 1024)
.description('Plaintext message'),
html: Joi.string()
.empty('')
.max(1024 * 1024)
.description('HTML formatted message'),
attachments: Joi.array().items(Attachment).description('Attachments for the message'),

meta: metaDataSchema.label('metaData').description('Optional metadata, must be an object or JSON formatted string'),
sess: sessSchema,
ip: sessIPSchema,
reference: ReferenceWithAttachments.description(
'Optional referenced email. If uploaded message is a reply draft and relevant fields are not provided then these are resolved from the message to be replied to'
),
// if true then treat this message as a draft
isDraft: booleanSchema.default(false).description('Is the message a draft or not'),
// if set then this message is based on a draft that should be deleted after processing
draft: Joi.object()
.keys({
mailbox: mailboxId,
id: Joi.number().required().description('Message ID')
})
.description('Draft message to base this one on'),
sendTime: Joi.date().description('Send time'),
uploadOnly: booleanSchema.default(false).description('If true only uploads the message but does not send it'),
envelope: Joi.object()
.keys({
from: AddressOptionalName.description('Addres for the From: header'),
to: Joi.array().items(
Joi.object()
.keys({
name: Joi.string().empty('').max(255).description('Name of the sender'),
address: Joi.string().email({ tlds: false }).required().description('Address of the sender')
})
.description('Addresses for the To: header')
)
})
.description('Optional envelope')
},
queryParams: {},
pathParams: {
user: userId
},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
message: Joi.object({
mailbox: Joi.string().required().description('Mailbox ID the message was stored to'),
id: Joi.number().description('Message ID in the Mailbox').required(),
queueId: Joi.string().required().description('Queue ID in MTA')
})
.required()
.description('Information about submitted Message')
.$_setFlag('objectName', 'MessageWithQueueId')
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),

mailbox: Joi.string().hex().lowercase().length(24),

reference: Joi.object().keys({
mailbox: Joi.string().hex().lowercase().length(24).required(),
id: Joi.number().required(),
action: Joi.string().valid('reply', 'replyAll', 'forward').required()
}),

// if true then treat this message as a draft
isDraft: booleanSchema.default(false),

// if set then this message is based on a draft that should be deleted after processing
draft: Joi.object().keys({
mailbox: Joi.string().hex().lowercase().length(24).required(),
id: Joi.number().required()
}),

uploadOnly: booleanSchema.default(false),
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

sendTime: Joi.date(),

envelope: Joi.object().keys({
from: Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
}),
to: Joi.array().items(
Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
})
)
}),

from: Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
}),

replyTo: Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
}),

to: Joi.array().items(
Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
})
),

cc: Joi.array().items(
Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).required()
})
),

bcc: Joi.array().items(
Joi.object().keys({
name: Joi.string().empty('').max(255),
address: Joi.string().email({ tlds: false }).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(2 * 1024),
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),
contentTransferEncoding: Joi.string().empty('').trim().lowercase(),
contentDisposition: Joi.string().empty('').trim().lowercase().valid('inline', 'attachment'),
cid: Joi.string().empty('').max(255),

encoding: Joi.string().empty('').default('base64'),
content: Joi.string().required()
})
),
meta: Joi.object().unknown(true),
sess: sessSchema,
ip: sessIPSchema
const schema = Joi.object({
...pathParams,
...requestBody,
...queryParams
});

// extract embedded attachments from HTML
Expand Down