@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.esm.js
CHANGED
|
@@ -3,6 +3,7 @@ import { lru } from 'tiny-lru';
|
|
|
3
3
|
import { promises } from 'node:dns';
|
|
4
4
|
import { stringSimilarity } from 'string-similarity-js';
|
|
5
5
|
import * as net from 'node:net';
|
|
6
|
+
import * as tls from 'node:tls';
|
|
6
7
|
|
|
7
8
|
class LRUAdapter {
|
|
8
9
|
constructor(maxSize = 1e3, ttlMs = 36e5) {
|
|
@@ -52,6 +53,8 @@ const DEFAULT_CACHE_OPTIONS = {
|
|
|
52
53
|
// 24 hours
|
|
53
54
|
smtp: 18e5,
|
|
54
55
|
// 30 minutes
|
|
56
|
+
smtpPort: 36e5,
|
|
57
|
+
// 1 hour
|
|
55
58
|
domainSuggestion: 864e5,
|
|
56
59
|
// 24 hours
|
|
57
60
|
whois: 36e5
|
|
@@ -63,6 +66,7 @@ const DEFAULT_CACHE_OPTIONS = {
|
|
|
63
66
|
free: 1e3,
|
|
64
67
|
domainValid: 1e3,
|
|
65
68
|
smtp: 500,
|
|
69
|
+
smtpPort: 500,
|
|
66
70
|
domainSuggestion: 1e3,
|
|
67
71
|
whois: 200
|
|
68
72
|
}
|
|
@@ -76,6 +80,7 @@ function getDefaultCache() {
|
|
|
76
80
|
free: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.free, DEFAULT_CACHE_OPTIONS.ttl.free),
|
|
77
81
|
domainValid: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainValid, DEFAULT_CACHE_OPTIONS.ttl.domainValid),
|
|
78
82
|
smtp: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtp, DEFAULT_CACHE_OPTIONS.ttl.smtp),
|
|
83
|
+
smtpPort: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.smtpPort, DEFAULT_CACHE_OPTIONS.ttl.smtpPort),
|
|
79
84
|
domainSuggestion: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.domainSuggestion, DEFAULT_CACHE_OPTIONS.ttl.domainSuggestion),
|
|
80
85
|
whois: new LRUAdapter(DEFAULT_CACHE_OPTIONS.maxSize.whois, DEFAULT_CACHE_OPTIONS.ttl.whois)
|
|
81
86
|
};
|
|
@@ -92,6 +97,7 @@ function clearDefaultCache() {
|
|
|
92
97
|
defaultCacheInstance.free.clear();
|
|
93
98
|
defaultCacheInstance.domainValid.clear();
|
|
94
99
|
defaultCacheInstance.smtp.clear();
|
|
100
|
+
defaultCacheInstance.smtpPort.clear();
|
|
95
101
|
defaultCacheInstance.domainSuggestion.clear();
|
|
96
102
|
defaultCacheInstance.whois.clear();
|
|
97
103
|
}
|
|
@@ -1126,6 +1132,40 @@ function detectName(email) {
|
|
|
1126
1132
|
return detectNameFromEmail({ email });
|
|
1127
1133
|
}
|
|
1128
1134
|
|
|
1135
|
+
var VerificationErrorCode;
|
|
1136
|
+
(function(VerificationErrorCode2) {
|
|
1137
|
+
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1138
|
+
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1139
|
+
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1140
|
+
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1141
|
+
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1142
|
+
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1143
|
+
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1144
|
+
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1145
|
+
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1146
|
+
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1147
|
+
})(VerificationErrorCode || (VerificationErrorCode = {}));
|
|
1148
|
+
var SMTPStep;
|
|
1149
|
+
(function(SMTPStep2) {
|
|
1150
|
+
SMTPStep2["GREETING"] = "GREETING";
|
|
1151
|
+
SMTPStep2["EHLO"] = "EHLO";
|
|
1152
|
+
SMTPStep2["HELO"] = "HELO";
|
|
1153
|
+
SMTPStep2["STARTTLS"] = "STARTTLS";
|
|
1154
|
+
SMTPStep2["MAIL_FROM"] = "MAIL_FROM";
|
|
1155
|
+
SMTPStep2["RCPT_TO"] = "RCPT_TO";
|
|
1156
|
+
SMTPStep2["VRFY"] = "VRFY";
|
|
1157
|
+
SMTPStep2["QUIT"] = "QUIT";
|
|
1158
|
+
})(SMTPStep || (SMTPStep = {}));
|
|
1159
|
+
|
|
1160
|
+
function isIPAddress(host) {
|
|
1161
|
+
const ipv4Regex = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
1162
|
+
if (ipv4Regex.test(host)) {
|
|
1163
|
+
const octets = host.split(".");
|
|
1164
|
+
return octets.every((octet) => parseInt(octet, 10) <= 255);
|
|
1165
|
+
}
|
|
1166
|
+
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}:)$/;
|
|
1167
|
+
return ipv6Regex.test(host);
|
|
1168
|
+
}
|
|
1129
1169
|
function isOverQuota(smtpReply) {
|
|
1130
1170
|
return Boolean(smtpReply && /(over quota)/gi.test(smtpReply));
|
|
1131
1171
|
}
|
|
@@ -1135,129 +1175,382 @@ function isInvalidMailboxError(smtpReply) {
|
|
|
1135
1175
|
function isMultilineGreet(smtpReply) {
|
|
1136
1176
|
return Boolean(smtpReply && /^(250|220)-/.test(smtpReply));
|
|
1137
1177
|
}
|
|
1178
|
+
const DEFAULT_PORTS = [25, 587, 465];
|
|
1179
|
+
const DEFAULT_TIMEOUT = 3e3;
|
|
1180
|
+
const DEFAULT_MAX_RETRIES = 1;
|
|
1181
|
+
const PORT_CONFIGS = {
|
|
1182
|
+
25: { tls: false, starttls: true },
|
|
1183
|
+
587: { tls: false, starttls: true },
|
|
1184
|
+
465: { tls: true, starttls: false }
|
|
1185
|
+
};
|
|
1138
1186
|
async function verifyMailboxSMTP(params) {
|
|
1139
|
-
const { local, domain, mxRecords = [],
|
|
1140
|
-
const
|
|
1187
|
+
const { local, domain, mxRecords = [], options = {} } = params;
|
|
1188
|
+
const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
|
|
1189
|
+
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1141
1190
|
};
|
|
1142
1191
|
if (!mxRecords || mxRecords.length === 0) {
|
|
1143
|
-
|
|
1192
|
+
log("No MX records found");
|
|
1193
|
+
return { result: false, cached: false, port: 0, portCached: false };
|
|
1194
|
+
}
|
|
1195
|
+
const mxHost = mxRecords[0];
|
|
1196
|
+
log(`Verifying ${local}@${domain} via ${mxHost}`);
|
|
1197
|
+
const smtpCacheStore = cache ? getCacheStore(cache, "smtp") : null;
|
|
1198
|
+
if (smtpCacheStore) {
|
|
1199
|
+
let cachedResult;
|
|
1200
|
+
try {
|
|
1201
|
+
cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
|
|
1202
|
+
if (cachedResult !== void 0) {
|
|
1203
|
+
log(`Using cached SMTP result: ${cachedResult}`);
|
|
1204
|
+
return {
|
|
1205
|
+
result: typeof cachedResult === "boolean" ? cachedResult : null,
|
|
1206
|
+
cached: true,
|
|
1207
|
+
port: 0,
|
|
1208
|
+
portCached: false
|
|
1209
|
+
};
|
|
1210
|
+
}
|
|
1211
|
+
} catch (_error) {
|
|
1212
|
+
cachedResult = void 0;
|
|
1213
|
+
}
|
|
1144
1214
|
}
|
|
1145
|
-
const
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
port,
|
|
1153
|
-
timeout,
|
|
1154
|
-
log,
|
|
1155
|
-
attempt
|
|
1156
|
-
});
|
|
1157
|
-
if (result !== null) {
|
|
1158
|
-
return result;
|
|
1215
|
+
const smtpPortCacheStore = cache ? getCacheStore(cache, "smtpPort") : null;
|
|
1216
|
+
if (smtpPortCacheStore) {
|
|
1217
|
+
let cachedPort;
|
|
1218
|
+
try {
|
|
1219
|
+
cachedPort = await smtpPortCacheStore.get(mxHost);
|
|
1220
|
+
} catch (_error) {
|
|
1221
|
+
cachedPort = null;
|
|
1159
1222
|
}
|
|
1160
|
-
if (
|
|
1161
|
-
|
|
1223
|
+
if (cachedPort) {
|
|
1224
|
+
log(`Using cached port: ${cachedPort}`);
|
|
1225
|
+
const result = await testSMTPConnection({
|
|
1226
|
+
mxHost,
|
|
1227
|
+
port: cachedPort,
|
|
1228
|
+
local,
|
|
1229
|
+
domain,
|
|
1230
|
+
timeout,
|
|
1231
|
+
tlsConfig,
|
|
1232
|
+
hostname,
|
|
1233
|
+
useVRFY,
|
|
1234
|
+
sequence,
|
|
1235
|
+
log
|
|
1236
|
+
});
|
|
1237
|
+
if (smtpCacheStore && result !== void 0) {
|
|
1238
|
+
try {
|
|
1239
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
|
|
1240
|
+
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1241
|
+
} catch (_error) {
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
return { result, cached: false, port: cachedPort, portCached: true };
|
|
1162
1245
|
}
|
|
1163
1246
|
}
|
|
1164
|
-
|
|
1247
|
+
for (const port of ports) {
|
|
1248
|
+
log(`Testing port ${port}`);
|
|
1249
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1250
|
+
if (attempt > 0) {
|
|
1251
|
+
const delay = Math.min(1e3 * 2 ** (attempt - 1), 5e3);
|
|
1252
|
+
log(`Retry ${attempt + 1}, waiting ${delay}ms`);
|
|
1253
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1254
|
+
}
|
|
1255
|
+
const result = await testSMTPConnection({
|
|
1256
|
+
mxHost,
|
|
1257
|
+
port,
|
|
1258
|
+
local,
|
|
1259
|
+
domain,
|
|
1260
|
+
timeout,
|
|
1261
|
+
tlsConfig,
|
|
1262
|
+
hostname,
|
|
1263
|
+
useVRFY,
|
|
1264
|
+
sequence,
|
|
1265
|
+
log
|
|
1266
|
+
});
|
|
1267
|
+
if (smtpCacheStore && result !== void 0) {
|
|
1268
|
+
try {
|
|
1269
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, result);
|
|
1270
|
+
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1271
|
+
} catch (_error) {
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
if (result !== null) {
|
|
1275
|
+
if (smtpPortCacheStore) {
|
|
1276
|
+
try {
|
|
1277
|
+
await smtpPortCacheStore.set(mxHost, port);
|
|
1278
|
+
log(`Cached port ${port} for ${mxHost}`);
|
|
1279
|
+
} catch (_error) {
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
return { result, cached: false, port, portCached: false };
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
log("All ports failed");
|
|
1287
|
+
return { result: null, cached: false, port: 0, portCached: false };
|
|
1165
1288
|
}
|
|
1166
|
-
async function
|
|
1167
|
-
const {
|
|
1289
|
+
async function testSMTPConnection(params) {
|
|
1290
|
+
const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
|
|
1291
|
+
const portConfig = PORT_CONFIGS[port] || { tls: false, starttls: false };
|
|
1292
|
+
const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
|
|
1293
|
+
const implicitTLS = portConfig.tls;
|
|
1294
|
+
const defaultSequence = {
|
|
1295
|
+
steps: [SMTPStep.GREETING, SMTPStep.EHLO, SMTPStep.MAIL_FROM, SMTPStep.RCPT_TO]
|
|
1296
|
+
};
|
|
1297
|
+
const activeSequence = sequence || defaultSequence;
|
|
1298
|
+
if (port === 25) {
|
|
1299
|
+
activeSequence.steps = activeSequence.steps.map((step) => step === SMTPStep.EHLO ? SMTPStep.HELO : step);
|
|
1300
|
+
}
|
|
1301
|
+
const tlsOptions = {
|
|
1302
|
+
host: mxHost,
|
|
1303
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost,
|
|
1304
|
+
// Don't set servername for IP addresses
|
|
1305
|
+
rejectUnauthorized: false,
|
|
1306
|
+
minVersion: "TLSv1.2",
|
|
1307
|
+
...typeof tlsConfig === "object" ? tlsConfig : {}
|
|
1308
|
+
};
|
|
1168
1309
|
return new Promise((resolve) => {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
});
|
|
1174
|
-
let resTimeout = null;
|
|
1310
|
+
let socket;
|
|
1311
|
+
let buffer = "";
|
|
1312
|
+
let isTLS = implicitTLS;
|
|
1313
|
+
let currentStepIndex = 0;
|
|
1175
1314
|
let resolved = false;
|
|
1176
|
-
let
|
|
1315
|
+
let supportsSTARTTLS = false;
|
|
1316
|
+
let supportsVRFY = false;
|
|
1177
1317
|
const cleanup = () => {
|
|
1178
|
-
if (cleaned)
|
|
1179
|
-
return;
|
|
1180
|
-
cleaned = true;
|
|
1181
|
-
if (resTimeout) {
|
|
1182
|
-
clearTimeout(resTimeout);
|
|
1183
|
-
resTimeout = null;
|
|
1184
|
-
}
|
|
1185
|
-
if (socket && !socket.destroyed) {
|
|
1186
|
-
socket.removeAllListeners();
|
|
1187
|
-
socket.destroy();
|
|
1188
|
-
}
|
|
1189
|
-
};
|
|
1190
|
-
const ret = (result) => {
|
|
1191
1318
|
if (resolved)
|
|
1192
1319
|
return;
|
|
1193
1320
|
resolved = true;
|
|
1194
|
-
|
|
1195
|
-
log("[verifyMailboxSMTP] closing socket");
|
|
1321
|
+
try {
|
|
1196
1322
|
socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
|
|
1197
|
-
|
|
1323
|
+
} catch {
|
|
1198
1324
|
}
|
|
1325
|
+
setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
|
|
1326
|
+
};
|
|
1327
|
+
const finish = (result, reason) => {
|
|
1328
|
+
if (resolved)
|
|
1329
|
+
return;
|
|
1330
|
+
log(`${port}: ${reason || (result ? "valid" : "invalid")}`);
|
|
1199
1331
|
cleanup();
|
|
1200
1332
|
resolve(result);
|
|
1201
1333
|
};
|
|
1202
|
-
const
|
|
1203
|
-
|
|
1204
|
-
socket.on("data", (data) => {
|
|
1205
|
-
const dataString = String(data);
|
|
1206
|
-
log("[verifyMailboxSMTP] got data", dataString);
|
|
1207
|
-
if (isInvalidMailboxError(dataString))
|
|
1208
|
-
return ret(false);
|
|
1209
|
-
if (isOverQuota(dataString))
|
|
1210
|
-
return ret(false);
|
|
1211
|
-
if (!dataString.includes("220") && !dataString.includes("250"))
|
|
1212
|
-
return ret(null);
|
|
1213
|
-
if (isMultilineGreet(dataString))
|
|
1334
|
+
const sendCommand = (cmd) => {
|
|
1335
|
+
if (resolved)
|
|
1214
1336
|
return;
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
log("[verifyMailboxSMTP] writing message", message);
|
|
1218
|
-
return socket.write(`${message}\r
|
|
1337
|
+
log(`\u2192 ${cmd}`);
|
|
1338
|
+
socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
|
|
1219
1339
|
`);
|
|
1340
|
+
};
|
|
1341
|
+
const nextStep = () => {
|
|
1342
|
+
currentStepIndex++;
|
|
1343
|
+
if (currentStepIndex >= activeSequence.steps.length) {
|
|
1344
|
+
finish(true, "sequence_complete");
|
|
1345
|
+
return;
|
|
1220
1346
|
}
|
|
1221
|
-
|
|
1222
|
-
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1347
|
+
executeStep(activeSequence.steps[currentStepIndex]);
|
|
1348
|
+
};
|
|
1349
|
+
const executeStep = (step) => {
|
|
1350
|
+
if (resolved)
|
|
1351
|
+
return;
|
|
1352
|
+
switch (step) {
|
|
1353
|
+
case SMTPStep.EHLO:
|
|
1354
|
+
sendCommand(`EHLO ${hostname}`);
|
|
1355
|
+
break;
|
|
1356
|
+
case SMTPStep.HELO:
|
|
1357
|
+
sendCommand(`HELO ${domain}`);
|
|
1358
|
+
break;
|
|
1359
|
+
case SMTPStep.GREETING:
|
|
1360
|
+
break;
|
|
1361
|
+
case SMTPStep.STARTTLS:
|
|
1362
|
+
sendCommand("STARTTLS");
|
|
1363
|
+
break;
|
|
1364
|
+
case SMTPStep.MAIL_FROM: {
|
|
1365
|
+
const from = activeSequence.from || "<>";
|
|
1366
|
+
sendCommand(`MAIL FROM:${from}`);
|
|
1367
|
+
break;
|
|
1368
|
+
}
|
|
1369
|
+
case SMTPStep.RCPT_TO:
|
|
1370
|
+
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1371
|
+
break;
|
|
1372
|
+
case SMTPStep.VRFY: {
|
|
1373
|
+
const vrfyTarget = activeSequence.vrfyTarget || local;
|
|
1374
|
+
sendCommand(`VRFY ${vrfyTarget}`);
|
|
1375
|
+
break;
|
|
1376
|
+
}
|
|
1377
|
+
case SMTPStep.QUIT:
|
|
1378
|
+
sendCommand("QUIT");
|
|
1379
|
+
break;
|
|
1231
1380
|
}
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
log(
|
|
1239
|
-
if (
|
|
1240
|
-
|
|
1241
|
-
ret(null);
|
|
1381
|
+
};
|
|
1382
|
+
const processResponse = (response) => {
|
|
1383
|
+
if (resolved)
|
|
1384
|
+
return;
|
|
1385
|
+
const code = response.substring(0, 3);
|
|
1386
|
+
const isMultiline = response.length > 3 && response[3] === "-";
|
|
1387
|
+
log(`\u2190 ${response}`);
|
|
1388
|
+
if (isMultilineGreet(response)) {
|
|
1389
|
+
return;
|
|
1242
1390
|
}
|
|
1243
|
-
|
|
1391
|
+
if (isOverQuota(response)) {
|
|
1392
|
+
finish(false, "over_quota");
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1395
|
+
if (isInvalidMailboxError(response)) {
|
|
1396
|
+
finish(false, "not_found");
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1399
|
+
if (isMultiline) {
|
|
1400
|
+
const currentStep2 = activeSequence.steps[currentStepIndex];
|
|
1401
|
+
if (currentStep2 === SMTPStep.EHLO && code === "250") {
|
|
1402
|
+
const upper = response.toUpperCase();
|
|
1403
|
+
if (upper.includes("STARTTLS"))
|
|
1404
|
+
supportsSTARTTLS = true;
|
|
1405
|
+
if (upper.includes("VRFY"))
|
|
1406
|
+
supportsVRFY = true;
|
|
1407
|
+
}
|
|
1408
|
+
if (currentStep2 === SMTPStep.HELO && code === "250") {
|
|
1409
|
+
const upper = response.toUpperCase();
|
|
1410
|
+
if (upper.includes("VRFY"))
|
|
1411
|
+
supportsVRFY = true;
|
|
1412
|
+
}
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
if (!response.includes("220") && !response.includes("250") && !response.includes("550") && !response.includes("552")) {
|
|
1416
|
+
finish(null, "unrecognized_response");
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
const currentStep = activeSequence.steps[currentStepIndex];
|
|
1420
|
+
switch (currentStep) {
|
|
1421
|
+
case SMTPStep.GREETING:
|
|
1422
|
+
if (code.startsWith("220")) {
|
|
1423
|
+
nextStep();
|
|
1424
|
+
} else {
|
|
1425
|
+
finish(null, "no_greeting");
|
|
1426
|
+
}
|
|
1427
|
+
break;
|
|
1428
|
+
case SMTPStep.EHLO:
|
|
1429
|
+
if (code.startsWith("250")) {
|
|
1430
|
+
const hasSTARTTLS = activeSequence.steps.includes(SMTPStep.STARTTLS);
|
|
1431
|
+
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1432
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.STARTTLS);
|
|
1433
|
+
executeStep(SMTPStep.STARTTLS);
|
|
1434
|
+
} else {
|
|
1435
|
+
nextStep();
|
|
1436
|
+
}
|
|
1437
|
+
} else {
|
|
1438
|
+
finish(null, "ehlo_failed");
|
|
1439
|
+
}
|
|
1440
|
+
break;
|
|
1441
|
+
case SMTPStep.HELO:
|
|
1442
|
+
if (code.startsWith("250")) {
|
|
1443
|
+
nextStep();
|
|
1444
|
+
} else {
|
|
1445
|
+
finish(null, "helo_failed");
|
|
1446
|
+
}
|
|
1447
|
+
break;
|
|
1448
|
+
case SMTPStep.STARTTLS:
|
|
1449
|
+
if (code.startsWith("220")) {
|
|
1450
|
+
const plainSocket = socket;
|
|
1451
|
+
socket = tls.connect({
|
|
1452
|
+
...tlsOptions,
|
|
1453
|
+
socket: plainSocket,
|
|
1454
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost
|
|
1455
|
+
}, () => {
|
|
1456
|
+
isTLS = true;
|
|
1457
|
+
log("TLS upgraded");
|
|
1458
|
+
buffer = "";
|
|
1459
|
+
const starttlsIndex = activeSequence.steps.indexOf(SMTPStep.STARTTLS);
|
|
1460
|
+
currentStepIndex = starttlsIndex;
|
|
1461
|
+
nextStep();
|
|
1462
|
+
});
|
|
1463
|
+
socket.on("data", handleData);
|
|
1464
|
+
socket.on("error", () => finish(null, "tls_error"));
|
|
1465
|
+
} else {
|
|
1466
|
+
nextStep();
|
|
1467
|
+
}
|
|
1468
|
+
break;
|
|
1469
|
+
case SMTPStep.MAIL_FROM:
|
|
1470
|
+
if (code.startsWith("250")) {
|
|
1471
|
+
nextStep();
|
|
1472
|
+
} else {
|
|
1473
|
+
finish(null, "mail_from_rejected");
|
|
1474
|
+
}
|
|
1475
|
+
break;
|
|
1476
|
+
case SMTPStep.RCPT_TO:
|
|
1477
|
+
if (code.startsWith("250") || code.startsWith("251")) {
|
|
1478
|
+
finish(true, "valid");
|
|
1479
|
+
} else if (code.startsWith("552") || code.startsWith("452")) {
|
|
1480
|
+
finish(false, "over_quota");
|
|
1481
|
+
} else if (code.startsWith("4")) {
|
|
1482
|
+
finish(null, "temporary_failure");
|
|
1483
|
+
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(SMTPStep.VRFY)) {
|
|
1484
|
+
currentStepIndex = activeSequence.steps.indexOf(SMTPStep.VRFY);
|
|
1485
|
+
executeStep(SMTPStep.VRFY);
|
|
1486
|
+
} else {
|
|
1487
|
+
finish(null, "ambiguous");
|
|
1488
|
+
}
|
|
1489
|
+
break;
|
|
1490
|
+
case SMTPStep.VRFY:
|
|
1491
|
+
if (code.startsWith("250") || code.startsWith("252")) {
|
|
1492
|
+
finish(true, "vrfy_valid");
|
|
1493
|
+
} else if (code.startsWith("550")) {
|
|
1494
|
+
finish(false, "vrfy_invalid");
|
|
1495
|
+
} else {
|
|
1496
|
+
finish(null, "vrfy_failed");
|
|
1497
|
+
}
|
|
1498
|
+
break;
|
|
1499
|
+
case SMTPStep.QUIT:
|
|
1500
|
+
if (code.startsWith("221")) {
|
|
1501
|
+
finish(null, "quit_received");
|
|
1502
|
+
}
|
|
1503
|
+
break;
|
|
1504
|
+
}
|
|
1505
|
+
};
|
|
1506
|
+
const handleData = (data) => {
|
|
1507
|
+
if (resolved)
|
|
1508
|
+
return;
|
|
1509
|
+
buffer += data.toString();
|
|
1510
|
+
let pos;
|
|
1511
|
+
while ((pos = buffer.indexOf("\r\n")) !== -1) {
|
|
1512
|
+
const line = buffer.substring(0, pos);
|
|
1513
|
+
buffer = buffer.substring(pos + 2);
|
|
1514
|
+
processResponse(line.trim());
|
|
1515
|
+
}
|
|
1516
|
+
};
|
|
1517
|
+
if (port < 0 || port > 65535 || !Number.isInteger(port)) {
|
|
1518
|
+
finish(null, "invalid_port");
|
|
1519
|
+
return;
|
|
1520
|
+
}
|
|
1521
|
+
if (implicitTLS) {
|
|
1522
|
+
const connectOptions = {
|
|
1523
|
+
...tlsOptions,
|
|
1524
|
+
port,
|
|
1525
|
+
servername: isIPAddress(mxHost) ? void 0 : mxHost
|
|
1526
|
+
};
|
|
1527
|
+
socket = tls.connect(connectOptions, () => {
|
|
1528
|
+
log(`Connected to ${mxHost}:${port} with TLS`);
|
|
1529
|
+
socket.on("data", handleData);
|
|
1530
|
+
});
|
|
1531
|
+
} else {
|
|
1532
|
+
socket = net.connect({ host: mxHost, port }, () => {
|
|
1533
|
+
log(`Connected to ${mxHost}:${port}`);
|
|
1534
|
+
socket.on("data", handleData);
|
|
1535
|
+
});
|
|
1536
|
+
}
|
|
1537
|
+
if (activeSequence.steps.length === 0) {
|
|
1538
|
+
finish(true, "sequence_complete");
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
const firstStep = activeSequence.steps[0];
|
|
1542
|
+
if (firstStep !== SMTPStep.GREETING) {
|
|
1543
|
+
executeStep(firstStep);
|
|
1544
|
+
}
|
|
1545
|
+
socket.setTimeout(timeout, () => finish(null, "timeout"));
|
|
1546
|
+
socket.on("error", () => finish(null, "connection_error"));
|
|
1547
|
+
socket.on("close", () => {
|
|
1548
|
+
if (!resolved)
|
|
1549
|
+
finish(null, "connection_closed");
|
|
1550
|
+
});
|
|
1244
1551
|
});
|
|
1245
1552
|
}
|
|
1246
1553
|
|
|
1247
|
-
var VerificationErrorCode;
|
|
1248
|
-
(function(VerificationErrorCode2) {
|
|
1249
|
-
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1250
|
-
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1251
|
-
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1252
|
-
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1253
|
-
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1254
|
-
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1255
|
-
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1256
|
-
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1257
|
-
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1258
|
-
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1259
|
-
})(VerificationErrorCode || (VerificationErrorCode = {}));
|
|
1260
|
-
|
|
1261
1554
|
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1262
1555
|
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1263
1556
|
if (!emailDomain) {
|
|
@@ -2350,17 +2643,22 @@ async function verifyEmail(params) {
|
|
|
2350
2643
|
domainPort = domainPorts[mxDomain.domain];
|
|
2351
2644
|
}
|
|
2352
2645
|
}
|
|
2353
|
-
const smtpResult = await verifyMailboxSMTP({
|
|
2646
|
+
const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
|
|
2354
2647
|
local,
|
|
2355
2648
|
domain,
|
|
2356
2649
|
mxRecords,
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
|
|
2650
|
+
options: {
|
|
2651
|
+
cache: params.cache,
|
|
2652
|
+
ports: domainPort ? [domainPort] : void 0,
|
|
2653
|
+
timeout,
|
|
2654
|
+
debug,
|
|
2655
|
+
maxRetries: params.retryAttempts
|
|
2656
|
+
}
|
|
2361
2657
|
});
|
|
2362
2658
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2363
2659
|
result.validSmtp = smtpResult;
|
|
2660
|
+
if (result.metadata)
|
|
2661
|
+
result.metadata.cached = cached;
|
|
2364
2662
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2365
2663
|
}
|
|
2366
2664
|
if (result.validSmtp === false && result.metadata) {
|
|
@@ -2385,5 +2683,5 @@ async function verifyEmail(params) {
|
|
|
2385
2683
|
return result;
|
|
2386
2684
|
}
|
|
2387
2685
|
|
|
2388
|
-
export { COMMON_EMAIL_DOMAINS, DEFAULT_CACHE_OPTIONS, LRUAdapter, RedisAdapter, VerificationErrorCode, cleanNameForAlgrothin, clearDefaultCache, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgrothin, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, resetDefaultCache, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2686
|
+
export { COMMON_EMAIL_DOMAINS, DEFAULT_CACHE_OPTIONS, LRUAdapter, RedisAdapter, SMTPStep, VerificationErrorCode, cleanNameForAlgrothin, clearDefaultCache, defaultDomainSuggestionMethod, defaultNameDetectionMethod, detectName, detectNameForAlgrothin, detectNameFromEmail, domainPorts, getCacheStore, getDefaultCache, getDomainAge, getDomainRegistrationStatus, getDomainSimilarity, isCommonDomain, isDisposableEmail, isFreeEmail, isValidEmail, isValidEmailDomain, resetDefaultCache, suggestDomain, suggestEmailDomain, verifyEmail, verifyEmailBatch };
|
|
2389
2687
|
//# sourceMappingURL=index.esm.js.map
|