@emailcheck/email-validator-js 2.14.2 → 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 +398 -106
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +398 -105
- 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,135 +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
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1334
|
+
const sendCommand = (cmd) => {
|
|
1335
|
+
if (resolved)
|
|
1336
|
+
return;
|
|
1337
|
+
log(`\u2192 ${cmd}`);
|
|
1338
|
+
socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
|
|
1339
|
+
`);
|
|
1340
|
+
};
|
|
1341
|
+
const nextStep = () => {
|
|
1342
|
+
currentStepIndex++;
|
|
1343
|
+
if (currentStepIndex >= activeSequence.steps.length) {
|
|
1344
|
+
finish(true, "sequence_complete");
|
|
1345
|
+
return;
|
|
1210
1346
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
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;
|
|
1214
1380
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
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;
|
|
1218
1390
|
}
|
|
1219
|
-
if (
|
|
1391
|
+
if (isOverQuota(response)) {
|
|
1392
|
+
finish(false, "over_quota");
|
|
1220
1393
|
return;
|
|
1221
|
-
if (messages.length > 0) {
|
|
1222
|
-
const message = messages.shift();
|
|
1223
|
-
log("[verifyMailboxSMTP] writing message", message);
|
|
1224
|
-
return socket.write(`${message}\r
|
|
1225
|
-
`);
|
|
1226
1394
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
log("[verifyMailboxSMTP] error in socket", err);
|
|
1231
|
-
ret(null);
|
|
1232
|
-
});
|
|
1233
|
-
socket.on("close", (err) => {
|
|
1234
|
-
if (!resolved) {
|
|
1235
|
-
log("[verifyMailboxSMTP] close socket", err);
|
|
1236
|
-
ret(null);
|
|
1395
|
+
if (isInvalidMailboxError(response)) {
|
|
1396
|
+
finish(false, "not_found");
|
|
1397
|
+
return;
|
|
1237
1398
|
}
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
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;
|
|
1248
1414
|
}
|
|
1249
|
-
|
|
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
|
+
});
|
|
1250
1551
|
});
|
|
1251
1552
|
}
|
|
1252
1553
|
|
|
1253
|
-
var VerificationErrorCode;
|
|
1254
|
-
(function(VerificationErrorCode2) {
|
|
1255
|
-
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1256
|
-
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1257
|
-
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1258
|
-
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1259
|
-
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1260
|
-
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1261
|
-
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1262
|
-
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1263
|
-
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1264
|
-
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1265
|
-
})(VerificationErrorCode || (VerificationErrorCode = {}));
|
|
1266
|
-
|
|
1267
1554
|
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1268
1555
|
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1269
1556
|
if (!emailDomain) {
|
|
@@ -2356,17 +2643,22 @@ async function verifyEmail(params) {
|
|
|
2356
2643
|
domainPort = domainPorts[mxDomain.domain];
|
|
2357
2644
|
}
|
|
2358
2645
|
}
|
|
2359
|
-
const smtpResult = await verifyMailboxSMTP({
|
|
2646
|
+
const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
|
|
2360
2647
|
local,
|
|
2361
2648
|
domain,
|
|
2362
2649
|
mxRecords,
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2650
|
+
options: {
|
|
2651
|
+
cache: params.cache,
|
|
2652
|
+
ports: domainPort ? [domainPort] : void 0,
|
|
2653
|
+
timeout,
|
|
2654
|
+
debug,
|
|
2655
|
+
maxRetries: params.retryAttempts
|
|
2656
|
+
}
|
|
2367
2657
|
});
|
|
2368
2658
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2369
2659
|
result.validSmtp = smtpResult;
|
|
2660
|
+
if (result.metadata)
|
|
2661
|
+
result.metadata.cached = cached;
|
|
2370
2662
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2371
2663
|
}
|
|
2372
2664
|
if (result.validSmtp === false && result.metadata) {
|
|
@@ -2391,5 +2683,5 @@ async function verifyEmail(params) {
|
|
|
2391
2683
|
return result;
|
|
2392
2684
|
}
|
|
2393
2685
|
|
|
2394
|
-
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 };
|
|
2395
2687
|
//# sourceMappingURL=index.esm.js.map
|