domma-cms 0.116.1 → 0.116.2
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
CHANGED
|
@@ -206,7 +206,7 @@ export function createPool(config) {
|
|
|
206
206
|
} catch {
|
|
207
207
|
// Already down - nothing to close.
|
|
208
208
|
}
|
|
209
|
-
return {ok: false, error: describeError(err)};
|
|
209
|
+
return {ok: false, error: describeError(err, connection)};
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
|
|
@@ -261,19 +261,52 @@ export function createPool(config) {
|
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
/**
|
|
264
|
-
* Turn an IMAP/network error into something worth showing a user.
|
|
264
|
+
* Turn an IMAP/SMTP/network error into something worth showing a user.
|
|
265
265
|
*
|
|
266
266
|
* ImapFlow's own messages are decent but its auth failures surface as raw
|
|
267
|
-
* server responses, which vary by server and read like a stack trace.
|
|
267
|
+
* server responses, which vary by server and read like a stack trace. TLS
|
|
268
|
+
* failures are worse: OpenSSL's text ("…:tls_validate_record_header:wrong
|
|
269
|
+
* version number:…") names nothing the user can change. nodemailer also wraps
|
|
270
|
+
* every socket error as `ESOCKET`, dropping the TLS code, so those are
|
|
271
|
+
* recognised by `reason`/message as well as by code.
|
|
268
272
|
*
|
|
269
|
-
* @param {Error & {code?: string, authenticationFailed?: boolean}} err
|
|
273
|
+
* @param {Error & {code?: string, reason?: string, authenticationFailed?: boolean}} err
|
|
274
|
+
* @param {{host?: string, port?: number, secure?: boolean}} [connection] - what was tried, so the message can name it
|
|
270
275
|
* @returns {string}
|
|
271
276
|
*/
|
|
272
|
-
export function describeError(err) {
|
|
277
|
+
export function describeError(err, connection = {}) {
|
|
273
278
|
if (!err) return 'Unknown error.';
|
|
274
|
-
if (err.authenticationFailed) return 'The mail server rejected that username and password.';
|
|
279
|
+
if (err.authenticationFailed || err.code === 'EAUTH') return 'The mail server rejected that username and password.';
|
|
275
280
|
|
|
276
|
-
|
|
281
|
+
const host = connection.host || 'The mail server';
|
|
282
|
+
const port = connection.port ? `Port ${connection.port}` : 'That port';
|
|
283
|
+
const text = `${err.reason || ''} ${err.message || ''}`;
|
|
284
|
+
|
|
285
|
+
// Implicit TLS against a port that starts in plain text (587, 143, 25):
|
|
286
|
+
// the "server hello" is a greeting line, which OpenSSL reads as a bad version.
|
|
287
|
+
if (err.code === 'ERR_SSL_WRONG_VERSION_NUMBER' || /wrong version number/i.test(text)) {
|
|
288
|
+
return connection.secure
|
|
289
|
+
? `${port} does not start with TLS, so Implicit TLS must be off for it - ports 587 and 143 switch to TLS with STARTTLS. Untick Implicit TLS, or use the implicit-TLS port (465 to send, 993 to read).`
|
|
290
|
+
: `${port} answered in a different TLS mode from the one chosen. Check the port and the Implicit TLS setting.`;
|
|
291
|
+
}
|
|
292
|
+
if (['DEPTH_ZERO_SELF_SIGNED_CERT', 'SELF_SIGNED_CERT_IN_CHAIN'].includes(err.code) || /self[- ]signed certificate/i.test(text)) {
|
|
293
|
+
return `${host === 'The mail server' ? 'The mail server’s' : `${host}’s`} TLS certificate is self-signed, so the server cannot prove who it is. `
|
|
294
|
+
+ `Install a trusted certificate (Let’s Encrypt, for example) that covers this hostname on the mail server. `
|
|
295
|
+
+ `For a server you control, the plugin’s allowInsecureTLS setting accepts it instead.`;
|
|
296
|
+
}
|
|
297
|
+
if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID' || /altnames|does not match certificate/i.test(text)) {
|
|
298
|
+
return `The mail server’s TLS certificate does not cover ${connection.host || 'that hostname'}. Use the hostname the certificate was issued for, or reissue it to include this one.`;
|
|
299
|
+
}
|
|
300
|
+
if (err.code === 'CERT_HAS_EXPIRED' || /certificate has expired/i.test(text)) {
|
|
301
|
+
return 'The mail server’s TLS certificate has expired. It needs renewing on the server.';
|
|
302
|
+
}
|
|
303
|
+
if (['UNABLE_TO_VERIFY_LEAF_SIGNATURE', 'UNABLE_TO_GET_ISSUER_CERT_LOCALLY'].includes(err.code) || /unable to (verify the first certificate|get local issuer)/i.test(text)) {
|
|
304
|
+
return 'The mail server’s TLS certificate could not be verified - it is missing its intermediate certificate, or was issued by an authority this server does not trust.';
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// nodemailer reports these as ESOCKET/EDNS; the system code survives only in the message.
|
|
308
|
+
const netCode = text.match(/\b(ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT|ECONNRESET)\b/)?.[1] ?? err.code;
|
|
309
|
+
switch (netCode) {
|
|
277
310
|
case 'ENOTFOUND':
|
|
278
311
|
case 'EAI_AGAIN':
|
|
279
312
|
return 'That mail server hostname could not be resolved.';
|
|
@@ -282,11 +315,6 @@ export function describeError(err) {
|
|
|
282
315
|
case 'ETIMEDOUT':
|
|
283
316
|
case 'ECONNRESET':
|
|
284
317
|
return 'The mail server did not respond in time.';
|
|
285
|
-
case 'CERT_HAS_EXPIRED':
|
|
286
|
-
case 'DEPTH_ZERO_SELF_SIGNED_CERT':
|
|
287
|
-
case 'SELF_SIGNED_CERT_IN_CHAIN':
|
|
288
|
-
case 'ERR_TLS_CERT_ALTNAME_INVALID':
|
|
289
|
-
return 'The mail server’s TLS certificate could not be verified.';
|
|
290
318
|
default:
|
|
291
319
|
return err.message || 'The mail server connection failed.';
|
|
292
320
|
}
|
|
@@ -13,6 +13,7 @@ import {createRequire} from 'node:module';
|
|
|
13
13
|
import nodemailer from 'nodemailer';
|
|
14
14
|
|
|
15
15
|
import {bareAddress, formatFrom} from '../admin/mail/identity.js';
|
|
16
|
+
import {describeError} from './imapPool.js';
|
|
16
17
|
import {normalisePriority, priorityHeaders} from './priority.js';
|
|
17
18
|
|
|
18
19
|
// MailComposer is not on nodemailer's public export, but it is the piece that
|
|
@@ -389,7 +390,7 @@ export async function verifySmtp(transportConfig, allowInsecureTLS = false) {
|
|
|
389
390
|
await transport.verify();
|
|
390
391
|
return {ok: true};
|
|
391
392
|
} catch (err) {
|
|
392
|
-
return {ok: false, error: err
|
|
393
|
+
return {ok: false, error: describeError(err, transportConfig)};
|
|
393
394
|
} finally {
|
|
394
395
|
transport.close();
|
|
395
396
|
}
|