@emailcheck/email-validator-js 3.0.1-beta.0 → 3.0.1-beta.1
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 +176 -24
- package/dist/adapters/lru-adapter.d.ts +2 -2
- package/dist/adapters/redis-adapter.d.ts +4 -4
- package/dist/batch-verifier.d.ts +5 -0
- package/dist/cache-interface.d.ts +23 -16
- package/dist/cache.d.ts +5 -5
- package/dist/check-if-email-exists.d.ts +205 -0
- package/dist/domain-suggester.d.ts +6 -6
- package/dist/{validator.d.ts → email-validator.d.ts} +2 -2
- package/dist/email-verifier-types.d.ts +225 -0
- package/dist/index.d.ts +8 -8
- package/dist/index.esm.js +457 -236
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +460 -238
- package/dist/index.js.map +1 -1
- package/dist/mx-resolver.d.ts +2 -0
- package/dist/name-detector.d.ts +6 -6
- package/dist/serverless/adapters/aws-lambda.cjs.js.map +1 -1
- package/dist/serverless/adapters/aws-lambda.esm.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.cjs.js.map +1 -1
- package/dist/serverless/adapters/cloudflare.esm.js.map +1 -1
- package/dist/serverless/adapters/vercel.cjs.js.map +1 -1
- package/dist/serverless/adapters/vercel.esm.js.map +1 -1
- package/dist/serverless/index.cjs.js.map +1 -1
- package/dist/serverless/index.d.ts +1 -1
- package/dist/serverless/index.esm.js.map +1 -1
- package/dist/serverless/{core.cjs.js → verifier.cjs.js} +1 -1
- package/dist/serverless/verifier.cjs.js.map +1 -0
- package/dist/serverless/{core.esm.js → verifier.esm.js} +1 -1
- package/dist/serverless/verifier.esm.js.map +1 -0
- package/dist/{smtp.d.ts → smtp-verifier.d.ts} +2 -2
- package/dist/types.d.ts +128 -34
- package/dist/whois.d.ts +3 -3
- package/package.json +19 -19
- package/dist/batch.d.ts +0 -5
- package/dist/dns.d.ts +0 -2
- package/dist/serverless/core.cjs.js.map +0 -1
- package/dist/serverless/core.esm.js.map +0 -1
- /package/dist/serverless/{core.d.ts → verifier.d.ts} +0 -0
- /package/dist/serverless/{core.min.js → verifier.min.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
var psl = require('psl');
|
|
4
4
|
var tinyLru = require('tiny-lru');
|
|
5
|
-
var node_dns = require('node:dns');
|
|
6
5
|
var stringSimilarityJs = require('string-similarity-js');
|
|
6
|
+
var node_dns = require('node:dns');
|
|
7
7
|
var net = require('node:net');
|
|
8
8
|
var tls = require('node:tls');
|
|
9
9
|
|
|
@@ -66,31 +66,23 @@ class LRUAdapter {
|
|
|
66
66
|
const DEFAULT_CACHE_OPTIONS = {
|
|
67
67
|
ttl: {
|
|
68
68
|
mx: 36e5,
|
|
69
|
-
// 1 hour
|
|
70
69
|
disposable: 864e5,
|
|
71
|
-
// 24 hours
|
|
72
70
|
free: 864e5,
|
|
73
|
-
// 24 hours
|
|
74
71
|
domainValid: 864e5,
|
|
75
|
-
// 24 hours
|
|
76
72
|
smtp: 18e5,
|
|
77
|
-
|
|
78
|
-
smtpPort: 36e5,
|
|
79
|
-
// 1 hour
|
|
73
|
+
smtpPort: 864e5,
|
|
80
74
|
domainSuggestion: 864e5,
|
|
81
|
-
// 24 hours
|
|
82
75
|
whois: 36e5
|
|
83
|
-
// 1 hour
|
|
84
76
|
},
|
|
85
77
|
maxSize: {
|
|
86
|
-
mx:
|
|
87
|
-
disposable:
|
|
88
|
-
free:
|
|
89
|
-
domainValid:
|
|
90
|
-
smtp:
|
|
91
|
-
smtpPort:
|
|
92
|
-
domainSuggestion:
|
|
93
|
-
whois:
|
|
78
|
+
mx: 1e4,
|
|
79
|
+
disposable: 1e4,
|
|
80
|
+
free: 1e4,
|
|
81
|
+
domainValid: 1e4,
|
|
82
|
+
smtp: 1e4,
|
|
83
|
+
smtpPort: 1e4,
|
|
84
|
+
domainSuggestion: 1e4,
|
|
85
|
+
whois: 1e4
|
|
94
86
|
}
|
|
95
87
|
};
|
|
96
88
|
let defaultCacheInstance = null;
|
|
@@ -128,41 +120,7 @@ function resetDefaultCache() {
|
|
|
128
120
|
defaultCacheInstance = null;
|
|
129
121
|
}
|
|
130
122
|
|
|
131
|
-
|
|
132
|
-
const { domain, cache, logger } = params;
|
|
133
|
-
const log = logger || (() => {
|
|
134
|
-
});
|
|
135
|
-
const cacheStore = getCacheStore(cache, "mx");
|
|
136
|
-
const cached = await cacheStore.get(domain);
|
|
137
|
-
if (cached !== null && cached !== void 0) {
|
|
138
|
-
log(`[resolveMxRecords] Cache hit for ${domain}: ${cached.length} MX records`);
|
|
139
|
-
return cached;
|
|
140
|
-
}
|
|
141
|
-
log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
|
|
142
|
-
try {
|
|
143
|
-
const records = await node_dns.promises.resolveMx(domain);
|
|
144
|
-
records.sort((a, b) => {
|
|
145
|
-
if (a.priority < b.priority) {
|
|
146
|
-
return -1;
|
|
147
|
-
}
|
|
148
|
-
if (a.priority > b.priority) {
|
|
149
|
-
return 1;
|
|
150
|
-
}
|
|
151
|
-
return 0;
|
|
152
|
-
});
|
|
153
|
-
const exchanges = records.map((record) => record.exchange);
|
|
154
|
-
log(`[resolveMxRecords] Found ${exchanges.length} MX records for ${domain}: [${exchanges.join(", ")}]`);
|
|
155
|
-
await cacheStore.set(domain, exchanges);
|
|
156
|
-
log(`[resolveMxRecords] Cached ${exchanges.length} MX records for ${domain}`);
|
|
157
|
-
return exchanges;
|
|
158
|
-
} catch (error) {
|
|
159
|
-
log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
|
|
160
|
-
await cacheStore.set(domain, []);
|
|
161
|
-
throw error;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
const COMMON_EMAIL_DOMAINS = [
|
|
123
|
+
const commonEmailDomains = [
|
|
166
124
|
// Popular free email providers
|
|
167
125
|
"gmail.com",
|
|
168
126
|
"yahoo.com",
|
|
@@ -247,7 +205,7 @@ function getSimilarityThreshold(domain) {
|
|
|
247
205
|
return 0.75;
|
|
248
206
|
}
|
|
249
207
|
function defaultDomainSuggestionMethod(domain, commonDomains) {
|
|
250
|
-
const domainsToCheck = commonDomains ||
|
|
208
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
251
209
|
const lowerDomain = domain.toLowerCase();
|
|
252
210
|
if (domainsToCheck.includes(lowerDomain)) {
|
|
253
211
|
return null;
|
|
@@ -303,7 +261,7 @@ async function defaultDomainSuggestionMethodImpl(domain, commonDomains, cache) {
|
|
|
303
261
|
if (!domain || domain.length < 3) {
|
|
304
262
|
return null;
|
|
305
263
|
}
|
|
306
|
-
const domainsToCheck = commonDomains ||
|
|
264
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
307
265
|
const lowerDomain = domain.toLowerCase();
|
|
308
266
|
const cacheKey = `${lowerDomain}:${domainsToCheck.length}`;
|
|
309
267
|
const cacheStore = getCacheStore(cache, "domainSuggestion");
|
|
@@ -399,15 +357,107 @@ async function suggestEmailDomain(email, commonDomains, cache) {
|
|
|
399
357
|
return null;
|
|
400
358
|
}
|
|
401
359
|
function isCommonDomain(domain, commonDomains) {
|
|
402
|
-
const domainsToCheck = commonDomains ||
|
|
360
|
+
const domainsToCheck = commonDomains || commonEmailDomains;
|
|
403
361
|
return domainsToCheck.includes(domain.toLowerCase());
|
|
404
362
|
}
|
|
405
363
|
function getDomainSimilarity(domain1, domain2) {
|
|
406
364
|
return stringSimilarityJs.stringSimilarity(domain1.toLowerCase(), domain2.toLowerCase());
|
|
407
365
|
}
|
|
408
366
|
|
|
409
|
-
|
|
410
|
-
|
|
367
|
+
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
368
|
+
let [localPart, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
369
|
+
if (!emailDomain) {
|
|
370
|
+
emailDomain = localPart;
|
|
371
|
+
}
|
|
372
|
+
if (!emailDomain) {
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
const cacheStore = getCacheStore(cache, "domainValid");
|
|
376
|
+
const cached = await cacheStore.get(emailDomain);
|
|
377
|
+
if (cached !== null && cached !== void 0) {
|
|
378
|
+
return cached.isValid;
|
|
379
|
+
}
|
|
380
|
+
try {
|
|
381
|
+
const isValidResult = psl.isValid(emailDomain) || false;
|
|
382
|
+
const richResult = {
|
|
383
|
+
isValid: isValidResult,
|
|
384
|
+
hasMX: false,
|
|
385
|
+
// MX not checked in this function
|
|
386
|
+
checkedAt: Date.now()
|
|
387
|
+
};
|
|
388
|
+
await cacheStore.set(emailDomain, richResult);
|
|
389
|
+
return isValidResult;
|
|
390
|
+
} catch (validationError) {
|
|
391
|
+
const errorResult = {
|
|
392
|
+
isValid: false,
|
|
393
|
+
hasMX: false,
|
|
394
|
+
checkedAt: Date.now()
|
|
395
|
+
};
|
|
396
|
+
await cacheStore.set(emailDomain, errorResult);
|
|
397
|
+
return false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function isValidEmail(emailAddress) {
|
|
401
|
+
if (!emailAddress || typeof emailAddress !== "string") {
|
|
402
|
+
return false;
|
|
403
|
+
}
|
|
404
|
+
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
405
|
+
const emailLower = emailAddress.toLowerCase();
|
|
406
|
+
if (emailLower.indexOf(".+") !== -1)
|
|
407
|
+
return false;
|
|
408
|
+
if (emailLower.indexOf("..") !== -1)
|
|
409
|
+
return false;
|
|
410
|
+
if (emailLower.startsWith(".") || emailLower.endsWith("."))
|
|
411
|
+
return false;
|
|
412
|
+
const parts = emailAddress.split("@");
|
|
413
|
+
if (parts.length !== 2)
|
|
414
|
+
return false;
|
|
415
|
+
const [localPart, domain] = parts;
|
|
416
|
+
if (!localPart || !domain)
|
|
417
|
+
return false;
|
|
418
|
+
if (localPart.length > 64)
|
|
419
|
+
return false;
|
|
420
|
+
if (domain.length > 253)
|
|
421
|
+
return false;
|
|
422
|
+
return re.test(emailLower);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
async function resolveMxRecords(params) {
|
|
426
|
+
const { domain, cache, logger } = params;
|
|
427
|
+
const log = logger || (() => {
|
|
428
|
+
});
|
|
429
|
+
const cacheStore = getCacheStore(cache, "mx");
|
|
430
|
+
const cached = await cacheStore.get(domain);
|
|
431
|
+
if (cached !== null && cached !== void 0) {
|
|
432
|
+
log(`[resolveMxRecords] Cache hit for ${domain}: ${cached === null || cached === void 0 ? void 0 : cached.length} MX records`);
|
|
433
|
+
return cached;
|
|
434
|
+
}
|
|
435
|
+
log(`[resolveMxRecords] Performing DNS MX lookup for ${domain}`);
|
|
436
|
+
try {
|
|
437
|
+
const records = await node_dns.promises.resolveMx(domain);
|
|
438
|
+
records === null || records === void 0 ? void 0 : records.sort((a, b) => {
|
|
439
|
+
if (a.priority < b.priority) {
|
|
440
|
+
return -1;
|
|
441
|
+
}
|
|
442
|
+
if (a.priority > b.priority) {
|
|
443
|
+
return 1;
|
|
444
|
+
}
|
|
445
|
+
return 0;
|
|
446
|
+
});
|
|
447
|
+
const exchanges = records === null || records === void 0 ? void 0 : records.map((record) => record.exchange);
|
|
448
|
+
log(`[resolveMxRecords] Found ${exchanges === null || exchanges === void 0 ? void 0 : exchanges.length} MX records for ${domain}: [${exchanges === null || exchanges === void 0 ? void 0 : exchanges.join(", ")}]`);
|
|
449
|
+
await cacheStore.set(domain, exchanges);
|
|
450
|
+
log(`[resolveMxRecords] Cached ${exchanges === null || exchanges === void 0 ? void 0 : exchanges.length} MX records for ${domain}`);
|
|
451
|
+
return exchanges;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
log(`[resolveMxRecords] MX lookup failed for ${domain}, caching empty result`);
|
|
454
|
+
await cacheStore.set(domain, []);
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
const nameSeparator = [".", "_", "-"];
|
|
460
|
+
const commonNameSuffixes = [
|
|
411
461
|
"mail",
|
|
412
462
|
"email",
|
|
413
463
|
"contact",
|
|
@@ -422,7 +472,7 @@ const COMMON_NAME_SUFFIXES = [
|
|
|
422
472
|
"notifications",
|
|
423
473
|
"alerts"
|
|
424
474
|
];
|
|
425
|
-
const
|
|
475
|
+
const contextualSuffixes = [
|
|
426
476
|
"dev",
|
|
427
477
|
"company",
|
|
428
478
|
"team",
|
|
@@ -454,7 +504,7 @@ const COMMON_TITLES = [
|
|
|
454
504
|
"father",
|
|
455
505
|
"sister"
|
|
456
506
|
];
|
|
457
|
-
const
|
|
507
|
+
const commonFirstName = /* @__PURE__ */ new Set([
|
|
458
508
|
// English names
|
|
459
509
|
"james",
|
|
460
510
|
"john",
|
|
@@ -632,7 +682,7 @@ const COMMON_FIRST_NAMES = /* @__PURE__ */ new Set([
|
|
|
632
682
|
"sekou",
|
|
633
683
|
"mariama"
|
|
634
684
|
]);
|
|
635
|
-
const
|
|
685
|
+
const commonLastName = /* @__PURE__ */ new Set([
|
|
636
686
|
// English surnames
|
|
637
687
|
"smith",
|
|
638
688
|
"johnson",
|
|
@@ -752,19 +802,19 @@ function isYearLike(str) {
|
|
|
752
802
|
return /^(19|20)\d{2}$/.test(str);
|
|
753
803
|
}
|
|
754
804
|
function isKnownFirstName(str) {
|
|
755
|
-
return
|
|
805
|
+
return commonFirstName.has(str.toLowerCase());
|
|
756
806
|
}
|
|
757
807
|
function isKnownLastName(str) {
|
|
758
|
-
return
|
|
808
|
+
return commonLastName.has(str.toLowerCase());
|
|
759
809
|
}
|
|
760
810
|
function isTitle(str) {
|
|
761
811
|
return COMMON_TITLES.includes(str.toLowerCase().replace(".", ""));
|
|
762
812
|
}
|
|
763
813
|
function getFirstNameScore(str) {
|
|
764
814
|
const lower = str.toLowerCase();
|
|
765
|
-
if (
|
|
815
|
+
if (commonFirstName.has(lower))
|
|
766
816
|
return 1;
|
|
767
|
-
if (
|
|
817
|
+
if (commonLastName.has(lower))
|
|
768
818
|
return 0.3;
|
|
769
819
|
if (str.length >= 2 && str.length <= 15 && /^[a-zA-Z]+$/.test(str))
|
|
770
820
|
return 0.5;
|
|
@@ -772,9 +822,9 @@ function getFirstNameScore(str) {
|
|
|
772
822
|
}
|
|
773
823
|
function getLastNameScore(str) {
|
|
774
824
|
const lower = str.toLowerCase();
|
|
775
|
-
if (
|
|
825
|
+
if (commonLastName.has(lower))
|
|
776
826
|
return 1;
|
|
777
|
-
if (
|
|
827
|
+
if (commonFirstName.has(lower))
|
|
778
828
|
return 0.3;
|
|
779
829
|
if (str.length >= 2 && str.length <= 20 && /^[a-zA-Z]+$/.test(str))
|
|
780
830
|
return 0.5;
|
|
@@ -883,7 +933,7 @@ function isLikelyName(str, allowNumbers = false, allowSingleLetter = false) {
|
|
|
883
933
|
return false;
|
|
884
934
|
if (str.length === 1 && allowSingleLetter && /^[a-zA-Z]$/.test(str))
|
|
885
935
|
return true;
|
|
886
|
-
if (
|
|
936
|
+
if (commonNameSuffixes.includes(str.toLowerCase()))
|
|
887
937
|
return false;
|
|
888
938
|
if (allowNumbers) {
|
|
889
939
|
if (/^\d+$/.test(str))
|
|
@@ -945,7 +995,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
945
995
|
}
|
|
946
996
|
}
|
|
947
997
|
if (!firstName && !lastName) {
|
|
948
|
-
for (const separator of
|
|
998
|
+
for (const separator of nameSeparator) {
|
|
949
999
|
if (cleanedLocal.includes(separator)) {
|
|
950
1000
|
const parts = cleanedLocal.split(separator).filter((p) => p.length > 0);
|
|
951
1001
|
if (parts.length === 2) {
|
|
@@ -1007,7 +1057,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
1007
1057
|
const firstParsed = parseCompositeNamePart(first);
|
|
1008
1058
|
const middleParsed = parseCompositeNamePart(middle);
|
|
1009
1059
|
const lastParsed = parseCompositeNamePart(last);
|
|
1010
|
-
const isLastSuffix =
|
|
1060
|
+
const isLastSuffix = commonNameSuffixes.includes(last.toLowerCase()) || contextualSuffixes.includes(last.toLowerCase()) || isYearLike(last);
|
|
1011
1061
|
if (isLastSuffix) {
|
|
1012
1062
|
if (isLikelyName(first, true, true) && isLikelyName(middle, true, true)) {
|
|
1013
1063
|
const cleanedFirst = firstParsed.hasNumbers ? firstParsed.cleaned : first;
|
|
@@ -1040,7 +1090,7 @@ function defaultNameDetectionMethod(email) {
|
|
|
1040
1090
|
} else if (parts.length > 3) {
|
|
1041
1091
|
const firstPart = parts[0];
|
|
1042
1092
|
const lastPartLower = parts[parts.length - 1].toLowerCase();
|
|
1043
|
-
const isLastPartSuffix =
|
|
1093
|
+
const isLastPartSuffix = commonNameSuffixes.includes(lastPartLower) || contextualSuffixes.includes(lastPartLower) || isYearLike(parts[parts.length - 1]);
|
|
1044
1094
|
const effectiveLastIndex = isLastPartSuffix ? parts.length - 2 : parts.length - 1;
|
|
1045
1095
|
const lastToUse = effectiveLastIndex >= 0 ? parts[effectiveLastIndex] : null;
|
|
1046
1096
|
if (lastToUse && isLikelyName(firstPart, true, true) && isLikelyName(lastToUse, true, true)) {
|
|
@@ -1123,7 +1173,7 @@ function detectNameFromEmail(params) {
|
|
|
1123
1173
|
}
|
|
1124
1174
|
return defaultNameDetectionMethod(email);
|
|
1125
1175
|
}
|
|
1126
|
-
function
|
|
1176
|
+
function cleanNameForAlgorithm(name) {
|
|
1127
1177
|
if (!name)
|
|
1128
1178
|
return "";
|
|
1129
1179
|
let cleaned = name.replace(/[._*]/g, "");
|
|
@@ -1133,13 +1183,13 @@ function cleanNameForAlgrothin(name) {
|
|
|
1133
1183
|
}
|
|
1134
1184
|
return cleaned;
|
|
1135
1185
|
}
|
|
1136
|
-
function
|
|
1186
|
+
function detectNameForAlgorithm(email) {
|
|
1137
1187
|
const detectedName = detectName(email);
|
|
1138
1188
|
if (!detectedName) {
|
|
1139
1189
|
return null;
|
|
1140
1190
|
}
|
|
1141
|
-
const cleanedFirstName = detectedName.firstName ?
|
|
1142
|
-
const cleanedLastName = detectedName.lastName ?
|
|
1191
|
+
const cleanedFirstName = detectedName.firstName ? cleanNameForAlgorithm(detectedName.firstName) : void 0;
|
|
1192
|
+
const cleanedLastName = detectedName.lastName ? cleanNameForAlgorithm(detectedName.lastName) : void 0;
|
|
1143
1193
|
if (!cleanedFirstName && !cleanedLastName) {
|
|
1144
1194
|
return null;
|
|
1145
1195
|
}
|
|
@@ -1156,27 +1206,143 @@ function detectName(email) {
|
|
|
1156
1206
|
|
|
1157
1207
|
exports.VerificationErrorCode = void 0;
|
|
1158
1208
|
(function(VerificationErrorCode2) {
|
|
1159
|
-
VerificationErrorCode2["
|
|
1160
|
-
VerificationErrorCode2["
|
|
1161
|
-
VerificationErrorCode2["
|
|
1162
|
-
VerificationErrorCode2["
|
|
1163
|
-
VerificationErrorCode2["
|
|
1164
|
-
VerificationErrorCode2["
|
|
1165
|
-
VerificationErrorCode2["
|
|
1166
|
-
VerificationErrorCode2["
|
|
1167
|
-
VerificationErrorCode2["
|
|
1168
|
-
VerificationErrorCode2["
|
|
1209
|
+
VerificationErrorCode2["invalidFormat"] = "INVALID_FORMAT";
|
|
1210
|
+
VerificationErrorCode2["invalidDomain"] = "INVALID_DOMAIN";
|
|
1211
|
+
VerificationErrorCode2["noMxRecords"] = "NO_MX_RECORDS";
|
|
1212
|
+
VerificationErrorCode2["smtpConnectionFailed"] = "SMTP_CONNECTION_FAILED";
|
|
1213
|
+
VerificationErrorCode2["smtpTimeout"] = "SMTP_TIMEOUT";
|
|
1214
|
+
VerificationErrorCode2["mailboxNotFound"] = "MAILBOX_NOT_FOUND";
|
|
1215
|
+
VerificationErrorCode2["mailboxFull"] = "MAILBOX_FULL";
|
|
1216
|
+
VerificationErrorCode2["networkError"] = "NETWORK_ERROR";
|
|
1217
|
+
VerificationErrorCode2["disposableEmail"] = "DISPOSABLE_EMAIL";
|
|
1218
|
+
VerificationErrorCode2["freeEmailProvider"] = "FREE_EMAIL_PROVIDER";
|
|
1169
1219
|
})(exports.VerificationErrorCode || (exports.VerificationErrorCode = {}));
|
|
1220
|
+
exports.EmailProvider = void 0;
|
|
1221
|
+
(function(EmailProvider2) {
|
|
1222
|
+
EmailProvider2["gmail"] = "gmail";
|
|
1223
|
+
EmailProvider2["hotmailB2b"] = "hotmail_b2b";
|
|
1224
|
+
EmailProvider2["hotmailB2c"] = "hotmail_b2c";
|
|
1225
|
+
EmailProvider2["proofpoint"] = "proofpoint";
|
|
1226
|
+
EmailProvider2["mimecast"] = "mimecast";
|
|
1227
|
+
EmailProvider2["yahoo"] = "yahoo";
|
|
1228
|
+
EmailProvider2["everythingElse"] = "everything_else";
|
|
1229
|
+
})(exports.EmailProvider || (exports.EmailProvider = {}));
|
|
1230
|
+
function parseSmtpError(errorMessage) {
|
|
1231
|
+
const lowerError = errorMessage.toLowerCase();
|
|
1232
|
+
const networkErrorPatterns = [
|
|
1233
|
+
"etimedout",
|
|
1234
|
+
"econnrefused",
|
|
1235
|
+
"enotfound",
|
|
1236
|
+
"econnreset",
|
|
1237
|
+
"socket hang up",
|
|
1238
|
+
"connection_timeout",
|
|
1239
|
+
"socket_timeout",
|
|
1240
|
+
"connection_error",
|
|
1241
|
+
"connection_closed"
|
|
1242
|
+
];
|
|
1243
|
+
const isNetworkError = networkErrorPatterns.some((pattern) => lowerError.includes(pattern));
|
|
1244
|
+
if (isNetworkError) {
|
|
1245
|
+
return {
|
|
1246
|
+
isDisabled: false,
|
|
1247
|
+
hasFullInbox: false,
|
|
1248
|
+
isInvalid: true,
|
|
1249
|
+
isCatchAll: false
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
const disabledPatterns = [
|
|
1253
|
+
"account disabled",
|
|
1254
|
+
"account is disabled",
|
|
1255
|
+
"user disabled",
|
|
1256
|
+
"user is disabled",
|
|
1257
|
+
"account locked",
|
|
1258
|
+
"account is locked",
|
|
1259
|
+
"user blocked",
|
|
1260
|
+
"user is blocked",
|
|
1261
|
+
"mailbox disabled",
|
|
1262
|
+
"delivery not authorized",
|
|
1263
|
+
"message rejected",
|
|
1264
|
+
"access denied",
|
|
1265
|
+
"permission denied",
|
|
1266
|
+
"recipient unknown",
|
|
1267
|
+
"recipient address rejected",
|
|
1268
|
+
"user unknown",
|
|
1269
|
+
"address unknown",
|
|
1270
|
+
"invalid recipient",
|
|
1271
|
+
"not a valid recipient",
|
|
1272
|
+
"recipient does not exist",
|
|
1273
|
+
"no such user",
|
|
1274
|
+
"user does not exist",
|
|
1275
|
+
"mailbox unavailable",
|
|
1276
|
+
"recipient unavailable",
|
|
1277
|
+
"address rejected",
|
|
1278
|
+
"550",
|
|
1279
|
+
"551",
|
|
1280
|
+
"553",
|
|
1281
|
+
"not_found",
|
|
1282
|
+
"ambiguous"
|
|
1283
|
+
];
|
|
1284
|
+
const fullInboxPatterns = [
|
|
1285
|
+
"mailbox full",
|
|
1286
|
+
"inbox full",
|
|
1287
|
+
"quota exceeded",
|
|
1288
|
+
"over quota",
|
|
1289
|
+
"storage limit exceeded",
|
|
1290
|
+
"message too large",
|
|
1291
|
+
"insufficient storage",
|
|
1292
|
+
"mailbox over quota",
|
|
1293
|
+
"over the quota",
|
|
1294
|
+
"mailbox size limit exceeded",
|
|
1295
|
+
"account over quota",
|
|
1296
|
+
"storage space",
|
|
1297
|
+
"overquota",
|
|
1298
|
+
"452",
|
|
1299
|
+
"552",
|
|
1300
|
+
"over_quota"
|
|
1301
|
+
];
|
|
1302
|
+
const catchAllPatterns = [
|
|
1303
|
+
"accept all mail",
|
|
1304
|
+
"catch-all",
|
|
1305
|
+
"catchall",
|
|
1306
|
+
"wildcard",
|
|
1307
|
+
"accepts any recipient",
|
|
1308
|
+
"recipient address accepted"
|
|
1309
|
+
];
|
|
1310
|
+
const rateLimitPatterns = [
|
|
1311
|
+
"receiving mail at a rate that",
|
|
1312
|
+
"rate limit",
|
|
1313
|
+
"too many messages",
|
|
1314
|
+
"temporarily rejected",
|
|
1315
|
+
"try again later",
|
|
1316
|
+
"greylisted",
|
|
1317
|
+
"greylist",
|
|
1318
|
+
"deferring",
|
|
1319
|
+
"temporarily deferred",
|
|
1320
|
+
"421",
|
|
1321
|
+
"450",
|
|
1322
|
+
"451",
|
|
1323
|
+
"temporary_failure"
|
|
1324
|
+
];
|
|
1325
|
+
const isDisabled = disabledPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("550") || lowerError.startsWith("551") || lowerError.startsWith("553");
|
|
1326
|
+
const hasFullInbox = fullInboxPatterns.some((pattern) => lowerError.includes(pattern)) || lowerError.startsWith("452") || lowerError.startsWith("552");
|
|
1327
|
+
const isCatchAll = catchAllPatterns.some((pattern) => lowerError.includes(pattern));
|
|
1328
|
+
const isInvalid = !isDisabled && !hasFullInbox && !isCatchAll && !rateLimitPatterns.some((pattern) => lowerError.includes(pattern)) && !lowerError.startsWith("421") && !lowerError.startsWith("450") && !lowerError.startsWith("451");
|
|
1329
|
+
return {
|
|
1330
|
+
isDisabled,
|
|
1331
|
+
hasFullInbox,
|
|
1332
|
+
isInvalid,
|
|
1333
|
+
isCatchAll
|
|
1334
|
+
};
|
|
1335
|
+
}
|
|
1170
1336
|
exports.SMTPStep = void 0;
|
|
1171
1337
|
(function(SMTPStep2) {
|
|
1172
|
-
SMTPStep2["
|
|
1173
|
-
SMTPStep2["
|
|
1174
|
-
SMTPStep2["
|
|
1175
|
-
SMTPStep2["
|
|
1176
|
-
SMTPStep2["
|
|
1177
|
-
SMTPStep2["
|
|
1178
|
-
SMTPStep2["
|
|
1179
|
-
SMTPStep2["
|
|
1338
|
+
SMTPStep2["greeting"] = "GREETING";
|
|
1339
|
+
SMTPStep2["ehlo"] = "EHLO";
|
|
1340
|
+
SMTPStep2["helo"] = "HELO";
|
|
1341
|
+
SMTPStep2["startTls"] = "STARTTLS";
|
|
1342
|
+
SMTPStep2["mailFrom"] = "MAIL_FROM";
|
|
1343
|
+
SMTPStep2["rcptTo"] = "RCPT_TO";
|
|
1344
|
+
SMTPStep2["vrfy"] = "VRFY";
|
|
1345
|
+
SMTPStep2["quit"] = "QUIT";
|
|
1180
1346
|
})(exports.SMTPStep || (exports.SMTPStep = {}));
|
|
1181
1347
|
|
|
1182
1348
|
function isIPAddress(host) {
|
|
@@ -1210,9 +1376,38 @@ async function verifyMailboxSMTP(params) {
|
|
|
1210
1376
|
const { ports = DEFAULT_PORTS, timeout = DEFAULT_TIMEOUT, maxRetries = DEFAULT_MAX_RETRIES, tls: tlsConfig = true, hostname = "localhost", useVRFY = true, cache, debug = false, sequence } = options;
|
|
1211
1377
|
const log = debug ? (...args) => console.log("[SMTP]", ...args) : () => {
|
|
1212
1378
|
};
|
|
1379
|
+
const createSmtpResult = (result, port, tlsUsed, mxHost2) => {
|
|
1380
|
+
const reason = result === true ? "valid" : result === null ? "ambiguous" : "not_found";
|
|
1381
|
+
const parsedError = parseSmtpError(reason);
|
|
1382
|
+
return {
|
|
1383
|
+
canConnectSmtp: result !== null,
|
|
1384
|
+
hasFullInbox: parsedError.hasFullInbox,
|
|
1385
|
+
isCatchAll: parsedError.isCatchAll,
|
|
1386
|
+
isDeliverable: result === true,
|
|
1387
|
+
isDisabled: result === false && parsedError.isDisabled,
|
|
1388
|
+
error: result === null ? reason : result === false ? reason : void 0,
|
|
1389
|
+
providerUsed: exports.EmailProvider.everythingElse,
|
|
1390
|
+
checkedAt: Date.now()
|
|
1391
|
+
};
|
|
1392
|
+
};
|
|
1393
|
+
const createFailureResult = (error) => ({
|
|
1394
|
+
canConnectSmtp: false,
|
|
1395
|
+
hasFullInbox: false,
|
|
1396
|
+
isCatchAll: false,
|
|
1397
|
+
isDeliverable: false,
|
|
1398
|
+
isDisabled: false,
|
|
1399
|
+
error,
|
|
1400
|
+
providerUsed: exports.EmailProvider.everythingElse,
|
|
1401
|
+
checkedAt: Date.now()
|
|
1402
|
+
});
|
|
1213
1403
|
if (!mxRecords || mxRecords.length === 0) {
|
|
1214
1404
|
log("No MX records found");
|
|
1215
|
-
return {
|
|
1405
|
+
return {
|
|
1406
|
+
smtpResult: createFailureResult("No MX records found"),
|
|
1407
|
+
cached: false,
|
|
1408
|
+
port: 0,
|
|
1409
|
+
portCached: false
|
|
1410
|
+
};
|
|
1216
1411
|
}
|
|
1217
1412
|
const mxHost = mxRecords[0];
|
|
1218
1413
|
log(`Verifying ${local}@${domain} via ${mxHost}`);
|
|
@@ -1221,16 +1416,16 @@ async function verifyMailboxSMTP(params) {
|
|
|
1221
1416
|
let cachedResult;
|
|
1222
1417
|
try {
|
|
1223
1418
|
cachedResult = await smtpCacheStore.get(`${mxHost}:${local}@${domain}`);
|
|
1224
|
-
if (cachedResult !== void 0) {
|
|
1225
|
-
log(`Using cached SMTP result: ${cachedResult}`);
|
|
1419
|
+
if (cachedResult !== void 0 && cachedResult !== null) {
|
|
1420
|
+
log(`Using cached SMTP result: ${cachedResult.isDeliverable}`);
|
|
1226
1421
|
return {
|
|
1227
|
-
|
|
1422
|
+
smtpResult: cachedResult,
|
|
1228
1423
|
cached: true,
|
|
1229
1424
|
port: 0,
|
|
1230
1425
|
portCached: false
|
|
1231
1426
|
};
|
|
1232
1427
|
}
|
|
1233
|
-
} catch (
|
|
1428
|
+
} catch (ignoredError) {
|
|
1234
1429
|
cachedResult = void 0;
|
|
1235
1430
|
}
|
|
1236
1431
|
}
|
|
@@ -1239,7 +1434,7 @@ async function verifyMailboxSMTP(params) {
|
|
|
1239
1434
|
let cachedPort;
|
|
1240
1435
|
try {
|
|
1241
1436
|
cachedPort = await smtpPortCacheStore.get(mxHost);
|
|
1242
|
-
} catch (
|
|
1437
|
+
} catch (ignoredError) {
|
|
1243
1438
|
cachedPort = null;
|
|
1244
1439
|
}
|
|
1245
1440
|
if (cachedPort) {
|
|
@@ -1256,21 +1451,22 @@ async function verifyMailboxSMTP(params) {
|
|
|
1256
1451
|
sequence,
|
|
1257
1452
|
log
|
|
1258
1453
|
});
|
|
1259
|
-
|
|
1454
|
+
const smtpResult = createSmtpResult(result);
|
|
1455
|
+
if (smtpCacheStore) {
|
|
1260
1456
|
try {
|
|
1261
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1457
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1262
1458
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1263
|
-
} catch (
|
|
1459
|
+
} catch (ignoredError) {
|
|
1264
1460
|
}
|
|
1265
1461
|
}
|
|
1266
|
-
return {
|
|
1462
|
+
return { smtpResult, cached: false, port: cachedPort, portCached: true };
|
|
1267
1463
|
}
|
|
1268
1464
|
}
|
|
1269
1465
|
for (const port of ports) {
|
|
1270
1466
|
log(`Testing port ${port}`);
|
|
1271
1467
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
1272
1468
|
if (attempt > 0) {
|
|
1273
|
-
const delay = Math.min(
|
|
1469
|
+
const delay = Math.min(200 * 2 ** (attempt - 1), 800);
|
|
1274
1470
|
log(`Retry ${attempt + 1}, waiting ${delay}ms`);
|
|
1275
1471
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1276
1472
|
}
|
|
@@ -1286,11 +1482,12 @@ async function verifyMailboxSMTP(params) {
|
|
|
1286
1482
|
sequence,
|
|
1287
1483
|
log
|
|
1288
1484
|
});
|
|
1289
|
-
|
|
1485
|
+
const smtpResult = createSmtpResult(result);
|
|
1486
|
+
if (smtpCacheStore) {
|
|
1290
1487
|
try {
|
|
1291
|
-
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`,
|
|
1488
|
+
await smtpCacheStore.set(`${mxHost}:${local}@${domain}`, smtpResult);
|
|
1292
1489
|
log(`Cached SMTP result ${result} for ${local}@${domain} via ${mxHost}`);
|
|
1293
|
-
} catch (
|
|
1490
|
+
} catch (ignoredError) {
|
|
1294
1491
|
}
|
|
1295
1492
|
}
|
|
1296
1493
|
if (result !== null) {
|
|
@@ -1298,15 +1495,20 @@ async function verifyMailboxSMTP(params) {
|
|
|
1298
1495
|
try {
|
|
1299
1496
|
await smtpPortCacheStore.set(mxHost, port);
|
|
1300
1497
|
log(`Cached port ${port} for ${mxHost}`);
|
|
1301
|
-
} catch (
|
|
1498
|
+
} catch (ignoredError) {
|
|
1302
1499
|
}
|
|
1303
1500
|
}
|
|
1304
|
-
return {
|
|
1501
|
+
return { smtpResult, cached: false, port, portCached: false };
|
|
1305
1502
|
}
|
|
1306
1503
|
}
|
|
1307
1504
|
}
|
|
1308
1505
|
log("All ports failed");
|
|
1309
|
-
return {
|
|
1506
|
+
return {
|
|
1507
|
+
smtpResult: createFailureResult("All SMTP connection attempts failed"),
|
|
1508
|
+
cached: false,
|
|
1509
|
+
port: 0,
|
|
1510
|
+
portCached: false
|
|
1511
|
+
};
|
|
1310
1512
|
}
|
|
1311
1513
|
async function testSMTPConnection(params) {
|
|
1312
1514
|
const { mxHost, port, local, domain, timeout, tlsConfig, hostname, useVRFY, sequence, log } = params;
|
|
@@ -1314,11 +1516,11 @@ async function testSMTPConnection(params) {
|
|
|
1314
1516
|
const useTLS = tlsConfig !== false && (portConfig.tls || portConfig.starttls);
|
|
1315
1517
|
const implicitTLS = portConfig.tls;
|
|
1316
1518
|
const defaultSequence = {
|
|
1317
|
-
steps: [exports.SMTPStep.
|
|
1519
|
+
steps: [exports.SMTPStep.greeting, exports.SMTPStep.ehlo, exports.SMTPStep.mailFrom, exports.SMTPStep.rcptTo]
|
|
1318
1520
|
};
|
|
1319
1521
|
const activeSequence = sequence || defaultSequence;
|
|
1320
1522
|
if (port === 25) {
|
|
1321
|
-
activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.
|
|
1523
|
+
activeSequence.steps = activeSequence.steps.map((step) => step === exports.SMTPStep.ehlo ? exports.SMTPStep.helo : step);
|
|
1322
1524
|
}
|
|
1323
1525
|
const tlsOptions = {
|
|
1324
1526
|
host: mxHost,
|
|
@@ -1336,7 +1538,7 @@ async function testSMTPConnection(params) {
|
|
|
1336
1538
|
let resolved = false;
|
|
1337
1539
|
let supportsSTARTTLS = false;
|
|
1338
1540
|
let supportsVRFY = false;
|
|
1339
|
-
|
|
1541
|
+
let cleanup = () => {
|
|
1340
1542
|
if (resolved)
|
|
1341
1543
|
return;
|
|
1342
1544
|
resolved = true;
|
|
@@ -1372,31 +1574,31 @@ async function testSMTPConnection(params) {
|
|
|
1372
1574
|
if (resolved)
|
|
1373
1575
|
return;
|
|
1374
1576
|
switch (step) {
|
|
1375
|
-
case exports.SMTPStep.
|
|
1577
|
+
case exports.SMTPStep.ehlo:
|
|
1376
1578
|
sendCommand(`EHLO ${hostname}`);
|
|
1377
1579
|
break;
|
|
1378
|
-
case exports.SMTPStep.
|
|
1580
|
+
case exports.SMTPStep.helo:
|
|
1379
1581
|
sendCommand(`HELO ${domain}`);
|
|
1380
1582
|
break;
|
|
1381
|
-
case exports.SMTPStep.
|
|
1583
|
+
case exports.SMTPStep.greeting:
|
|
1382
1584
|
break;
|
|
1383
|
-
case exports.SMTPStep.
|
|
1585
|
+
case exports.SMTPStep.startTls:
|
|
1384
1586
|
sendCommand("STARTTLS");
|
|
1385
1587
|
break;
|
|
1386
|
-
case exports.SMTPStep.
|
|
1588
|
+
case exports.SMTPStep.mailFrom: {
|
|
1387
1589
|
const from = activeSequence.from || "<>";
|
|
1388
1590
|
sendCommand(`MAIL FROM:${from}`);
|
|
1389
1591
|
break;
|
|
1390
1592
|
}
|
|
1391
|
-
case exports.SMTPStep.
|
|
1593
|
+
case exports.SMTPStep.rcptTo:
|
|
1392
1594
|
sendCommand(`RCPT TO:<${local}@${domain}>`);
|
|
1393
1595
|
break;
|
|
1394
|
-
case exports.SMTPStep.
|
|
1596
|
+
case exports.SMTPStep.vrfy: {
|
|
1395
1597
|
const vrfyTarget = activeSequence.vrfyTarget || local;
|
|
1396
1598
|
sendCommand(`VRFY ${vrfyTarget}`);
|
|
1397
1599
|
break;
|
|
1398
1600
|
}
|
|
1399
|
-
case exports.SMTPStep.
|
|
1601
|
+
case exports.SMTPStep.quit:
|
|
1400
1602
|
sendCommand("QUIT");
|
|
1401
1603
|
break;
|
|
1402
1604
|
}
|
|
@@ -1420,14 +1622,14 @@ async function testSMTPConnection(params) {
|
|
|
1420
1622
|
}
|
|
1421
1623
|
if (isMultiline) {
|
|
1422
1624
|
const currentStep2 = activeSequence.steps[currentStepIndex];
|
|
1423
|
-
if (currentStep2 === exports.SMTPStep.
|
|
1625
|
+
if (currentStep2 === exports.SMTPStep.ehlo && code === "250") {
|
|
1424
1626
|
const upper = response.toUpperCase();
|
|
1425
1627
|
if (upper.includes("STARTTLS"))
|
|
1426
1628
|
supportsSTARTTLS = true;
|
|
1427
1629
|
if (upper.includes("VRFY"))
|
|
1428
1630
|
supportsVRFY = true;
|
|
1429
1631
|
}
|
|
1430
|
-
if (currentStep2 === exports.SMTPStep.
|
|
1632
|
+
if (currentStep2 === exports.SMTPStep.helo && code === "250") {
|
|
1431
1633
|
const upper = response.toUpperCase();
|
|
1432
1634
|
if (upper.includes("VRFY"))
|
|
1433
1635
|
supportsVRFY = true;
|
|
@@ -1440,19 +1642,19 @@ async function testSMTPConnection(params) {
|
|
|
1440
1642
|
}
|
|
1441
1643
|
const currentStep = activeSequence.steps[currentStepIndex];
|
|
1442
1644
|
switch (currentStep) {
|
|
1443
|
-
case exports.SMTPStep.
|
|
1645
|
+
case exports.SMTPStep.greeting:
|
|
1444
1646
|
if (code.startsWith("220")) {
|
|
1445
1647
|
nextStep();
|
|
1446
1648
|
} else {
|
|
1447
1649
|
finish(null, "no_greeting");
|
|
1448
1650
|
}
|
|
1449
1651
|
break;
|
|
1450
|
-
case exports.SMTPStep.
|
|
1652
|
+
case exports.SMTPStep.ehlo:
|
|
1451
1653
|
if (code.startsWith("250")) {
|
|
1452
|
-
const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.
|
|
1654
|
+
const hasSTARTTLS = activeSequence.steps.includes(exports.SMTPStep.startTls);
|
|
1453
1655
|
if (!isTLS && useTLS && supportsSTARTTLS && !implicitTLS && hasSTARTTLS) {
|
|
1454
|
-
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.
|
|
1455
|
-
executeStep(exports.SMTPStep.
|
|
1656
|
+
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
|
|
1657
|
+
executeStep(exports.SMTPStep.startTls);
|
|
1456
1658
|
} else {
|
|
1457
1659
|
nextStep();
|
|
1458
1660
|
}
|
|
@@ -1460,14 +1662,14 @@ async function testSMTPConnection(params) {
|
|
|
1460
1662
|
finish(null, "ehlo_failed");
|
|
1461
1663
|
}
|
|
1462
1664
|
break;
|
|
1463
|
-
case exports.SMTPStep.
|
|
1665
|
+
case exports.SMTPStep.helo:
|
|
1464
1666
|
if (code.startsWith("250")) {
|
|
1465
1667
|
nextStep();
|
|
1466
1668
|
} else {
|
|
1467
1669
|
finish(null, "helo_failed");
|
|
1468
1670
|
}
|
|
1469
1671
|
break;
|
|
1470
|
-
case exports.SMTPStep.
|
|
1672
|
+
case exports.SMTPStep.startTls:
|
|
1471
1673
|
if (code.startsWith("220")) {
|
|
1472
1674
|
const plainSocket = socket;
|
|
1473
1675
|
socket = tls__namespace.connect({
|
|
@@ -1478,7 +1680,7 @@ async function testSMTPConnection(params) {
|
|
|
1478
1680
|
isTLS = true;
|
|
1479
1681
|
log("TLS upgraded");
|
|
1480
1682
|
buffer = "";
|
|
1481
|
-
const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.
|
|
1683
|
+
const starttlsIndex = activeSequence.steps.indexOf(exports.SMTPStep.startTls);
|
|
1482
1684
|
currentStepIndex = starttlsIndex;
|
|
1483
1685
|
nextStep();
|
|
1484
1686
|
});
|
|
@@ -1488,28 +1690,28 @@ async function testSMTPConnection(params) {
|
|
|
1488
1690
|
nextStep();
|
|
1489
1691
|
}
|
|
1490
1692
|
break;
|
|
1491
|
-
case exports.SMTPStep.
|
|
1693
|
+
case exports.SMTPStep.mailFrom:
|
|
1492
1694
|
if (code.startsWith("250")) {
|
|
1493
1695
|
nextStep();
|
|
1494
1696
|
} else {
|
|
1495
1697
|
finish(null, "mail_from_rejected");
|
|
1496
1698
|
}
|
|
1497
1699
|
break;
|
|
1498
|
-
case exports.SMTPStep.
|
|
1700
|
+
case exports.SMTPStep.rcptTo:
|
|
1499
1701
|
if (code.startsWith("250") || code.startsWith("251")) {
|
|
1500
1702
|
finish(true, "valid");
|
|
1501
1703
|
} else if (code.startsWith("552") || code.startsWith("452")) {
|
|
1502
1704
|
finish(false, "over_quota");
|
|
1503
1705
|
} else if (code.startsWith("4")) {
|
|
1504
1706
|
finish(null, "temporary_failure");
|
|
1505
|
-
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.
|
|
1506
|
-
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.
|
|
1507
|
-
executeStep(exports.SMTPStep.
|
|
1707
|
+
} else if (useVRFY && supportsVRFY && code.startsWith("5") && activeSequence.steps.includes(exports.SMTPStep.vrfy)) {
|
|
1708
|
+
currentStepIndex = activeSequence.steps.indexOf(exports.SMTPStep.vrfy);
|
|
1709
|
+
executeStep(exports.SMTPStep.vrfy);
|
|
1508
1710
|
} else {
|
|
1509
1711
|
finish(null, "ambiguous");
|
|
1510
1712
|
}
|
|
1511
1713
|
break;
|
|
1512
|
-
case exports.SMTPStep.
|
|
1714
|
+
case exports.SMTPStep.vrfy:
|
|
1513
1715
|
if (code.startsWith("250") || code.startsWith("252")) {
|
|
1514
1716
|
finish(true, "vrfy_valid");
|
|
1515
1717
|
} else if (code.startsWith("550")) {
|
|
@@ -1518,14 +1720,14 @@ async function testSMTPConnection(params) {
|
|
|
1518
1720
|
finish(null, "vrfy_failed");
|
|
1519
1721
|
}
|
|
1520
1722
|
break;
|
|
1521
|
-
case exports.SMTPStep.
|
|
1723
|
+
case exports.SMTPStep.quit:
|
|
1522
1724
|
if (code.startsWith("221")) {
|
|
1523
1725
|
finish(null, "quit_received");
|
|
1524
1726
|
}
|
|
1525
1727
|
break;
|
|
1526
1728
|
}
|
|
1527
1729
|
};
|
|
1528
|
-
|
|
1730
|
+
let handleData = (data) => {
|
|
1529
1731
|
if (resolved)
|
|
1530
1732
|
return;
|
|
1531
1733
|
buffer += data.toString();
|
|
@@ -1561,65 +1763,69 @@ async function testSMTPConnection(params) {
|
|
|
1561
1763
|
return;
|
|
1562
1764
|
}
|
|
1563
1765
|
const firstStep = activeSequence.steps[0];
|
|
1564
|
-
|
|
1766
|
+
let connectionTimeout;
|
|
1767
|
+
let stepTimeout;
|
|
1768
|
+
const resetActivityTimeout = () => {
|
|
1769
|
+
if (stepTimeout) {
|
|
1770
|
+
clearTimeout(stepTimeout);
|
|
1771
|
+
}
|
|
1772
|
+
stepTimeout = setTimeout(() => {
|
|
1773
|
+
if (!resolved) {
|
|
1774
|
+
log(`Step timeout after ${timeout}ms of inactivity`);
|
|
1775
|
+
finish(null, "step_timeout");
|
|
1776
|
+
}
|
|
1777
|
+
}, timeout);
|
|
1778
|
+
};
|
|
1779
|
+
connectionTimeout = setTimeout(() => {
|
|
1780
|
+
if (!resolved) {
|
|
1781
|
+
log(`Connection timeout after ${timeout}ms`);
|
|
1782
|
+
finish(null, "connection_timeout");
|
|
1783
|
+
}
|
|
1784
|
+
}, timeout);
|
|
1785
|
+
if (firstStep !== exports.SMTPStep.greeting) {
|
|
1565
1786
|
executeStep(firstStep);
|
|
1566
1787
|
}
|
|
1567
|
-
socket.setTimeout(timeout, () =>
|
|
1568
|
-
|
|
1569
|
-
|
|
1788
|
+
socket.setTimeout(timeout, () => {
|
|
1789
|
+
if (!resolved) {
|
|
1790
|
+
log(`Socket timeout after ${timeout}ms`);
|
|
1791
|
+
finish(null, "socket_timeout");
|
|
1792
|
+
}
|
|
1793
|
+
});
|
|
1794
|
+
socket.on("error", (error) => {
|
|
1795
|
+
log(`Socket error: ${error.message}`);
|
|
1570
1796
|
if (!resolved)
|
|
1797
|
+
finish(null, "connection_error");
|
|
1798
|
+
});
|
|
1799
|
+
socket.on("close", () => {
|
|
1800
|
+
if (!resolved) {
|
|
1801
|
+
log("Socket closed unexpectedly");
|
|
1571
1802
|
finish(null, "connection_closed");
|
|
1803
|
+
}
|
|
1572
1804
|
});
|
|
1805
|
+
const originalHandleData = handleData;
|
|
1806
|
+
handleData = (data) => {
|
|
1807
|
+
resetActivityTimeout();
|
|
1808
|
+
originalHandleData(data);
|
|
1809
|
+
};
|
|
1810
|
+
const enhancedCleanup = () => {
|
|
1811
|
+
if (resolved)
|
|
1812
|
+
return;
|
|
1813
|
+
resolved = true;
|
|
1814
|
+
if (connectionTimeout)
|
|
1815
|
+
clearTimeout(connectionTimeout);
|
|
1816
|
+
if (stepTimeout)
|
|
1817
|
+
clearTimeout(stepTimeout);
|
|
1818
|
+
socket.setTimeout(0);
|
|
1819
|
+
try {
|
|
1820
|
+
socket === null || socket === void 0 ? void 0 : socket.write("QUIT\r\n");
|
|
1821
|
+
} catch {
|
|
1822
|
+
}
|
|
1823
|
+
setTimeout(() => socket === null || socket === void 0 ? void 0 : socket.destroy(), 100);
|
|
1824
|
+
};
|
|
1825
|
+
cleanup = enhancedCleanup;
|
|
1573
1826
|
});
|
|
1574
1827
|
}
|
|
1575
1828
|
|
|
1576
|
-
async function isValidEmailDomain(emailOrDomain, cache) {
|
|
1577
|
-
let [_, emailDomain] = (emailOrDomain === null || emailOrDomain === void 0 ? void 0 : emailOrDomain.split("@")) || [];
|
|
1578
|
-
if (!emailDomain) {
|
|
1579
|
-
emailDomain = _;
|
|
1580
|
-
}
|
|
1581
|
-
if (!emailDomain) {
|
|
1582
|
-
return false;
|
|
1583
|
-
}
|
|
1584
|
-
const cacheStore = getCacheStore(cache, "domainValid");
|
|
1585
|
-
const cached = await cacheStore.get(emailDomain);
|
|
1586
|
-
if (cached !== null && cached !== void 0) {
|
|
1587
|
-
return cached;
|
|
1588
|
-
}
|
|
1589
|
-
try {
|
|
1590
|
-
const result = psl.isValid(emailDomain) || false;
|
|
1591
|
-
await cacheStore.set(emailDomain, result);
|
|
1592
|
-
return result;
|
|
1593
|
-
} catch (_e) {
|
|
1594
|
-
await cacheStore.set(emailDomain, false);
|
|
1595
|
-
return false;
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
function isValidEmail(emailAddress) {
|
|
1599
|
-
if (!emailAddress || typeof emailAddress !== "string") {
|
|
1600
|
-
return false;
|
|
1601
|
-
}
|
|
1602
|
-
const re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
|
1603
|
-
const emailLower = emailAddress.toLowerCase();
|
|
1604
|
-
if (emailLower.indexOf(".+") !== -1)
|
|
1605
|
-
return false;
|
|
1606
|
-
if (emailLower.indexOf("..") !== -1)
|
|
1607
|
-
return false;
|
|
1608
|
-
if (emailLower.startsWith(".") || emailLower.endsWith("."))
|
|
1609
|
-
return false;
|
|
1610
|
-
const parts = emailAddress.split("@");
|
|
1611
|
-
if (parts.length !== 2)
|
|
1612
|
-
return false;
|
|
1613
|
-
const [localPart, domain] = parts;
|
|
1614
|
-
if (!localPart || !domain)
|
|
1615
|
-
return false;
|
|
1616
|
-
if (localPart.length > 64)
|
|
1617
|
-
return false;
|
|
1618
|
-
if (domain.length > 253)
|
|
1619
|
-
return false;
|
|
1620
|
-
return re.test(emailLower);
|
|
1621
|
-
}
|
|
1622
|
-
|
|
1623
1829
|
const defaultRegex = {
|
|
1624
1830
|
domainName: "Domain Name: *([^\\s]+)",
|
|
1625
1831
|
registrar: "Registrar: *(.+)",
|
|
@@ -2037,7 +2243,7 @@ function parseWhoisData({ rawData, domain }) {
|
|
|
2037
2243
|
return result;
|
|
2038
2244
|
}
|
|
2039
2245
|
|
|
2040
|
-
const
|
|
2246
|
+
const whoisServers = {
|
|
2041
2247
|
com: "whois.verisign-grs.com",
|
|
2042
2248
|
net: "whois.verisign-grs.com",
|
|
2043
2249
|
org: "whois.pir.org",
|
|
@@ -2137,7 +2343,7 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2137
2343
|
throw new Error("Invalid domain");
|
|
2138
2344
|
}
|
|
2139
2345
|
log(`[whois] extracted TLD: ${tld} for domain: ${domain}`);
|
|
2140
|
-
const whoisServer =
|
|
2346
|
+
const whoisServer = whoisServers[tld];
|
|
2141
2347
|
if (!whoisServer) {
|
|
2142
2348
|
log(`[whois] no specific server for TLD ${tld}, trying IANA`);
|
|
2143
2349
|
const defaultServer = "whois.iana.org";
|
|
@@ -2163,8 +2369,8 @@ async function getWhoisData(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2163
2369
|
await cacheStore.set(cacheKey, whoisData);
|
|
2164
2370
|
log(`[whois] successfully retrieved and cached WHOIS data for ${domain}`);
|
|
2165
2371
|
return whoisData;
|
|
2166
|
-
} catch (
|
|
2167
|
-
log(`[whois] failed to get WHOIS data for ${domain}: ${
|
|
2372
|
+
} catch (ignoredError) {
|
|
2373
|
+
log(`[whois] failed to get WHOIS data for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2168
2374
|
return null;
|
|
2169
2375
|
}
|
|
2170
2376
|
}
|
|
@@ -2201,8 +2407,8 @@ async function getDomainAge(domain, timeout = 5e3, debug = false, cache) {
|
|
|
2201
2407
|
expirationDate: whoisData.expirationDate ? new Date(whoisData.expirationDate) : null,
|
|
2202
2408
|
updatedDate: whoisData.updatedDate ? new Date(whoisData.updatedDate) : null
|
|
2203
2409
|
};
|
|
2204
|
-
} catch (
|
|
2205
|
-
log(`[whois] error getting domain age for ${domain}: ${
|
|
2410
|
+
} catch (ignoredError) {
|
|
2411
|
+
log(`[whois] error getting domain age for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2206
2412
|
return null;
|
|
2207
2413
|
}
|
|
2208
2414
|
}
|
|
@@ -2273,8 +2479,8 @@ async function getDomainRegistrationStatus(domain, timeout = 5e3, debug = false,
|
|
|
2273
2479
|
isPendingDelete,
|
|
2274
2480
|
isLocked
|
|
2275
2481
|
};
|
|
2276
|
-
} catch (
|
|
2277
|
-
log(`[whois] error getting domain registration status for ${domain}: ${
|
|
2482
|
+
} catch (ignoredError) {
|
|
2483
|
+
log(`[whois] error getting domain registration status for ${domain}: ${ignoredError instanceof Error ? ignoredError.message : "Unknown error"}`);
|
|
2278
2484
|
return null;
|
|
2279
2485
|
}
|
|
2280
2486
|
}
|
|
@@ -2450,7 +2656,7 @@ function createErrorResult(email, _error) {
|
|
|
2450
2656
|
metadata: {
|
|
2451
2657
|
verificationTime: 0,
|
|
2452
2658
|
cached: false,
|
|
2453
|
-
error: exports.VerificationErrorCode.
|
|
2659
|
+
error: exports.VerificationErrorCode.smtpConnectionFailed
|
|
2454
2660
|
}
|
|
2455
2661
|
};
|
|
2456
2662
|
}
|
|
@@ -2470,25 +2676,31 @@ async function isDisposableEmail(params) {
|
|
|
2470
2676
|
let cached;
|
|
2471
2677
|
try {
|
|
2472
2678
|
cached = await cacheStore.get(emailDomain);
|
|
2473
|
-
} catch (
|
|
2679
|
+
} catch (ignoredError) {
|
|
2474
2680
|
cached = null;
|
|
2475
2681
|
}
|
|
2476
2682
|
if (cached !== null && cached !== void 0) {
|
|
2477
|
-
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2478
|
-
return cached;
|
|
2683
|
+
log(`[isDisposableEmail] Cache hit for ${emailDomain}: ${cached.isDisposable}`);
|
|
2684
|
+
return cached.isDisposable;
|
|
2479
2685
|
}
|
|
2480
2686
|
if (!disposableEmailProviders) {
|
|
2481
2687
|
disposableEmailProviders = new Set(require("./disposable-email-providers.json"));
|
|
2482
2688
|
}
|
|
2483
|
-
const
|
|
2689
|
+
const isDisposable = disposableEmailProviders.has(emailDomain);
|
|
2690
|
+
const richResult = {
|
|
2691
|
+
isDisposable,
|
|
2692
|
+
source: "disposable-email-providers.json",
|
|
2693
|
+
category: isDisposable ? "disposable" : void 0,
|
|
2694
|
+
checkedAt: Date.now()
|
|
2695
|
+
};
|
|
2484
2696
|
try {
|
|
2485
|
-
await cacheStore.set(emailDomain,
|
|
2486
|
-
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${
|
|
2487
|
-
} catch (
|
|
2697
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2698
|
+
log(`[isDisposableEmail] Cached result for ${emailDomain}: ${isDisposable}`);
|
|
2699
|
+
} catch (ignoredError) {
|
|
2488
2700
|
log(`[isDisposableEmail] Cache write error for ${emailDomain}`);
|
|
2489
2701
|
}
|
|
2490
|
-
log(`[isDisposableEmail] Check result for ${emailDomain}: ${
|
|
2491
|
-
return
|
|
2702
|
+
log(`[isDisposableEmail] Check result for ${emailDomain}: ${isDisposable}`);
|
|
2703
|
+
return isDisposable;
|
|
2492
2704
|
}
|
|
2493
2705
|
async function isFreeEmail(params) {
|
|
2494
2706
|
const { emailOrDomain, cache, logger } = params;
|
|
@@ -2503,25 +2715,30 @@ async function isFreeEmail(params) {
|
|
|
2503
2715
|
let cached;
|
|
2504
2716
|
try {
|
|
2505
2717
|
cached = await cacheStore.get(emailDomain);
|
|
2506
|
-
} catch (
|
|
2718
|
+
} catch (ignoredError) {
|
|
2507
2719
|
cached = null;
|
|
2508
2720
|
}
|
|
2509
2721
|
if (cached !== null && cached !== void 0) {
|
|
2510
|
-
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached}`);
|
|
2511
|
-
return cached;
|
|
2722
|
+
log(`[isFreeEmail] Cache hit for ${emailDomain}: ${cached.isFree}`);
|
|
2723
|
+
return cached.isFree;
|
|
2512
2724
|
}
|
|
2513
2725
|
if (!freeEmailProviders) {
|
|
2514
2726
|
freeEmailProviders = new Set(require("./free-email-providers.json"));
|
|
2515
2727
|
}
|
|
2516
|
-
const
|
|
2728
|
+
const isFree = freeEmailProviders.has(emailDomain);
|
|
2729
|
+
const richResult = {
|
|
2730
|
+
isFree,
|
|
2731
|
+
provider: isFree ? emailDomain : void 0,
|
|
2732
|
+
checkedAt: Date.now()
|
|
2733
|
+
};
|
|
2517
2734
|
try {
|
|
2518
|
-
await cacheStore.set(emailDomain,
|
|
2519
|
-
log(`[isFreeEmail] Cached result for ${emailDomain}: ${
|
|
2520
|
-
} catch (
|
|
2735
|
+
await cacheStore.set(emailDomain, richResult);
|
|
2736
|
+
log(`[isFreeEmail] Cached result for ${emailDomain}: ${isFree}`);
|
|
2737
|
+
} catch (ignoredError) {
|
|
2521
2738
|
log(`[isFreeEmail] Cache write error for ${emailDomain}`);
|
|
2522
2739
|
}
|
|
2523
|
-
log(`[isFreeEmail] Check result for ${emailDomain}: ${
|
|
2524
|
-
return
|
|
2740
|
+
log(`[isFreeEmail] Check result for ${emailDomain}: ${isFree}`);
|
|
2741
|
+
return isFree;
|
|
2525
2742
|
}
|
|
2526
2743
|
const domainPorts = {
|
|
2527
2744
|
// 465 or 587
|
|
@@ -2529,7 +2746,7 @@ const domainPorts = {
|
|
|
2529
2746
|
"ovh.net": 465
|
|
2530
2747
|
};
|
|
2531
2748
|
async function verifyEmail(params) {
|
|
2532
|
-
var _a;
|
|
2749
|
+
var _a, _b;
|
|
2533
2750
|
const { emailAddress, timeout = 4e3, verifyMx = true, verifySmtp = false, debug = false, checkDisposable = true, checkFree = true, detectName: detectName2 = false, nameDetectionMethod, suggestDomain: suggestDomain2 = true, domainSuggestionMethod, commonDomains, checkDomainAge = false, checkDomainRegistration = false, whoisTimeout = 5e3, skipMxForDisposable = false, skipDomainWhoisForDisposable = false } = params;
|
|
2534
2751
|
const startTime = Date.now();
|
|
2535
2752
|
const log = debug ? console.debug : (..._args) => {
|
|
@@ -2549,7 +2766,7 @@ async function verifyEmail(params) {
|
|
|
2549
2766
|
if (!isValidEmail(emailAddress)) {
|
|
2550
2767
|
if (result.metadata) {
|
|
2551
2768
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2552
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2769
|
+
result.metadata.error = exports.VerificationErrorCode.invalidFormat;
|
|
2553
2770
|
}
|
|
2554
2771
|
return result;
|
|
2555
2772
|
}
|
|
@@ -2575,14 +2792,14 @@ async function verifyEmail(params) {
|
|
|
2575
2792
|
if (!domain || !local) {
|
|
2576
2793
|
if (result.metadata) {
|
|
2577
2794
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2578
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2795
|
+
result.metadata.error = exports.VerificationErrorCode.invalidFormat;
|
|
2579
2796
|
}
|
|
2580
2797
|
return result;
|
|
2581
2798
|
}
|
|
2582
2799
|
if (!await isValidEmailDomain(domain, params.cache)) {
|
|
2583
2800
|
if (result.metadata) {
|
|
2584
2801
|
result.metadata.verificationTime = Date.now() - startTime;
|
|
2585
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2802
|
+
result.metadata.error = exports.VerificationErrorCode.invalidDomain;
|
|
2586
2803
|
}
|
|
2587
2804
|
return result;
|
|
2588
2805
|
}
|
|
@@ -2591,7 +2808,7 @@ async function verifyEmail(params) {
|
|
|
2591
2808
|
result.isDisposable = await isDisposableEmail({ emailOrDomain: emailAddress, cache: params.cache, logger: log });
|
|
2592
2809
|
log(`[verifyEmail] Disposable check result: ${result.isDisposable}`);
|
|
2593
2810
|
if (result.isDisposable && result.metadata) {
|
|
2594
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2811
|
+
result.metadata.error = exports.VerificationErrorCode.disposableEmail;
|
|
2595
2812
|
}
|
|
2596
2813
|
}
|
|
2597
2814
|
if (checkFree) {
|
|
@@ -2612,8 +2829,8 @@ async function verifyEmail(params) {
|
|
|
2612
2829
|
try {
|
|
2613
2830
|
result.domainAge = await getDomainAge(domain, whoisTimeout, debug, params.cache);
|
|
2614
2831
|
log(`[verifyEmail] Domain age result:`, result.domainAge ? `${result.domainAge.ageInDays} days` : "null");
|
|
2615
|
-
} catch (
|
|
2616
|
-
log("[verifyEmail] Failed to get domain age",
|
|
2832
|
+
} catch (error) {
|
|
2833
|
+
log("[verifyEmail] Failed to get domain age", error);
|
|
2617
2834
|
result.domainAge = null;
|
|
2618
2835
|
}
|
|
2619
2836
|
} else if (checkDomainAge && shouldSkipDomainWhois) {
|
|
@@ -2624,8 +2841,8 @@ async function verifyEmail(params) {
|
|
|
2624
2841
|
try {
|
|
2625
2842
|
result.domainRegistration = await getDomainRegistrationStatus(domain, whoisTimeout, debug, params.cache);
|
|
2626
2843
|
log(`[verifyEmail] Domain registration result:`, ((_a = result.domainRegistration) === null || _a === void 0 ? void 0 : _a.isRegistered) ? "registered" : "not registered");
|
|
2627
|
-
} catch (
|
|
2628
|
-
log("[verifyEmail] Failed to get domain registration status",
|
|
2844
|
+
} catch (error) {
|
|
2845
|
+
log("[verifyEmail] Failed to get domain registration status", error);
|
|
2629
2846
|
result.domainRegistration = null;
|
|
2630
2847
|
}
|
|
2631
2848
|
} else if (checkDomainRegistration && shouldSkipDomainWhois) {
|
|
@@ -2638,14 +2855,14 @@ async function verifyEmail(params) {
|
|
|
2638
2855
|
result.validMx = mxRecords.length > 0;
|
|
2639
2856
|
log(`[verifyEmail] MX records found: ${mxRecords.length}, valid: ${result.validMx}`);
|
|
2640
2857
|
if (!result.validMx && result.metadata) {
|
|
2641
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2858
|
+
result.metadata.error = exports.VerificationErrorCode.noMxRecords;
|
|
2642
2859
|
}
|
|
2643
2860
|
if (verifySmtp && mxRecords.length > 0) {
|
|
2644
2861
|
const cacheKey = `${emailAddress}:smtp`;
|
|
2645
2862
|
const smtpCacheInstance = getCacheStore(params.cache, "smtp");
|
|
2646
2863
|
const cachedSmtp = await smtpCacheInstance.get(cacheKey);
|
|
2647
2864
|
if (cachedSmtp !== null && cachedSmtp !== void 0) {
|
|
2648
|
-
result.validSmtp = cachedSmtp;
|
|
2865
|
+
result.validSmtp = (_b = cachedSmtp.isDeliverable) !== null && _b !== void 0 ? _b : null;
|
|
2649
2866
|
log(`[verifyEmail] SMTP result from cache: ${result.validSmtp} for ${emailAddress}`);
|
|
2650
2867
|
if (result.metadata) {
|
|
2651
2868
|
result.metadata.cached = true;
|
|
@@ -2665,7 +2882,7 @@ async function verifyEmail(params) {
|
|
|
2665
2882
|
domainPort = domainPorts[mxDomain.domain];
|
|
2666
2883
|
}
|
|
2667
2884
|
}
|
|
2668
|
-
const {
|
|
2885
|
+
const { smtpResult, cached, port } = await verifyMailboxSMTP({
|
|
2669
2886
|
local,
|
|
2670
2887
|
domain,
|
|
2671
2888
|
mxRecords,
|
|
@@ -2678,22 +2895,26 @@ async function verifyEmail(params) {
|
|
|
2678
2895
|
}
|
|
2679
2896
|
});
|
|
2680
2897
|
await smtpCacheInstance.set(cacheKey, smtpResult);
|
|
2681
|
-
|
|
2898
|
+
if (!smtpResult.canConnectSmtp) {
|
|
2899
|
+
result.validSmtp = null;
|
|
2900
|
+
} else {
|
|
2901
|
+
result.validSmtp = smtpResult.isDeliverable;
|
|
2902
|
+
}
|
|
2682
2903
|
if (result.metadata)
|
|
2683
2904
|
result.metadata.cached = cached;
|
|
2684
2905
|
log(`[verifyEmail] SMTP verification result: ${result.validSmtp} for ${emailAddress} (cached for future use)`);
|
|
2685
2906
|
}
|
|
2686
2907
|
if (result.validSmtp === false && result.metadata) {
|
|
2687
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2908
|
+
result.metadata.error = exports.VerificationErrorCode.mailboxNotFound;
|
|
2688
2909
|
} else if (result.validSmtp === null && result.metadata) {
|
|
2689
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2910
|
+
result.metadata.error = exports.VerificationErrorCode.smtpConnectionFailed;
|
|
2690
2911
|
}
|
|
2691
2912
|
}
|
|
2692
|
-
} catch (
|
|
2693
|
-
log("[verifyEmail] Failed to resolve MX records",
|
|
2913
|
+
} catch (error) {
|
|
2914
|
+
log("[verifyEmail] Failed to resolve MX records", error);
|
|
2694
2915
|
result.validMx = false;
|
|
2695
2916
|
if (result.metadata) {
|
|
2696
|
-
result.metadata.error = exports.VerificationErrorCode.
|
|
2917
|
+
result.metadata.error = exports.VerificationErrorCode.noMxRecords;
|
|
2697
2918
|
}
|
|
2698
2919
|
}
|
|
2699
2920
|
} else if ((verifyMx || verifySmtp) && shouldSkipMx) {
|
|
@@ -2705,16 +2926,16 @@ async function verifyEmail(params) {
|
|
|
2705
2926
|
return result;
|
|
2706
2927
|
}
|
|
2707
2928
|
|
|
2708
|
-
exports.COMMON_EMAIL_DOMAINS = COMMON_EMAIL_DOMAINS;
|
|
2709
2929
|
exports.DEFAULT_CACHE_OPTIONS = DEFAULT_CACHE_OPTIONS;
|
|
2710
2930
|
exports.LRUAdapter = LRUAdapter;
|
|
2711
2931
|
exports.RedisAdapter = RedisAdapter;
|
|
2712
|
-
exports.
|
|
2932
|
+
exports.cleanNameForAlgorithm = cleanNameForAlgorithm;
|
|
2713
2933
|
exports.clearDefaultCache = clearDefaultCache;
|
|
2934
|
+
exports.commonEmailDomains = commonEmailDomains;
|
|
2714
2935
|
exports.defaultDomainSuggestionMethod = defaultDomainSuggestionMethod;
|
|
2715
2936
|
exports.defaultNameDetectionMethod = defaultNameDetectionMethod;
|
|
2716
2937
|
exports.detectName = detectName;
|
|
2717
|
-
exports.
|
|
2938
|
+
exports.detectNameForAlgorithm = detectNameForAlgorithm;
|
|
2718
2939
|
exports.detectNameFromEmail = detectNameFromEmail;
|
|
2719
2940
|
exports.domainPorts = domainPorts;
|
|
2720
2941
|
exports.getCacheStore = getCacheStore;
|
|
@@ -2727,6 +2948,7 @@ exports.isDisposableEmail = isDisposableEmail;
|
|
|
2727
2948
|
exports.isFreeEmail = isFreeEmail;
|
|
2728
2949
|
exports.isValidEmail = isValidEmail;
|
|
2729
2950
|
exports.isValidEmailDomain = isValidEmailDomain;
|
|
2951
|
+
exports.parseSmtpError = parseSmtpError;
|
|
2730
2952
|
exports.resetDefaultCache = resetDefaultCache;
|
|
2731
2953
|
exports.suggestDomain = suggestDomain;
|
|
2732
2954
|
exports.suggestEmailDomain = suggestEmailDomain;
|