@remit/smtp-service 0.0.14 → 0.0.16
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/package.json +1 -1
- package/src/message-builder.test.ts +48 -1
- package/src/message-builder.ts +15 -1
- package/src/smtp-client.test.ts +7 -3
- package/src/smtp-client.ts +8 -1
package/package.json
CHANGED
|
@@ -132,7 +132,7 @@ describe("buildMailMessage", () => {
|
|
|
132
132
|
},
|
|
133
133
|
];
|
|
134
134
|
const message = buildMailMessage(baseOutbox(), attachments);
|
|
135
|
-
assert.
|
|
135
|
+
assert.deepEqual(message.attachments, attachments);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
it("leaves attachments undefined when none are provided", () => {
|
|
@@ -257,6 +257,53 @@ describe("renderRawMessage", () => {
|
|
|
257
257
|
assert.ok(raw.includes(Buffer.from("png-bytes").toString("base64")));
|
|
258
258
|
});
|
|
259
259
|
|
|
260
|
+
it("puts a file the html body refers to inline, next to the html", async () => {
|
|
261
|
+
const raw = String(
|
|
262
|
+
await renderRawMessage(
|
|
263
|
+
buildMailMessage(
|
|
264
|
+
baseOutbox({
|
|
265
|
+
textBody: "see below",
|
|
266
|
+
htmlBody: '<p>see below</p><p><img src="cid:shot-1@remit"></p>',
|
|
267
|
+
}),
|
|
268
|
+
[
|
|
269
|
+
{
|
|
270
|
+
filename: "shot.png",
|
|
271
|
+
content: Buffer.from("png-bytes"),
|
|
272
|
+
contentType: "image/png",
|
|
273
|
+
cid: "shot-1@remit",
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
),
|
|
277
|
+
),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
assert.match(raw, /Content-Type: multipart\/related; type="text\/html"/);
|
|
281
|
+
assert.match(raw, /Content-Disposition: inline; filename=shot\.png/);
|
|
282
|
+
assert.match(raw, /Content-ID: <shot-1@remit>/);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it("sends a file the html body does not refer to as an ordinary attachment", async () => {
|
|
286
|
+
const raw = String(
|
|
287
|
+
await renderRawMessage(
|
|
288
|
+
buildMailMessage(
|
|
289
|
+
baseOutbox({ textBody: "attached", htmlBody: "<p>attached</p>" }),
|
|
290
|
+
[
|
|
291
|
+
{
|
|
292
|
+
filename: "photo.png",
|
|
293
|
+
content: Buffer.from("png-bytes"),
|
|
294
|
+
contentType: "image/png",
|
|
295
|
+
cid: "photo-1@remit",
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
),
|
|
299
|
+
),
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
assert.match(raw, /Content-Disposition: attachment; filename=photo\.png/);
|
|
303
|
+
assert.ok(!raw.includes("Content-ID"));
|
|
304
|
+
assert.ok(!raw.includes("multipart/related"));
|
|
305
|
+
});
|
|
306
|
+
|
|
260
307
|
it("dates the rendered copy from the moment the send was recorded", async () => {
|
|
261
308
|
const raw = String(
|
|
262
309
|
await renderRawMessage(
|
package/src/message-builder.ts
CHANGED
|
@@ -30,6 +30,18 @@ export interface MailAttachment {
|
|
|
30
30
|
const formatFrom = (address: string, name: string | undefined): string =>
|
|
31
31
|
name ? `"${name.replace(/(["\\])/g, "\\$1")}" <${address}>` : address;
|
|
32
32
|
|
|
33
|
+
const placeAttachment = (
|
|
34
|
+
attachment: MailAttachment,
|
|
35
|
+
html: string | undefined,
|
|
36
|
+
): MailAttachment => {
|
|
37
|
+
if (!attachment.cid || attachment.contentDisposition) return attachment;
|
|
38
|
+
if (html?.includes(`cid:${attachment.cid}`)) {
|
|
39
|
+
return { ...attachment, contentDisposition: "inline" };
|
|
40
|
+
}
|
|
41
|
+
const { cid: _unreferenced, ...standalone } = attachment;
|
|
42
|
+
return standalone;
|
|
43
|
+
};
|
|
44
|
+
|
|
33
45
|
/** The one description of an outgoing message: the wire copy and the Sent copy. */
|
|
34
46
|
export const buildMailMessage = (
|
|
35
47
|
outbox: OutboxMessageItem,
|
|
@@ -50,7 +62,9 @@ export const buildMailMessage = (
|
|
|
50
62
|
// submission is accepted, so on the wire path this is always absent and
|
|
51
63
|
// nodemailer dates the message as it goes out.
|
|
52
64
|
date: outbox.sentAt ? new Date(outbox.sentAt) : undefined,
|
|
53
|
-
attachments
|
|
65
|
+
attachments: attachments?.map((attachment) =>
|
|
66
|
+
placeAttachment(attachment, outbox.htmlBody),
|
|
67
|
+
),
|
|
54
68
|
});
|
|
55
69
|
|
|
56
70
|
export const toNodemailerOptions = (message: MailMessage): SendMailOptions => ({
|
package/src/smtp-client.test.ts
CHANGED
|
@@ -158,9 +158,13 @@ describe("sendMail", () => {
|
|
|
158
158
|
);
|
|
159
159
|
});
|
|
160
160
|
|
|
161
|
-
it("throws a classified auth error on a 535 AUTH rejection", async () => {
|
|
161
|
+
it("throws a classified auth error on a 535 AUTH rejection, in the server's words", async () => {
|
|
162
|
+
// What Microsoft answers a tenant with SMTP AUTH switched off. Signing in
|
|
163
|
+
// again never clears it, so the text has to survive classification for the
|
|
164
|
+
// account card to say what actually has to change.
|
|
162
165
|
const server = await spawn({
|
|
163
|
-
authResponse:
|
|
166
|
+
authResponse:
|
|
167
|
+
"535 5.7.139 Authentication unsuccessful, SmtpClientAuthentication is disabled for the Tenant\r\n",
|
|
164
168
|
});
|
|
165
169
|
|
|
166
170
|
await assert.rejects(
|
|
@@ -168,7 +172,7 @@ describe("sendMail", () => {
|
|
|
168
172
|
(error: unknown) => {
|
|
169
173
|
assert.ok(error instanceof SmtpConnectionError);
|
|
170
174
|
assert.equal(error.kind, "auth");
|
|
171
|
-
assert.
|
|
175
|
+
assert.match(error.message, /SmtpClientAuthentication is disabled/);
|
|
172
176
|
return true;
|
|
173
177
|
},
|
|
174
178
|
);
|
package/src/smtp-client.ts
CHANGED
|
@@ -111,6 +111,7 @@ export const sendMail = async (
|
|
|
111
111
|
responseCode?: number;
|
|
112
112
|
code?: string;
|
|
113
113
|
command?: string;
|
|
114
|
+
response?: string;
|
|
114
115
|
},
|
|
115
116
|
) => {
|
|
116
117
|
const smtpCode = error.responseCode;
|
|
@@ -122,9 +123,15 @@ export const sendMail = async (
|
|
|
122
123
|
smtpCode >= 500 &&
|
|
123
124
|
error.command === "AUTH")
|
|
124
125
|
) {
|
|
126
|
+
// The server's own words, when it gave any: a 535 reading
|
|
127
|
+
// "SmtpClientAuthentication is disabled for the Tenant" is not a
|
|
128
|
+
// failure signing in again clears, and the account card can only
|
|
129
|
+
// say so if the text survives the classification.
|
|
125
130
|
throw new SmtpConnectionError(
|
|
126
131
|
"auth",
|
|
127
|
-
|
|
132
|
+
error.response
|
|
133
|
+
? `SMTP authentication failed: ${error.response.trim()}`
|
|
134
|
+
: "SMTP authentication failed",
|
|
128
135
|
error,
|
|
129
136
|
);
|
|
130
137
|
}
|