@timshel_npm/maildev 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +16 -0
- package/README.md +287 -0
- package/bin/maildev +13 -0
- package/dist/app/components/angular/angular.js +20282 -0
- package/dist/app/components/angular/angular.min.js +201 -0
- package/dist/app/components/angular/angular.min.js.map +8 -0
- package/dist/app/components/angular-cookies/angular-cookies.js +202 -0
- package/dist/app/components/angular-cookies/angular-cookies.min.js +8 -0
- package/dist/app/components/angular-cookies/angular-cookies.min.js.map +8 -0
- package/dist/app/components/angular-resource/angular-resource.js +546 -0
- package/dist/app/components/angular-resource/angular-resource.min.js +12 -0
- package/dist/app/components/angular-resource/angular-resource.min.js.map +8 -0
- package/dist/app/components/angular-route/angular-route.js +891 -0
- package/dist/app/components/angular-route/angular-route.min.js +14 -0
- package/dist/app/components/angular-route/angular-route.min.js.map +8 -0
- package/dist/app/components/angular-sanitize/angular-sanitize.js +615 -0
- package/dist/app/components/angular-sanitize/angular-sanitize.min.js +14 -0
- package/dist/app/components/angular-sanitize/angular-sanitize.min.js.map +8 -0
- package/dist/app/components/socket.io/socket.io.min.js +7 -0
- package/dist/app/favicon.ico +0 -0
- package/dist/app/index.html +176 -0
- package/dist/app/scripts/app.js +68 -0
- package/dist/app/scripts/components/address.js +15 -0
- package/dist/app/scripts/controllers/item.js +191 -0
- package/dist/app/scripts/controllers/main.js +259 -0
- package/dist/app/scripts/services.js +84 -0
- package/dist/app/styles/style.css +37 -0
- package/dist/app/views/address.html +3 -0
- package/dist/app/views/item.html +310 -0
- package/dist/app/views/main.html +18 -0
- package/dist/app/webfonts/fa-brands-400.eot +0 -0
- package/dist/app/webfonts/fa-brands-400.svg +3717 -0
- package/dist/app/webfonts/fa-brands-400.ttf +0 -0
- package/dist/app/webfonts/fa-brands-400.woff +0 -0
- package/dist/app/webfonts/fa-brands-400.woff2 +0 -0
- package/dist/app/webfonts/fa-regular-400.eot +0 -0
- package/dist/app/webfonts/fa-regular-400.svg +801 -0
- package/dist/app/webfonts/fa-regular-400.ttf +0 -0
- package/dist/app/webfonts/fa-regular-400.woff +0 -0
- package/dist/app/webfonts/fa-regular-400.woff2 +0 -0
- package/dist/app/webfonts/fa-solid-900.eot +0 -0
- package/dist/app/webfonts/fa-solid-900.svg +5028 -0
- package/dist/app/webfonts/fa-solid-900.ttf +0 -0
- package/dist/app/webfonts/fa-solid-900.woff +0 -0
- package/dist/app/webfonts/fa-solid-900.woff2 +0 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +50 -0
- package/dist/lib/auth.d.ts +4 -0
- package/dist/lib/auth.js +26 -0
- package/dist/lib/helpers/bcc.d.ts +5 -0
- package/dist/lib/helpers/bcc.js +16 -0
- package/dist/lib/helpers/smtp.d.ts +4 -0
- package/dist/lib/helpers/smtp.js +16 -0
- package/dist/lib/helpers/strtotime.d.ts +2 -0
- package/dist/lib/helpers/strtotime.js +1301 -0
- package/dist/lib/logger.d.ts +2 -0
- package/dist/lib/logger.js +30 -0
- package/dist/lib/mailbuffer.d.ts +21 -0
- package/dist/lib/mailbuffer.js +102 -0
- package/dist/lib/mailparser.d.ts +2 -0
- package/dist/lib/mailparser.js +253 -0
- package/dist/lib/mailserver.d.ts +115 -0
- package/dist/lib/mailserver.js +497 -0
- package/dist/lib/options.d.ts +3 -0
- package/dist/lib/options.js +150 -0
- package/dist/lib/outgoing.d.ts +40 -0
- package/dist/lib/outgoing.js +162 -0
- package/dist/lib/routes.d.ts +2 -0
- package/dist/lib/routes.js +139 -0
- package/dist/lib/type.d.ts +228 -0
- package/dist/lib/type.js +2 -0
- package/dist/lib/utils.d.ts +4 -0
- package/dist/lib/utils.js +65 -0
- package/dist/lib/web.d.ts +32 -0
- package/dist/lib/web.js +90 -0
- package/package.json +109 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface OutgoingOptions {
|
|
2
|
+
host?: string;
|
|
3
|
+
port?: number;
|
|
4
|
+
secure?: boolean;
|
|
5
|
+
auth?: {
|
|
6
|
+
user: string;
|
|
7
|
+
pass: string;
|
|
8
|
+
};
|
|
9
|
+
autoRelayAddress?: string;
|
|
10
|
+
autoRelayRules?: string | {
|
|
11
|
+
allow?: string;
|
|
12
|
+
deny?: string;
|
|
13
|
+
}[];
|
|
14
|
+
}
|
|
15
|
+
export declare class Outgoing {
|
|
16
|
+
port: number;
|
|
17
|
+
host: string;
|
|
18
|
+
secure: boolean;
|
|
19
|
+
autoRelay: boolean;
|
|
20
|
+
autoRelayAddress: string | undefined;
|
|
21
|
+
autoRelayRules: {
|
|
22
|
+
allow?: string;
|
|
23
|
+
deny?: string;
|
|
24
|
+
}[] | undefined;
|
|
25
|
+
auth: {
|
|
26
|
+
user: string;
|
|
27
|
+
pass: string;
|
|
28
|
+
} | undefined;
|
|
29
|
+
client: any;
|
|
30
|
+
emailQueue: any;
|
|
31
|
+
constructor(options?: OutgoingOptions);
|
|
32
|
+
getOutgoingHost(): string;
|
|
33
|
+
close(): void;
|
|
34
|
+
setAutoRelayMode(enabled: boolean, emailAddress: string | undefined, rules: {
|
|
35
|
+
allow?: string;
|
|
36
|
+
deny?: string;
|
|
37
|
+
}[] | string | undefined): void;
|
|
38
|
+
isAutoRelayEnabled(): boolean;
|
|
39
|
+
relayMail(emailObject: any, emailStream: any, isAutoRelay: any, callback: any): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Outgoing = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* MailDev - outgoing
|
|
6
|
+
*/
|
|
7
|
+
const SMTPConnection = require("nodemailer/lib/smtp-connection");
|
|
8
|
+
const async = require("async");
|
|
9
|
+
const fs = require("fs");
|
|
10
|
+
const logger = require("./logger");
|
|
11
|
+
const wildstring = require("wildstring");
|
|
12
|
+
class Outgoing {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
this.autoRelay = false;
|
|
16
|
+
wildstring.caseSensitive = false;
|
|
17
|
+
this.secure = (_a = options === null || options === void 0 ? void 0 : options.secure) !== null && _a !== void 0 ? _a : false;
|
|
18
|
+
this.host = (_b = options === null || options === void 0 ? void 0 : options.host) !== null && _b !== void 0 ? _b : "localhost";
|
|
19
|
+
this.port = (_c = options === null || options === void 0 ? void 0 : options.port) !== null && _c !== void 0 ? _c : (this.secure ? 465 : 25);
|
|
20
|
+
this.auth = options === null || options === void 0 ? void 0 : options.auth;
|
|
21
|
+
createClient(this);
|
|
22
|
+
// Create a queue to sent out the emails
|
|
23
|
+
// We use a queue so we don't run into concurrency issues
|
|
24
|
+
this.emailQueue = async.queue((task, callback) => {
|
|
25
|
+
const relayCallback = function (err, result) {
|
|
26
|
+
task.callback && task.callback(err, result);
|
|
27
|
+
callback(err, result);
|
|
28
|
+
};
|
|
29
|
+
relayMail(this, task.emailObject, task.emailStream, task.isAutoRelay, relayCallback);
|
|
30
|
+
}, 1);
|
|
31
|
+
if ((options === null || options === void 0 ? void 0 : options.autoRelayAddress) || (options === null || options === void 0 ? void 0 : options.autoRelayRules)) {
|
|
32
|
+
this.setAutoRelayMode(true, options === null || options === void 0 ? void 0 : options.autoRelayAddress, options === null || options === void 0 ? void 0 : options.autoRelayRules);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
getOutgoingHost() {
|
|
36
|
+
return this.host;
|
|
37
|
+
}
|
|
38
|
+
close() {
|
|
39
|
+
if (this.client) {
|
|
40
|
+
this.client.close();
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
setAutoRelayMode(enabled, emailAddress, rules) {
|
|
44
|
+
this.autoRelay = enabled;
|
|
45
|
+
this.autoRelayAddress = emailAddress;
|
|
46
|
+
if (rules) {
|
|
47
|
+
if (typeof rules === "string") {
|
|
48
|
+
try {
|
|
49
|
+
rules = JSON.parse(fs.readFileSync(rules, "utf8"));
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
logger.error(`Error reading rules file at ${rules}: ${err}`);
|
|
53
|
+
throw err;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (Array.isArray(rules)) {
|
|
57
|
+
this.autoRelayRules = rules;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (this.autoRelay) {
|
|
61
|
+
const msg = ["Auto-Relay mode on"];
|
|
62
|
+
if (this.autoRelayAddress) {
|
|
63
|
+
msg.push(`Relaying all emails to ${this.autoRelayAddress}`);
|
|
64
|
+
}
|
|
65
|
+
if (this.autoRelayRules) {
|
|
66
|
+
msg.push(`Relay rules: ${JSON.stringify(this.autoRelayRules)}`);
|
|
67
|
+
}
|
|
68
|
+
logger.info(msg.join(", "));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
isAutoRelayEnabled() {
|
|
72
|
+
return this.autoRelay;
|
|
73
|
+
}
|
|
74
|
+
relayMail(emailObject, emailStream, isAutoRelay, callback) {
|
|
75
|
+
this.emailQueue.push({ emailObject, emailStream, isAutoRelay, callback });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.Outgoing = Outgoing;
|
|
79
|
+
function createClient(outgoing) {
|
|
80
|
+
var _a, _b;
|
|
81
|
+
try {
|
|
82
|
+
outgoing.client = new SMTPConnection({
|
|
83
|
+
port: outgoing.port,
|
|
84
|
+
host: outgoing.host,
|
|
85
|
+
secure: outgoing.secure,
|
|
86
|
+
auth: outgoing.auth || false,
|
|
87
|
+
tls: { rejectUnauthorized: false },
|
|
88
|
+
debug: true,
|
|
89
|
+
});
|
|
90
|
+
outgoing.client.on("error", function (err) {
|
|
91
|
+
logger.error("SMTP Connection error for outgoing email: ", err);
|
|
92
|
+
});
|
|
93
|
+
logger.info("MailDev outgoing SMTP Server %s:%d (user:%s, pass:%s, secure:%s)", outgoing.host, outgoing.port, (_a = outgoing === null || outgoing === void 0 ? void 0 : outgoing.auth) === null || _a === void 0 ? void 0 : _a.user, ((_b = outgoing === null || outgoing === void 0 ? void 0 : outgoing.auth) === null || _b === void 0 ? void 0 : _b.pass) ? "####" : undefined, outgoing.secure ? "yes" : "no");
|
|
94
|
+
}
|
|
95
|
+
catch (err) {
|
|
96
|
+
logger.error("Error during configuration of SMTP Server for outgoing email", err);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function relayMail(outgoing, emailObject, emailStream, isAutoRelay, done) {
|
|
100
|
+
if (isAutoRelay && outgoing.autoRelayAddress) {
|
|
101
|
+
emailObject.to = [{ address: outgoing.autoRelayAddress }];
|
|
102
|
+
emailObject.envelope.to = [{ address: outgoing.autoRelayAddress, args: false }];
|
|
103
|
+
}
|
|
104
|
+
let recipients = emailObject.envelope.to.map(getAddressFromAddressObject);
|
|
105
|
+
if (isAutoRelay && outgoing.autoRelayRules) {
|
|
106
|
+
recipients = getAutoRelayableRecipients(recipients, outgoing.autoRelayRules);
|
|
107
|
+
}
|
|
108
|
+
// Fail silently with auth relay mode on
|
|
109
|
+
if (recipients.length === 0) {
|
|
110
|
+
return done("Email had no recipients");
|
|
111
|
+
}
|
|
112
|
+
const mailSendCallback = function (err) {
|
|
113
|
+
if (err) {
|
|
114
|
+
logger.error("Outgoing client login error: ", err);
|
|
115
|
+
return done(err);
|
|
116
|
+
}
|
|
117
|
+
const sender = getAddressFromAddressObject(emailObject.envelope.from);
|
|
118
|
+
outgoing.client.send({
|
|
119
|
+
from: sender,
|
|
120
|
+
to: recipients,
|
|
121
|
+
}, emailStream, function (err) {
|
|
122
|
+
outgoing.client.quit();
|
|
123
|
+
createClient(outgoing);
|
|
124
|
+
if (err) {
|
|
125
|
+
logger.error("Mail Delivery Error: ", err);
|
|
126
|
+
return done(err);
|
|
127
|
+
}
|
|
128
|
+
logger.log("Mail Delivered: ", emailObject.subject);
|
|
129
|
+
return done();
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
const mailConnectCallback = function (err) {
|
|
133
|
+
if (err) {
|
|
134
|
+
logger.error("Outgoing connection error: ", err);
|
|
135
|
+
return done(err);
|
|
136
|
+
}
|
|
137
|
+
if (outgoing.auth) {
|
|
138
|
+
outgoing.client.login(outgoing.auth, mailSendCallback);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
mailSendCallback(err);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
outgoing.client.connect(mailConnectCallback);
|
|
145
|
+
}
|
|
146
|
+
// Fallback to the object if the address key isn't defined
|
|
147
|
+
function getAddressFromAddressObject(addressObj) {
|
|
148
|
+
return typeof addressObj.address !== "undefined" ? addressObj.address : addressObj;
|
|
149
|
+
}
|
|
150
|
+
function getAutoRelayableRecipients(recipients, rules) {
|
|
151
|
+
return recipients.filter(function (email) {
|
|
152
|
+
return validateAutoRelayRules(email, rules);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
function validateAutoRelayRules(email, rules) {
|
|
156
|
+
return rules.reduce(function (result, rule) {
|
|
157
|
+
const toMatch = rule.allow || rule.deny || "";
|
|
158
|
+
const isMatch = wildstring.match(toMatch, email);
|
|
159
|
+
// Override previous rule if it matches
|
|
160
|
+
return isMatch ? !!rule.allow : result;
|
|
161
|
+
}, true);
|
|
162
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.routes = routes;
|
|
4
|
+
/**
|
|
5
|
+
* MailDev - routes
|
|
6
|
+
*/
|
|
7
|
+
const express = require("express");
|
|
8
|
+
const compression = require("compression");
|
|
9
|
+
const pkg = require("../../package.json");
|
|
10
|
+
const { filterEmails } = require("./utils");
|
|
11
|
+
const emailRegexp = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
12
|
+
function routes(app, mailserver, basePathname) {
|
|
13
|
+
const router = express.Router();
|
|
14
|
+
// Get all emails Enveloppe
|
|
15
|
+
router.get("/envelope", compression(), function (req, res) {
|
|
16
|
+
res.json(mailserver.getAllEnvelope());
|
|
17
|
+
});
|
|
18
|
+
// Get all emails
|
|
19
|
+
router.get("/email", compression(), function (req, res) {
|
|
20
|
+
mailserver
|
|
21
|
+
.getAllEmail()
|
|
22
|
+
.then((mails) => res.json(req.query ? filterEmails(mails, req.query) : mails))
|
|
23
|
+
.catch((err) => res.status(500).json({ error: err.message }));
|
|
24
|
+
});
|
|
25
|
+
// Get single email
|
|
26
|
+
router.get("/email/:id", function (req, res) {
|
|
27
|
+
mailserver
|
|
28
|
+
.getEmail(req.params.id)
|
|
29
|
+
.then((mail) => {
|
|
30
|
+
mail.envelope.isRead = true; // Mark the email as 'read'
|
|
31
|
+
res.json(mail);
|
|
32
|
+
})
|
|
33
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
34
|
+
});
|
|
35
|
+
// Read email
|
|
36
|
+
// router.patch('/email/:id/read', function (req, res) {
|
|
37
|
+
// mailserver.readEmail(req.params.id, function (err, email) {
|
|
38
|
+
// if (err) return res.status(500).json({ error: err.message })
|
|
39
|
+
// res.json(true)
|
|
40
|
+
// })
|
|
41
|
+
// })
|
|
42
|
+
// Read all emails
|
|
43
|
+
router.patch("/email/read-all", function (req, res) {
|
|
44
|
+
const count = mailserver.readAllEmail();
|
|
45
|
+
res.json(count);
|
|
46
|
+
});
|
|
47
|
+
// Delete all emails
|
|
48
|
+
router.delete("/email/all", function (req, res) {
|
|
49
|
+
mailserver
|
|
50
|
+
.deleteAllEmail()
|
|
51
|
+
.then((count) => res.json(count))
|
|
52
|
+
.catch((err) => res.status(500).json({ error: err.message }));
|
|
53
|
+
});
|
|
54
|
+
// Delete email by id
|
|
55
|
+
router.delete("/email/:id", function (req, res) {
|
|
56
|
+
mailserver
|
|
57
|
+
.deleteEmail(req.params.id)
|
|
58
|
+
.then((deleted) => res.json(deleted))
|
|
59
|
+
.catch((err) => res.status(500).json({ error: err.message }));
|
|
60
|
+
});
|
|
61
|
+
// Get Email HTML
|
|
62
|
+
router.get("/email/:id/html", function (req, res) {
|
|
63
|
+
// Use the headers over hostname to include any port
|
|
64
|
+
const baseUrl = req.headers.host + (req.baseUrl || "");
|
|
65
|
+
mailserver
|
|
66
|
+
.getEmailHTML(req.params.id, baseUrl)
|
|
67
|
+
.then((html) => res.send(html))
|
|
68
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
69
|
+
});
|
|
70
|
+
// Serve Attachments
|
|
71
|
+
router.get("/email/:id/attachment/:filename", function (req, res) {
|
|
72
|
+
mailserver
|
|
73
|
+
.getEmailAttachment(req.params.id, req.params.filename)
|
|
74
|
+
.then((attachement) => {
|
|
75
|
+
res.contentType(attachement.contentType);
|
|
76
|
+
res.send(attachement.content);
|
|
77
|
+
})
|
|
78
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
79
|
+
});
|
|
80
|
+
// Serve email.eml
|
|
81
|
+
router.get("/email/:id/download", function (req, res) {
|
|
82
|
+
mailserver
|
|
83
|
+
.getEmailEml(req.params.id)
|
|
84
|
+
.then(([contentType, filename, stream]) => {
|
|
85
|
+
res.setHeader("Content-disposition", "attachment; filename=" + filename);
|
|
86
|
+
res.contentType(contentType);
|
|
87
|
+
stream.pipe(res);
|
|
88
|
+
})
|
|
89
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
90
|
+
});
|
|
91
|
+
// Get email source from .eml file
|
|
92
|
+
router.get("/email/:id/source", function (req, res) {
|
|
93
|
+
mailserver
|
|
94
|
+
.getRawEmail(req.params.id)
|
|
95
|
+
.then((stream) => stream.pipe(res))
|
|
96
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
97
|
+
});
|
|
98
|
+
// Get any config settings for display
|
|
99
|
+
router.get("/config", function (req, res) {
|
|
100
|
+
res.json({
|
|
101
|
+
version: pkg.version,
|
|
102
|
+
smtpPort: mailserver.port,
|
|
103
|
+
isOutgoingEnabled: mailserver.isOutgoingEnabled(),
|
|
104
|
+
outgoingHost: mailserver.getOutgoingHost(),
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
// Relay the email
|
|
108
|
+
router.post("/email/:id/relay/:relayTo?", function (req, res) {
|
|
109
|
+
mailserver
|
|
110
|
+
.getEmail(req.params.id)
|
|
111
|
+
.then((mail) => {
|
|
112
|
+
if (req.params.relayTo) {
|
|
113
|
+
if (emailRegexp.test(req.params.relayTo)) {
|
|
114
|
+
mail.to = [{ address: req.params.relayTo, name: "" }];
|
|
115
|
+
mail.envelope.to = [{ address: req.params.relayTo, name: "" }];
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
return res.status(400).json({
|
|
119
|
+
error: "Incorrect email address provided :" + req.params.relayTo,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return mailserver
|
|
124
|
+
.relayMail(mail, false)
|
|
125
|
+
.then(() => res.json(true))
|
|
126
|
+
.catch((err) => res.status(500).json({ error: err.message }));
|
|
127
|
+
})
|
|
128
|
+
.catch((err) => res.status(404).json({ error: err.message }));
|
|
129
|
+
});
|
|
130
|
+
// Health check
|
|
131
|
+
router.get("/healthz", function (req, res) {
|
|
132
|
+
res.json(true);
|
|
133
|
+
});
|
|
134
|
+
router.get("/reloadMailsFromDirectory", function (req, res) {
|
|
135
|
+
mailserver.loadMailsFromDirectory();
|
|
136
|
+
res.json(true);
|
|
137
|
+
});
|
|
138
|
+
app.use(basePathname, router);
|
|
139
|
+
}
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type * as MailParser from "mailparser";
|
|
2
|
+
/**
|
|
3
|
+
* Structured object for headers with arguments.
|
|
4
|
+
*
|
|
5
|
+
* `content-type: text/plain; CHARSET="UTF-8"` =>
|
|
6
|
+
* ```
|
|
7
|
+
* {
|
|
8
|
+
* "value": "text/plain",
|
|
9
|
+
* "params": {
|
|
10
|
+
* "charset": "UTF-8"
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export interface StructuredHeader extends MailParser.StructuredHeader {
|
|
16
|
+
/**
|
|
17
|
+
* The main value.
|
|
18
|
+
*/
|
|
19
|
+
value: string;
|
|
20
|
+
/**
|
|
21
|
+
* Additional arguments.
|
|
22
|
+
*/
|
|
23
|
+
params: {
|
|
24
|
+
[key: string]: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Possible types of a header value.
|
|
29
|
+
*/
|
|
30
|
+
export type HeaderValue = string[];
|
|
31
|
+
/**
|
|
32
|
+
* A Map object with lowercase header keys.
|
|
33
|
+
*/
|
|
34
|
+
export type HeadersMap = Map<string, HeaderValue>;
|
|
35
|
+
/**
|
|
36
|
+
* Address object.
|
|
37
|
+
*/
|
|
38
|
+
export interface AddressObject {
|
|
39
|
+
/**
|
|
40
|
+
* An array with address details.
|
|
41
|
+
*/
|
|
42
|
+
value: EmailAddress[];
|
|
43
|
+
/**
|
|
44
|
+
* A formatted address string for HTML context.
|
|
45
|
+
*/
|
|
46
|
+
html: string;
|
|
47
|
+
/**
|
|
48
|
+
* A formatted address string for plaintext context.
|
|
49
|
+
*/
|
|
50
|
+
text: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Address details.
|
|
54
|
+
*/
|
|
55
|
+
export interface EmailAddress {
|
|
56
|
+
/**
|
|
57
|
+
* The email address.
|
|
58
|
+
*/
|
|
59
|
+
address: string;
|
|
60
|
+
/**
|
|
61
|
+
* The name part of the email/group.
|
|
62
|
+
*/
|
|
63
|
+
name: string;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A Map object with lowercase header keys.
|
|
67
|
+
*/
|
|
68
|
+
export type Headers = {
|
|
69
|
+
date: Date | undefined;
|
|
70
|
+
contentType: StructuredHeader | undefined;
|
|
71
|
+
contentDisposition: StructuredHeader | undefined;
|
|
72
|
+
dkimSignature: StructuredHeader[];
|
|
73
|
+
from: AddressObject | undefined;
|
|
74
|
+
to: AddressObject | undefined;
|
|
75
|
+
cc: AddressObject | undefined;
|
|
76
|
+
bcc: AddressObject | undefined;
|
|
77
|
+
sender: AddressObject | undefined;
|
|
78
|
+
replyTo: AddressObject | undefined;
|
|
79
|
+
deliveredTo: AddressObject | undefined;
|
|
80
|
+
dispositionNotificationTo: AddressObject | undefined;
|
|
81
|
+
received: string[];
|
|
82
|
+
priority: string | undefined;
|
|
83
|
+
headers: HeadersMap;
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* An array of raw header lines
|
|
87
|
+
*/
|
|
88
|
+
export type HeaderLines = ReadonlyArray<{
|
|
89
|
+
key: string;
|
|
90
|
+
line: string;
|
|
91
|
+
}>;
|
|
92
|
+
/**
|
|
93
|
+
* COmmon part of the Attachment object.
|
|
94
|
+
*/
|
|
95
|
+
export interface Attachment extends MailParser.Attachment {
|
|
96
|
+
/**
|
|
97
|
+
* Message type.
|
|
98
|
+
*/
|
|
99
|
+
type: "attachment";
|
|
100
|
+
/**
|
|
101
|
+
* If true then this attachment should not be offered for download
|
|
102
|
+
* (at least not in the main attachments list).
|
|
103
|
+
*/
|
|
104
|
+
related: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Attachment contents.
|
|
107
|
+
*/
|
|
108
|
+
content: Buffer;
|
|
109
|
+
/**
|
|
110
|
+
* MIME type of the message.
|
|
111
|
+
*/
|
|
112
|
+
contentType: string;
|
|
113
|
+
/**
|
|
114
|
+
* Content disposition type for the attachment,
|
|
115
|
+
* most probably `'attachment'`.
|
|
116
|
+
*/
|
|
117
|
+
contentDisposition: string;
|
|
118
|
+
/**
|
|
119
|
+
* File name of the attachment.
|
|
120
|
+
*/
|
|
121
|
+
filename?: string | undefined;
|
|
122
|
+
/**
|
|
123
|
+
* A Map value that holds MIME headers for the attachment node.
|
|
124
|
+
*/
|
|
125
|
+
headers: HeadersMap;
|
|
126
|
+
/**
|
|
127
|
+
* An array of raw header lines for the attachment node.
|
|
128
|
+
*/
|
|
129
|
+
headerLines: HeaderLines;
|
|
130
|
+
/**
|
|
131
|
+
* A MD5 hash of the message content.
|
|
132
|
+
*/
|
|
133
|
+
checksum: string;
|
|
134
|
+
/**
|
|
135
|
+
* Message size in bytes.
|
|
136
|
+
*/
|
|
137
|
+
size: number;
|
|
138
|
+
/**
|
|
139
|
+
* The header value from `Content-ID`.
|
|
140
|
+
*/
|
|
141
|
+
contentId?: string | undefined;
|
|
142
|
+
/**
|
|
143
|
+
* `contentId` without `<` and `>`.
|
|
144
|
+
*/
|
|
145
|
+
cid?: string | undefined;
|
|
146
|
+
generatedFileName: string;
|
|
147
|
+
}
|
|
148
|
+
export interface ParsedMail {
|
|
149
|
+
/**
|
|
150
|
+
* An array of attachments.
|
|
151
|
+
*/
|
|
152
|
+
attachments: Attachment[];
|
|
153
|
+
/**
|
|
154
|
+
* A Map object with lowercase header keys.
|
|
155
|
+
*
|
|
156
|
+
* - All address headers are converted into address objects.
|
|
157
|
+
* - `references` is a string if only a single reference-id exists or an
|
|
158
|
+
* array if multiple ids exist.
|
|
159
|
+
* - `date` value is a Date object.
|
|
160
|
+
*/
|
|
161
|
+
headers: Headers;
|
|
162
|
+
/**
|
|
163
|
+
* An array of raw header lines
|
|
164
|
+
*/
|
|
165
|
+
headerLines: HeaderLines;
|
|
166
|
+
/**
|
|
167
|
+
* The HTML body of the message.
|
|
168
|
+
*
|
|
169
|
+
* Sets to `false` when there is no HTML body.
|
|
170
|
+
*
|
|
171
|
+
* If the message included embedded images as cid: urls then these are all
|
|
172
|
+
* replaced with base64 formatted data: URIs.
|
|
173
|
+
*/
|
|
174
|
+
html: string | false;
|
|
175
|
+
/**
|
|
176
|
+
* The plaintext body of the message.
|
|
177
|
+
*/
|
|
178
|
+
text?: string | undefined;
|
|
179
|
+
/**
|
|
180
|
+
* The plaintext body of the message formatted as HTML.
|
|
181
|
+
*/
|
|
182
|
+
textAsHtml?: string | undefined;
|
|
183
|
+
/**
|
|
184
|
+
* The subject line.
|
|
185
|
+
*/
|
|
186
|
+
subject?: string | undefined;
|
|
187
|
+
/**
|
|
188
|
+
* An array of referenced Message-ID values.
|
|
189
|
+
*/
|
|
190
|
+
references: string[];
|
|
191
|
+
/**
|
|
192
|
+
* A Date object for the `Date:` header.
|
|
193
|
+
*/
|
|
194
|
+
date: Date;
|
|
195
|
+
to: EmailAddress[];
|
|
196
|
+
from: EmailAddress[];
|
|
197
|
+
cc: EmailAddress[];
|
|
198
|
+
bcc: EmailAddress[];
|
|
199
|
+
replyTo: EmailAddress[];
|
|
200
|
+
/**
|
|
201
|
+
* The In-Reply-To value string.
|
|
202
|
+
*/
|
|
203
|
+
inReplyTo: string[];
|
|
204
|
+
/**
|
|
205
|
+
* The Message-ID value string.
|
|
206
|
+
*/
|
|
207
|
+
messageId?: string | undefined;
|
|
208
|
+
/**
|
|
209
|
+
* Priority of the e-mail.
|
|
210
|
+
*/
|
|
211
|
+
priority: "normal" | "low" | "high";
|
|
212
|
+
}
|
|
213
|
+
export interface Envelope {
|
|
214
|
+
id: string;
|
|
215
|
+
from: EmailAddress[];
|
|
216
|
+
to: EmailAddress[];
|
|
217
|
+
date: Date;
|
|
218
|
+
subject?: string | undefined;
|
|
219
|
+
hasAttachment: boolean;
|
|
220
|
+
isRead: boolean;
|
|
221
|
+
}
|
|
222
|
+
export interface Mail extends ParsedMail {
|
|
223
|
+
id: string;
|
|
224
|
+
calculatedBcc: EmailAddress[];
|
|
225
|
+
size: number;
|
|
226
|
+
sizeHuman: string;
|
|
227
|
+
envelope: Envelope;
|
|
228
|
+
}
|
package/dist/lib/type.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeId = makeId;
|
|
4
|
+
exports.formatBytes = formatBytes;
|
|
5
|
+
exports.filterEmails = filterEmails;
|
|
6
|
+
exports.delay = delay;
|
|
7
|
+
// Create an unique id, length 8 characters
|
|
8
|
+
function makeId() {
|
|
9
|
+
let text = "";
|
|
10
|
+
const possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
11
|
+
for (let i = 0; i < 8; i++) {
|
|
12
|
+
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
|
13
|
+
}
|
|
14
|
+
return text;
|
|
15
|
+
}
|
|
16
|
+
// Format bytes
|
|
17
|
+
// Source: https://stackoverflow.com/a/18650828/3143704
|
|
18
|
+
function formatBytes(bytes, decimals = 2) {
|
|
19
|
+
if (bytes === 0)
|
|
20
|
+
return "0 bytes";
|
|
21
|
+
const k = 1024;
|
|
22
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
23
|
+
const sizes = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
24
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
25
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + " " + sizes[i];
|
|
26
|
+
}
|
|
27
|
+
function lookup(obj, path) {
|
|
28
|
+
const parts = path.split(".");
|
|
29
|
+
const base = obj[parts[0]];
|
|
30
|
+
if (!base)
|
|
31
|
+
return;
|
|
32
|
+
if (parts.length === 1) {
|
|
33
|
+
return base;
|
|
34
|
+
}
|
|
35
|
+
const next = parts.slice(1).join(".");
|
|
36
|
+
if (Array.isArray(base)) {
|
|
37
|
+
return base.map((el) => {
|
|
38
|
+
return lookup(el, next);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return lookup(base, next);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function filterEmails(emails, query) {
|
|
46
|
+
return emails.filter((email) => {
|
|
47
|
+
const hits = [];
|
|
48
|
+
for (const key in query) {
|
|
49
|
+
if (Object.hasOwnProperty.call(query, key)) {
|
|
50
|
+
const element = query[key];
|
|
51
|
+
const value = lookup(email, key);
|
|
52
|
+
if (Array.isArray(value)) {
|
|
53
|
+
hits.push(value.includes(element));
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
hits.push(value === element);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return !hits.includes(false);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
function delay(ms) {
|
|
64
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
65
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MailDev - web
|
|
3
|
+
*/
|
|
4
|
+
import { MailServer } from "./mailserver";
|
|
5
|
+
export interface WebOptions {
|
|
6
|
+
port?: number;
|
|
7
|
+
host?: string;
|
|
8
|
+
basePathname?: string;
|
|
9
|
+
auth?: {
|
|
10
|
+
user: string;
|
|
11
|
+
pass: string;
|
|
12
|
+
};
|
|
13
|
+
ssl?: {
|
|
14
|
+
cert: string;
|
|
15
|
+
key: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare class Web {
|
|
19
|
+
protocol: string;
|
|
20
|
+
port: number;
|
|
21
|
+
host: string;
|
|
22
|
+
basePathname: string;
|
|
23
|
+
/**
|
|
24
|
+
* Keep record of all connections to close them on shutdown
|
|
25
|
+
*/
|
|
26
|
+
connections: {};
|
|
27
|
+
server: any;
|
|
28
|
+
io: any;
|
|
29
|
+
constructor(mailserver: MailServer, options?: WebOptions);
|
|
30
|
+
listen(): Promise<void>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
}
|