mail-mime-builder
Version:
RFC-2822, RFC-2045 and RFC-2049 compliant zero dependency raw email message generator.
187 lines (186 loc) • 6.86 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MIMEMessageContentHeader = exports.MIMEMessageHeader = void 0;
const base64_js_1 = require("./base64.js");
const MIMETextError_js_1 = require("./MIMETextError.js");
const Mailbox_js_1 = require("./Mailbox.js");
/*
Headers are based on: https://www.rfc-editor.org/rfc/rfc4021#section-2.1
(Some are ignored as they can be added later or as a custom header.)
*/
class MIMEMessageHeader {
fields = [
{
name: 'Date',
generator: () => ((new Date()).toUTCString()).replace(/GMT|UTC/gi, '+0000')
},
{
name: 'From',
required: true,
validate: (v) => this.validateMailboxSingle(v),
dump: (v) => this.dumpMailboxSingle(v)
},
{
name: 'Sender',
validate: (v) => this.validateMailboxSingle(v),
dump: (v) => this.dumpMailboxSingle(v)
},
{
name: 'Reply-To',
validate: (v) => this.validateMailboxMulti(v),
dump: (v) => this.dumpMailboxMulti(v)
},
{
name: 'To',
validate: (v) => this.validateMailboxMulti(v),
dump: (v) => this.dumpMailboxMulti(v)
},
{
name: 'Cc',
validate: (v) => this.validateMailboxMulti(v),
dump: (v) => this.dumpMailboxMulti(v)
},
{
name: 'Bcc',
validate: (v) => this.validateMailboxMulti(v),
dump: (v) => this.dumpMailboxMulti(v)
},
{
name: 'Message-ID',
generator: () => {
const randomstr = Math.random().toString(36).slice(2);
const from = this.fields.filter((obj) => obj.name === 'From')[0].value;
const domain = from.getAddrDomain();
return '<' + randomstr + '@' + domain + '>';
}
},
{
name: 'Subject',
required: true,
dump: (v) => {
return typeof v === 'string' ? '=?utf-8?B?' + (0, base64_js_1.toBase64)(v) + '?=' : '';
}
},
{
name: 'MIME-Version',
generator: () => '1.0'
}
];
dump() {
let lines = '';
for (const field of this.fields) {
if (field.disabled)
continue;
const isValueDefinedByUser = field.value !== undefined && field.value !== null;
if (!isValueDefinedByUser && field.required) {
throw new MIMETextError_js_1.MIMETextError('MIMETEXT_MISSING_HEADER', `The "${field.name}" header is required.`);
}
if (!isValueDefinedByUser && typeof field.generator !== 'function')
continue;
if (!isValueDefinedByUser && typeof field.generator === 'function')
field.value = field.generator();
const strval = Object.hasOwn(field, 'dump') && typeof field.dump === 'function'
? field.dump(field.value)
: typeof field.value === 'string' ? field.value : '';
lines += `${field.name}: ${strval}\r\n`;
}
return lines.slice(0, -2);
}
toObject() {
return this.fields.reduce((memo, item) => {
memo[item.name] = item.value;
return memo;
}, {});
}
get(name) {
const fieldMatcher = (obj) => obj.name.toLowerCase() === name.toLowerCase();
const ind = this.fields.findIndex(fieldMatcher);
return ind !== -1 ? this.fields[ind].value : undefined;
}
set(name, value) {
const fieldMatcher = (obj) => obj.name.toLowerCase() === name.toLowerCase();
const isCustomHeader = !this.fields.some(fieldMatcher);
if (!isCustomHeader) {
const ind = this.fields.findIndex(fieldMatcher);
const field = this.fields[ind];
if (field.validate && !field.validate(value)) {
throw new MIMETextError_js_1.MIMETextError('MIMETEXT_INVALID_HEADER_VALUE', 'You specified an invalid value for the header ' + name);
}
this.fields[ind].value = value;
return this.fields[ind];
}
return this.setCustom({
name: name,
value: value,
custom: true,
dump: (v) => typeof v === 'string' ? v : ''
});
}
setCustom(obj) {
if (this.isHeaderField(obj)) {
if (typeof obj.value !== 'string') {
throw new MIMETextError_js_1.MIMETextError('MIMETEXT_INVALID_HEADER_FIELD', 'Custom header must have a value.');
}
this.fields.push(obj);
return obj;
}
throw new MIMETextError_js_1.MIMETextError('MIMETEXT_INVALID_HEADER_FIELD', 'You specified an invalid header field object.');
}
validateMailboxSingle(v) {
return v instanceof Mailbox_js_1.Mailbox;
}
validateMailboxMulti(v) {
return v instanceof Mailbox_js_1.Mailbox || this.isArrayOfMailboxes(v);
}
dumpMailboxMulti(v) {
const dump = (item) => item.name.length === 0
? item.dump()
: `=?utf-8?B?${(0, base64_js_1.toBase64)(item.name)}?= <${item.addr}>`;
return this.isArrayOfMailboxes(v) ? v.map(dump).join(`,\r\n `) : v instanceof Mailbox_js_1.Mailbox ? dump(v) : '';
}
dumpMailboxSingle(v) {
const dump = (item) => item.name.length === 0
? item.dump()
: `=?utf-8?B?${(0, base64_js_1.toBase64)(item.name)}?= <${item.addr}>`;
return v instanceof Mailbox_js_1.Mailbox ? dump(v) : '';
}
isHeaderField(v) {
const validProps = ['name', 'value', 'dump', 'required', 'disabled', 'generator', 'custom'];
if (this.isObject(v)) {
const h = v;
if (Object.hasOwn(h, 'name') && typeof h.name === 'string' && h.name.length > 0) {
if (!Object.keys(h).some((prop) => !validProps.includes(prop))) {
return true;
}
}
}
return false;
}
isObject(v) {
return (!!v) && (v.constructor === Object);
}
isArrayOfMailboxes(v) {
return this.isArray(v) && v.every((item) => item instanceof Mailbox_js_1.Mailbox);
}
isArray(v) {
return (!!v) && (v.constructor === Array);
}
}
exports.MIMEMessageHeader = MIMEMessageHeader;
class MIMEMessageContentHeader extends MIMEMessageHeader {
fields = [
{
name: 'Content-ID'
},
{
name: 'Content-Type'
},
{
name: 'Content-Transfer-Encoding'
},
{
name: 'Content-Disposition'
}
];
}
exports.MIMEMessageContentHeader = MIMEMessageContentHeader;