@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.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,135 +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
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1356
|
+
const sendCommand = (cmd) => {
|
|
1357
|
+
if (resolved)
|
|
1358
|
+
return;
|
|
1359
|
+
log(`\u2192 ${cmd}`);
|
|
1360
|
+
socket === null || socket === void 0 ? void 0 : socket.write(`${cmd}\r
|
|
1361
|
+
`);
|
|
1362
|
+
};
|
|
1363
|
+
const nextStep = () => {
|
|
1364
|
+
currentStepIndex++;
|
|
1365
|
+
if (currentStepIndex >= activeSequence.steps.length) {
|
|
1366
|
+
finish(true, "sequence_complete");
|
|
1367
|
+
return;
|
|
1231
1368
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
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;
|
|
1235
1402
|
}
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
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;
|
|
1239
1412
|
}
|
|
1240
|
-
if (
|
|
1413
|
+
if (isOverQuota(response)) {
|
|
1414
|
+
finish(false, "over_quota");
|
|
1241
1415
|
return;
|
|
1242
|
-
if (messages.length > 0) {
|
|
1243
|
-
const message = messages.shift();
|
|
1244
|
-
log("[verifyMailboxSMTP] writing message", message);
|
|
1245
|
-
return socket.write(`${message}\r
|
|
1246
|
-
`);
|
|
1247
1416
|
}
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
log("[verifyMailboxSMTP] error in socket", err);
|
|
1252
|
-
ret(null);
|
|
1253
|
-
});
|
|
1254
|
-
socket.on("close", (err) => {
|
|
1255
|
-
if (!resolved) {
|
|
1256
|
-
log("[verifyMailboxSMTP] close socket", err);
|
|
1257
|
-
ret(null);
|
|
1417
|
+
if (isInvalidMailboxError(response)) {
|
|
1418
|
+
finish(false, "not_found");
|
|
1419
|
+
return;
|
|
1258
1420
|
}
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
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;
|
|
1269
1436
|
}
|
|
1270
|
-
|
|
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
|
+
});
|
|
1271
1573
|
});
|
|
1272
1574
|
}
|
|
1273
1575
|
|
|
1274
|
-
exports.VerificationErrorCode = void 0;
|
|
1275
|
-
(function(VerificationErrorCode2) {
|
|
1276
|
-
VerificationErrorCode2["INVALID_FORMAT"] = "INVALID_FORMAT";
|
|
1277
|
-
VerificationErrorCode2["INVALID_DOMAIN"] = "INVALID_DOMAIN";
|
|
1278
|
-
VerificationErrorCode2["NO_MX_RECORDS"] = "NO_MX_RECORDS";
|
|
1279
|
-
VerificationErrorCode2["SMTP_CONNECTION_FAILED"] = "SMTP_CONNECTION_FAILED";
|
|
1280
|
-
VerificationErrorCode2["SMTP_TIMEOUT"] = "SMTP_TIMEOUT";
|
|
1281
|
-
VerificationErrorCode2["MAILBOX_NOT_FOUND"] = "MAILBOX_NOT_FOUND";
|
|
1282
|
-
VerificationErrorCode2["MAILBOX_FULL"] = "MAILBOX_FULL";
|
|
1283
|
-
VerificationErrorCode2["NETWORK_ERROR"] = "NETWORK_ERROR";
|
|
1284
|
-
VerificationErrorCode2["DISPOSABLE_EMAIL"] = "DISPOSABLE_EMAIL";
|
|
1285
|
-
VerificationErrorCode2["FREE_EMAIL_PROVIDER"] = "FREE_EMAIL_PROVIDER";
|
|
1286
|
-
})(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
|
|
1287
|
-
|
|
1288
1576
|
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1289
1577
|
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1290
1578
|
if (!emailDomain) {
|
|
@@ -2377,17 +2665,22 @@ async function verifyEmail(params) {
|
|
|
2377
2665
|
domainPort = domainPorts[mxDomain.domain];
|
|
2378
2666
|
}
|
|
2379
2667
|
}
|
|
2380
|
-
const smtpResult = await verifyMailboxSMTP({
|
|
2668
|
+
const { result: smtpResult, cached, portCached, port } = await verifyMailboxSMTP({
|
|
2381
2669
|
local,
|
|
2382
2670
|
domain,
|
|
2383
2671
|
mxRecords,
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2672
|
+
options: {
|
|
2673
|
+
cache: params.cache,
|
|
2674
|
+
ports: domainPort ? [domainPort] : void 0,
|
|
2675
|
+
timeout,
|
|
2676
|
+
debug,
|
|
2677
|
+
maxRetries: params.retryAttempts
|
|
2678
|
+
}
|
|
2388
2679
|
});
|
|
2389
2680
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2390
2681
|
result.validSmtp = smtpResult;
|
|
2682
|
+
if (result.metadata)
|
|
2683
|
+
result.metadata.cached = cached;
|
|
2391
2684
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2392
2685
|
}
|
|
2393
2686
|
if (result.validSmtp === false && result.metadata) {
|