@emailcheck/email-validator-js 2.14.1 → 3.0.1-beta.0
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/README.md +109 -0
- package/dist/cache-interface.d.ts +1 -0
- package/dist/cache.d.ts +2 -0
- package/dist/index.esm.js +400 -102
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +400 -101
- package/dist/index.js.map +1 -1
- package/dist/smtp.d.ts +6 -1
- package/dist/types.d.ts +52 -4
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var tinyLru = require('tiny-lru');
|
|
|
5
5
|
var node_dns = require('node:dns');
|
|
6
6
|
var stringSimilarityJs = require('string-similarity-js');
|
|
7
7
|
var net = require('node:net');
|
|
8
|
+
var tls = require('node:tls');
|
|
8
9
|
|
|
9
10
|
function _interopNamespaceDefault(e) {
|
|
10
11
|
var n = Object.create(null);
|
|
@@ -24,6 +25,7 @@ function _interopNamespaceDefault(e) {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
var net__namespace = /*#__PURE__*/_interopNamespaceDefault(net);
|
|
28
|
+
var tls__namespace = /*#__PURE__*/_interopNamespaceDefault(tls);
|
|
27
29
|
|
|
28
30
|
class LRUAdapter {
|
|
29
31
|
constructor(maxSize = 1e3, ttlMs = 36e5) {
|
|
@@ -73,6 +75,8 @@ const DEFAULT_CACHE_OPTIONS = {
|
|
|
73
75
|
// 24 hours
|
|
74
76
|
smtp: 18e5,
|
|
75
77
|
// 30 minutes
|
|
78
|
+
smtpPort: 36e5,
|
|
79
|
+
// 1 hour
|
|
76
80
|
domainSuggestion: 864e5,
|
|
77
81
|
// 24 hours
|
|
78
82
|
whois: 36e5
|
|
@@ -84,6 +88,7 @@ const DEFAULT_CACHE_OPTIONS = {
|
|
|
84
88
|
free: 1e3,
|
|
85
89
|
domainValid: 1e3,
|
|
86
90
|
smtp: 500,
|
|
91
|
+
smtpPort: 500,
|
|
87
92
|
domainSuggestion: 1e3,
|
|
88
93
|
whois: 200
|
|
89
94
|
}
|
|
@@ -97,6 +102,7 @@ function getDefaultCache() {
|
|
|
97
102
|
free: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.free, DEFAULT_CACHE_OPTIONS.ttl.free),
|
|
98
103
|
domainValid: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainValid, DEFAULT_CACHE_OPTIONS.ttl.domainValid),
|
|
99
104
|
smtp: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtp, DEFAULT_CACHE_OPTIONS.ttl.smtp),
|
|
105
|
+
smtpPort: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtpPort, DEFAULT_CACHE_OPTIONS.ttl.smtpPort),
|
|
100
106
|
domainSuggestion: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainSuggestion, DEFAULT_CACHE_OPTIONS.ttl.domainSuggestion),
|
|
101
107
|
whois: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.whois, DEFAULT_CACHE_OPTIONS.ttl.whois)
|
|
102
108
|
};
|
|
@@ -113,6 +119,7 @@ function clearDefaultCache() {
|
|
|
113
119
|
defaultCacheInstance.free.clear();
|
|
114
120
|
defaultCacheInstance.domainValid.clear();
|
|
115
121
|
defaultCacheInstance.smtp.clear();
|
|
122
|
+
defaultCacheInstance.smtpPort.clear();
|
|
116
123
|
defaultCacheInstance.domainSuggestion.clear();
|
|
117
124
|
defaultCacheInstance.whois.clear();
|
|
118
125
|
}
|
|
@@ -1147,6 +1154,40 @@ function detectName(email) {
|
|
|
1147
1154
|
return detectNameFromEmail({ email });
|
|
1148
1155
|
}
|
|
1149
1156
|
|
|
1157
|
+
exports.VerificationErrorCode = void 0;
|
|
1158
|
+
(function(VerificationErrorCode2) {
|
|
1159
|
+
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1160
|
+
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1161
|
+
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1162
|
+
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1163
|
+
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1164
|
+
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1165
|
+
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1166
|
+
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1167
|
+
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1168
|
+
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1169
|
+
})(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
|
|
1170
|
+
exports.SMTPStep = void 0;
|
|
1171
|
+
(function(SMTPStep2) {
|
|
1172
|
+
SMTPStep2["GREETING"] = "GREETING";
|
|
1173
|
+
SMTPStep2["EHLO"] = "EHLO";
|
|
1174
|
+
SMTPStep2["HELO"] = "HELO";
|
|
1175
|
+
SMTPStep2["STARTTLS"] = "STARTTLS";
|
|
1176
|
+
SMTPStep2["MAIL_FROM"] = "MAIL_FROM";
|
|
1177
|
+
SMTPStep2["RCPT_TO"] = "RCPT_TO";
|
|
1178
|
+
SMTPStep2["VRFY"] = "VRFY";
|
|
1179
|
+
SMTPStep2["QUIT"] = "QUIT";
|
|
1180
|
+
})(exports.SMTPStep || (exports.SMTPStep = {}));
|
|
1181
|
+
|
|
1182
|
+
function isIPAddress(host) {
|
|
1183
|
+
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
1184
|
+
if (ipv4Regex.test(host)) {
|
|
1185
|
+
const octets = host.split(".");
|
|
1186
|
+
return octets.every((octet) => parseInt(octet, 10) <= 255);
|
|
1187
|
+
}
|
|
1188
|
+
const ipv6Regex = /^(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,7}:$|^(?:[0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,5}(?::[0-9a-fA-F]{1,4}){1,2}$|^(?:[0-9a-fA-F]{1,4}:){1,4}(?::[0-9a-fA-F]{1,4}){1,3}$|^(?:[0-9a-fA-F]{1,4}:){1,3}(?::[0-9a-fA-F]{1,4}){1,4}$|^(?:[0-9a-fA-F]{1,4}:){1,2}(?::[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:(?:(?::[0-9a-fA-F]{1,4}){1,6})$|^::1(?::(?::[0-9a-fA-F]{1,4}){1,7})|$|:(?:(?::[0-9a-fA-F]{1,4}){1,7}:)$/;
|
|
1189
|
+
return ipv6Regex.test(host);
|
|
1190
|
+
}
|
|
1150
1191
|
function isOverQuota(smtpReply) {
|
|
1151
1192
|
return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
|
|
1152
1193
|
}
|
|
@@ -1156,129 +1197,382 @@ function isInvalidMailboxError(smtpReply) {
|
|
|
1156
1197
|
function isMultilineGreet(smtpReply) {
|
|
1157
1198
|
return Boolean(smtpReply && /^(250|220)-/.test(smtpReply));
|
|
1158
1199
|
}
|
|
1200
|
+
const DEFAULT_PORTS = [25, 587, 465];
|
|
1201
|
+
const DEFAULT_TIMEOUT = 3e3;
|
|
1202
|
+
const DEFAULT_MAX_RETRIES = 1;
|
|
1203
|
+
const PORT_CONFIGS = {
|
|
1204
|
+
25: { tls: false, starttls: true },
|
|
1205
|
+
587: { tls: false, starttls: true },
|
|
1206
|
+
465: { tls: true, starttls: false }
|
|
1207
|
+
};
|
|
1159
1208
|
async function verifyMailboxSMTP(params) {
|
|
1160
|
-
const { local, domain, mxRecords = [],
|
|
1161
|
-
const
|
|
1209
|
+
const { local, domain, mxRecords = [], options = {} } = params;
|
|
1210
|
+
const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
|
|
1211
|
+
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1162
1212
|
};
|
|
1163
1213
|
if (!mxRecords || mxRecords.length === 0) {
|
|
1164
|
-
|
|
1214
|
+
log("No MX records found");
|
|
1215
|
+
return { result: false, cached: false, port: 0, portCached: false };
|
|
1216
|
+
}
|
|
1217
|
+
const mxHost = mxRecords[0];
|
|
1218
|
+
log(`Verifying ${local}@${domain} via ${mxHost}`);
|
|
1219
|
+
const smtpCacheStore = cache ? getCacheStore(cache, "smtp") : null;
|
|
1220
|
+
if (smtpCacheStore) {
|
|
1221
|
+
let cachedResult;
|
|
1222
|
+
try {
|
|
1223
|
+
cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
|
|
1224
|
+
if (cachedResult !== void 0) {
|
|
1225
|
+
log(`Using cached SMTP result: ${cachedResult}`);
|
|
1226
|
+
return {
|
|
1227
|
+
result: typeof cachedResult === "boolean" ? cachedResult : null,
|
|
1228
|
+
cached: true,
|
|
1229
|
+
port: 0,
|
|
1230
|
+
portCached: false
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
} catch (_error) {
|
|
1234
|
+
cachedResult = void 0;
|
|
1235
|
+
}
|
|
1165
1236
|
}
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
port,
|
|
1174
|
-
timeout,
|
|
1175
|
-
log,
|
|
1176
|
-
attempt
|
|
1177
|
-
});
|
|
1178
|
-
if (result !== null) {
|
|
1179
|
-
return result;
|
|
1237
|
+
const smtpPortCacheStore = cache ? getCacheStore(cache, "smtpPort") : null;
|
|
1238
|
+
if (smtpPortCacheStore) {
|
|
1239
|
+
let cachedPort;
|
|
1240
|
+
try {
|
|
1241
|
+
cachedPort = await smtpPortCacheStore.get(mxHost);
|
|
1242
|
+
} catch (_error) {
|
|
1243
|
+
cachedPort = null;
|
|
1180
1244
|
}
|
|
1181
|
-
if (
|
|
1182
|
-
|
|
1245
|
+
if (cachedPort) {
|
|
1246
|
+
log(`Using cached port: ${cachedPort}`);
|
|
1247
|
+
const result = await testSMTPConnection({
|
|
1248
|
+
mxHost,
|
|
1249
|
+
port: cachedPort,
|
|
1250
|
+
local,
|
|
1251
|
+
domain,
|
|
1252
|
+
timeout,
|
|
1253
|
+
tlsConfig,
|
|
1254
|
+
hostname,
|
|
1255
|
+
useVRFY,
|
|
1256
|
+
sequence,
|
|
1257
|
+
log
|
|
1258
|
+
});
|
|
1259
|
+
if (smtpCacheStore && result !== void 0) {
|
|
1260
|
+
try {
|
|
1261
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
|
|
1262
|
+
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1263
|
+
} catch (_error) {
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
return { result, cached: false, port: cachedPort, portCached: true };
|
|
1183
1267
|
}
|
|
1184
1268
|
}
|
|
1185
|
-
|
|
1269
|
+
for (const port of ports) {
|
|
1270
|
+
log(`Testing port ${port}`);
|
|
1271
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1272
|
+
if (attempt > 0) {
|
|
1273
|
+
const delay = Math.min(1e3 * 2 ** (attempt - 1), 5e3);
|
|
1274
|
+
log(`Retry ${attempt + 1}, waiting ${delay}ms`);
|
|
1275
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1276
|
+
}
|
|
1277
|
+
const result = await testSMTPConnection({
|
|
1278
|
+
mxHost,
|
|
1279
|
+
port,
|
|
1280
|
+
local,
|
|
1281
|
+
domain,
|
|
1282
|
+
timeout,
|
|
1283
|
+
tlsConfig,
|
|
1284
|
+
hostname,
|
|
1285
|
+
useVRFY,
|
|
1286
|
+
sequence,
|
|
1287
|
+
log
|
|
1288
|
+
});
|
|
1289
|
+
if (smtpCacheStore && result !== void 0) {
|
|
1290
|
+
try {
|
|
1291
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
|
|
1292
|
+
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1293
|
+
} catch (_error) {
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
if (result !== null) {
|
|
1297
|
+
if (smtpPortCacheStore) {
|
|
1298
|
+
try {
|
|
1299
|
+
await smtpPortCacheStore.set(mxHost, port);
|
|
1300
|
+
log(`Cached port ${port} for ${mxHost}`);
|
|
1301
|
+
} catch (_error) {
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
return { result, cached: false, port, portCached: false };
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
}
|
|
1308
|
+
log("All ports failed");
|
|
1309
|
+
return { result: null, cached: false, port: 0, portCached: false };
|
|
1186
1310
|
}
|
|
1187
|
-
async function
|
|
1188
|
-
const {
|
|
1311
|
+
async function testSMTPConnection(params) {
|
|
1312
|
+
const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
|
|
1313
|
+
const portConfig = PORT_CONFIGS[port] || { tls: false, starttls: false };
|
|
1314
|
+
const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
|
|
1315
|
+
const implicitTLS = portConfig.tls;
|
|
1316
|
+
const defaultSequence = {
|
|
1317
|
+
steps: [exports.SMTPStep.GREETING, exports.SMTPStep.EHLO, exports.SMTPStep.MAIL_FROM, exports.SMTPStep.RCPT_TO]
|
|
1318
|
+
};
|
|
1319
|
+
const activeSequence = sequence || defaultSequence;
|
|
1320
|
+
if (port === 25) {
|
|
1321
|
+
activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.EHLO ? exports.SMTPStep.HELO : step);
|
|
1322
|
+
}
|
|
1323
|
+
const tlsOptions = {
|
|
1324
|
+
host: mxHost,
|
|
1325
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost,
|
|
1326
|
+
// Don't set servername for IP addresses
|
|
1327
|
+
rejectUnauthorized: false,
|
|
1328
|
+
minVersion: "TLSv1.2",
|
|
1329
|
+
...typeof tlsConfig === "object" ? tlsConfig : {}
|
|
1330
|
+
};
|
|
1189
1331
|
return new Promise((resolve) => {
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
});
|
|
1195
|
-
let resTimeout = null;
|
|
1332
|
+
let socket;
|
|
1333
|
+
let buffer = "";
|
|
1334
|
+
let isTLS = implicitTLS;
|
|
1335
|
+
let currentStepIndex = 0;
|
|
1196
1336
|
let resolved = false;
|
|
1197
|
-
let
|
|
1337
|
+
let supportsSTARTTLS = false;
|
|
1338
|
+
let supportsVRFY = false;
|
|
1198
1339
|
const cleanup = () => {
|
|
1199
|
-
if (cleaned)
|
|
1200
|
-
return;
|
|
1201
|
-
cleaned = true;
|
|
1202
|
-
if (resTimeout) {
|
|
1203
|
-
clearTimeout(resTimeout);
|
|
1204
|
-
resTimeout = null;
|
|
1205
|
-
}
|
|
1206
|
-
if (socket && !socket.destroyed) {
|
|
1207
|
-
socket.removeAllListeners();
|
|
1208
|
-
socket.destroy();
|
|
1209
|
-
}
|
|
1210
|
-
};
|
|
1211
|
-
const ret = (result) => {
|
|
1212
1340
|
if (resolved)
|
|
1213
1341
|
return;
|
|
1214
1342
|
resolved = true;
|
|
1215
|
-
|
|
1216
|
-
log("[verifyMailboxSMTP] closing socket");
|
|
1343
|
+
try {
|
|
1217
1344
|
socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
|
|
1218
|
-
|
|
1345
|
+
} catch {
|
|
1219
1346
|
}
|
|
1347
|
+
setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
|
|
1348
|
+
};
|
|
1349
|
+
const finish = (result, reason) => {
|
|
1350
|
+
if (resolved)
|
|
1351
|
+
return;
|
|
1352
|
+
log(`${port}: ${reason || (result ? "valid" : "invalid")}`);
|
|
1220
1353
|
cleanup();
|
|
1221
1354
|
resolve(result);
|
|
1222
1355
|
};
|
|
1223
|
-
const
|
|
1224
|
-
|
|
1225
|
-
socket.on("data", (data) => {
|
|
1226
|
-
const dataString = String(data);
|
|
1227
|
-
log("[verifyMailboxSMTP] got data", dataString);
|
|
1228
|
-
if (isInvalidMailboxError(dataString))
|
|
1229
|
-
return ret(false);
|
|
1230
|
-
if (isOverQuota(dataString))
|
|
1231
|
-
return ret(false);
|
|
1232
|
-
if (!dataString.includes("220") && !dataString.includes("250"))
|
|
1233
|
-
return ret(null);
|
|
1234
|
-
if (isMultilineGreet(dataString))
|
|
1356
|
+
const sendCommand = (cmd) => {
|
|
1357
|
+
if (resolved)
|
|
1235
1358
|
return;
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
log("[verifyMailboxSMTP] writing message", message);
|
|
1239
|
-
return socket.write(`${message}\r
|
|
1359
|
+
log(`\u2192 ${cmd}`);
|
|
1360
|
+
socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
|
|
1240
1361
|
`);
|
|
1362
|
+
};
|
|
1363
|
+
const nextStep = () => {
|
|
1364
|
+
currentStepIndex++;
|
|
1365
|
+
if (currentStepIndex >= activeSequence.steps.length) {
|
|
1366
|
+
finish(true, "sequence_complete");
|
|
1367
|
+
return;
|
|
1241
1368
|
}
|
|
1242
|
-
|
|
1243
|
-
}
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1369
|
+
executeStep(activeSequence.steps[currentStepIndex]);
|
|
1370
|
+
};
|
|
1371
|
+
const executeStep = (step) => {
|
|
1372
|
+
if (resolved)
|
|
1373
|
+
return;
|
|
1374
|
+
switch (step) {
|
|
1375
|
+
case exports.SMTPStep.EHLO:
|
|
1376
|
+
sendCommand(`EHLO ${hostname}`);
|
|
1377
|
+
break;
|
|
1378
|
+
case exports.SMTPStep.HELO:
|
|
1379
|
+
sendCommand(`HELO ${domain}`);
|
|
1380
|
+
break;
|
|
1381
|
+
case exports.SMTPStep.GREETING:
|
|
1382
|
+
break;
|
|
1383
|
+
case exports.SMTPStep.STARTTLS:
|
|
1384
|
+
sendCommand("STARTTLS");
|
|
1385
|
+
break;
|
|
1386
|
+
case exports.SMTPStep.MAIL_FROM: {
|
|
1387
|
+
const from = activeSequence.from || "<>";
|
|
1388
|
+
sendCommand(`MAIL FROM:${from}`);
|
|
1389
|
+
break;
|
|
1390
|
+
}
|
|
1391
|
+
case exports.SMTPStep.RCPT_TO:
|
|
1392
|
+
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1393
|
+
break;
|
|
1394
|
+
case exports.SMTPStep.VRFY: {
|
|
1395
|
+
const vrfyTarget = activeSequence.vrfyTarget || local;
|
|
1396
|
+
sendCommand(`VRFY ${vrfyTarget}`);
|
|
1397
|
+
break;
|
|
1398
|
+
}
|
|
1399
|
+
case exports.SMTPStep.QUIT:
|
|
1400
|
+
sendCommand("QUIT");
|
|
1401
|
+
break;
|
|
1252
1402
|
}
|
|
1253
|
-
}
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
log(
|
|
1260
|
-
if (
|
|
1261
|
-
|
|
1262
|
-
ret(null);
|
|
1403
|
+
};
|
|
1404
|
+
const processResponse = (response) => {
|
|
1405
|
+
if (resolved)
|
|
1406
|
+
return;
|
|
1407
|
+
const code = response.substring(0, 3);
|
|
1408
|
+
const isMultiline = response.length > 3 && response[3] === "-";
|
|
1409
|
+
log(`\u2190 ${response}`);
|
|
1410
|
+
if (isMultilineGreet(response)) {
|
|
1411
|
+
return;
|
|
1263
1412
|
}
|
|
1264
|
-
|
|
1413
|
+
if (isOverQuota(response)) {
|
|
1414
|
+
finish(false, "over_quota");
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1417
|
+
if (isInvalidMailboxError(response)) {
|
|
1418
|
+
finish(false, "not_found");
|
|
1419
|
+
return;
|
|
1420
|
+
}
|
|
1421
|
+
if (isMultiline) {
|
|
1422
|
+
const currentStep2 = activeSequence.steps[currentStepIndex];
|
|
1423
|
+
if (currentStep2 === exports.SMTPStep.EHLO && code === "250") {
|
|
1424
|
+
const upper = response.toUpperCase();
|
|
1425
|
+
if (upper.includes("STARTTLS"))
|
|
1426
|
+
supportsSTARTTLS = true;
|
|
1427
|
+
if (upper.includes("VRFY"))
|
|
1428
|
+
supportsVRFY = true;
|
|
1429
|
+
}
|
|
1430
|
+
if (currentStep2 === exports.SMTPStep.HELO && code === "250") {
|
|
1431
|
+
const upper = response.toUpperCase();
|
|
1432
|
+
if (upper.includes("VRFY"))
|
|
1433
|
+
supportsVRFY = true;
|
|
1434
|
+
}
|
|
1435
|
+
return;
|
|
1436
|
+
}
|
|
1437
|
+
if (!response.includes("220") && !response.includes("250") && !response.includes("550") && !response.includes("552")) {
|
|
1438
|
+
finish(null, "unrecognized_response");
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
const currentStep = activeSequence.steps[currentStepIndex];
|
|
1442
|
+
switch (currentStep) {
|
|
1443
|
+
case exports.SMTPStep.GREETING:
|
|
1444
|
+
if (code.startsWith("220")) {
|
|
1445
|
+
nextStep();
|
|
1446
|
+
} else {
|
|
1447
|
+
finish(null, "no_greeting");
|
|
1448
|
+
}
|
|
1449
|
+
break;
|
|
1450
|
+
case exports.SMTPStep.EHLO:
|
|
1451
|
+
if (code.startsWith("250")) {
|
|
1452
|
+
const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.STARTTLS);
|
|
1453
|
+
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1454
|
+
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.STARTTLS);
|
|
1455
|
+
executeStep(exports.SMTPStep.STARTTLS);
|
|
1456
|
+
} else {
|
|
1457
|
+
nextStep();
|
|
1458
|
+
}
|
|
1459
|
+
} else {
|
|
1460
|
+
finish(null, "ehlo_failed");
|
|
1461
|
+
}
|
|
1462
|
+
break;
|
|
1463
|
+
case exports.SMTPStep.HELO:
|
|
1464
|
+
if (code.startsWith("250")) {
|
|
1465
|
+
nextStep();
|
|
1466
|
+
} else {
|
|
1467
|
+
finish(null, "helo_failed");
|
|
1468
|
+
}
|
|
1469
|
+
break;
|
|
1470
|
+
case exports.SMTPStep.STARTTLS:
|
|
1471
|
+
if (code.startsWith("220")) {
|
|
1472
|
+
const plainSocket = socket;
|
|
1473
|
+
socket = tls__namespace.connect({
|
|
1474
|
+
...tlsOptions,
|
|
1475
|
+
socket: plainSocket,
|
|
1476
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost
|
|
1477
|
+
}, () => {
|
|
1478
|
+
isTLS = true;
|
|
1479
|
+
log("TLS upgraded");
|
|
1480
|
+
buffer = "";
|
|
1481
|
+
const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.STARTTLS);
|
|
1482
|
+
currentStepIndex = starttlsIndex;
|
|
1483
|
+
nextStep();
|
|
1484
|
+
});
|
|
1485
|
+
socket.on("data", handleData);
|
|
1486
|
+
socket.on("error", () => finish(null, "tls_error"));
|
|
1487
|
+
} else {
|
|
1488
|
+
nextStep();
|
|
1489
|
+
}
|
|
1490
|
+
break;
|
|
1491
|
+
case exports.SMTPStep.MAIL_FROM:
|
|
1492
|
+
if (code.startsWith("250")) {
|
|
1493
|
+
nextStep();
|
|
1494
|
+
} else {
|
|
1495
|
+
finish(null, "mail_from_rejected");
|
|
1496
|
+
}
|
|
1497
|
+
break;
|
|
1498
|
+
case exports.SMTPStep.RCPT_TO:
|
|
1499
|
+
if (code.startsWith("250") || code.startsWith("251")) {
|
|
1500
|
+
finish(true, "valid");
|
|
1501
|
+
} else if (code.startsWith("552") || code.startsWith("452")) {
|
|
1502
|
+
finish(false, "over_quota");
|
|
1503
|
+
} else if (code.startsWith("4")) {
|
|
1504
|
+
finish(null, "temporary_failure");
|
|
1505
|
+
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.VRFY)) {
|
|
1506
|
+
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.VRFY);
|
|
1507
|
+
executeStep(exports.SMTPStep.VRFY);
|
|
1508
|
+
} else {
|
|
1509
|
+
finish(null, "ambiguous");
|
|
1510
|
+
}
|
|
1511
|
+
break;
|
|
1512
|
+
case exports.SMTPStep.VRFY:
|
|
1513
|
+
if (code.startsWith("250") || code.startsWith("252")) {
|
|
1514
|
+
finish(true, "vrfy_valid");
|
|
1515
|
+
} else if (code.startsWith("550")) {
|
|
1516
|
+
finish(false, "vrfy_invalid");
|
|
1517
|
+
} else {
|
|
1518
|
+
finish(null, "vrfy_failed");
|
|
1519
|
+
}
|
|
1520
|
+
break;
|
|
1521
|
+
case exports.SMTPStep.QUIT:
|
|
1522
|
+
if (code.startsWith("221")) {
|
|
1523
|
+
finish(null, "quit_received");
|
|
1524
|
+
}
|
|
1525
|
+
break;
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
const handleData = (data) => {
|
|
1529
|
+
if (resolved)
|
|
1530
|
+
return;
|
|
1531
|
+
buffer += data.toString();
|
|
1532
|
+
let pos;
|
|
1533
|
+
while ((pos = buffer.indexOf("\r\n")) !== -1) {
|
|
1534
|
+
const line = buffer.substring(0, pos);
|
|
1535
|
+
buffer = buffer.substring(pos + 2);
|
|
1536
|
+
processResponse(line.trim());
|
|
1537
|
+
}
|
|
1538
|
+
};
|
|
1539
|
+
if (port < 0 || port > 65535 || !Number.isInteger(port)) {
|
|
1540
|
+
finish(null, "invalid_port");
|
|
1541
|
+
return;
|
|
1542
|
+
}
|
|
1543
|
+
if (implicitTLS) {
|
|
1544
|
+
const connectOptions = {
|
|
1545
|
+
...tlsOptions,
|
|
1546
|
+
port,
|
|
1547
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost
|
|
1548
|
+
};
|
|
1549
|
+
socket = tls__namespace.connect(connectOptions, () => {
|
|
1550
|
+
log(`Connected to ${mxHost}:${port} with TLS`);
|
|
1551
|
+
socket.on("data", handleData);
|
|
1552
|
+
});
|
|
1553
|
+
} else {
|
|
1554
|
+
socket = net__namespace.connect({ host: mxHost, port }, () => {
|
|
1555
|
+
log(`Connected to ${mxHost}:${port}`);
|
|
1556
|
+
socket.on("data", handleData);
|
|
1557
|
+
});
|
|
1558
|
+
}
|
|
1559
|
+
if (activeSequence.steps.length === 0) {
|
|
1560
|
+
finish(true, "sequence_complete");
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
const firstStep = activeSequence.steps[0];
|
|
1564
|
+
if (firstStep !== exports.SMTPStep.GREETING) {
|
|
1565
|
+
executeStep(firstStep);
|
|
1566
|
+
}
|
|
1567
|
+
socket.setTimeout(timeout, () => finish(null, "timeout"));
|
|
1568
|
+
socket.on("error", () => finish(null, "connection_error"));
|
|
1569
|
+
socket.on("close", () => {
|
|
1570
|
+
if (!resolved)
|
|
1571
|
+
finish(null, "connection_closed");
|
|
1572
|
+
});
|
|
1265
1573
|
});
|
|
1266
1574
|
}
|
|
1267
1575
|
|
|
1268
|
-
exports.VerificationErrorCode = void 0;
|
|
1269
|
-
(function(VerificationErrorCode2) {
|
|
1270
|
-
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1271
|
-
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1272
|
-
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1273
|
-
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1274
|
-
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1275
|
-
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1276
|
-
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1277
|
-
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1278
|
-
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1279
|
-
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1280
|
-
})(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
|
|
1281
|
-
|
|
1282
1576
|
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1283
1577
|
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1284
1578
|
if (!emailDomain) {
|
|
@@ -2371,17 +2665,22 @@ async function verifyEmail(params) {
|
|
|
2371
2665
|
domainPort = domainPorts[mxDomain.domain];
|
|
2372
2666
|
}
|
|
2373
2667
|
}
|
|
2374
|
-
const smtpResult = await verifyMailboxSMTP({
|
|
2668
|
+
const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
|
|
2375
2669
|
local,
|
|
2376
2670
|
domain,
|
|
2377
2671
|
mxRecords,
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2672
|
+
options: {
|
|
2673
|
+
cache: params.cache,
|
|
2674
|
+
ports: domainPort ? [domainPort] : void 0,
|
|
2675
|
+
timeout,
|
|
2676
|
+
debug,
|
|
2677
|
+
maxRetries: params.retryAttempts
|
|
2678
|
+
}
|
|
2382
2679
|
});
|
|
2383
2680
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2384
2681
|
result.validSmtp = smtpResult;
|
|
2682
|
+
if (result.metadata)
|
|
2683
|
+
result.metadata.cached = cached;
|
|
2385
2684
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2386
2685
|
}
|
|
2387
2686
|
if (result.validSmtp === false && result.metadata) {
|