UNPKG

zb-email-verifier

Version:

Promise-based library for verify an email address existence via SMTP

289 lines (288 loc) 14 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.delay = exports.verify = void 0; var dns_1 = __importDefault(require("dns")); var debug_1 = __importDefault(require("debug")); var lodash_1 = __importDefault(require("lodash")); var randomstring_1 = __importDefault(require("randomstring")); var bluebird_1 = __importDefault(require("bluebird")); var smtp_1 = require("./smtp"); var debug = debug_1.default.debug("email-verifier"); /** * Error for verify failure * * ```ts * catch (e) { * throw new VerifyError("RESULT_CODE", e.message) * } * ``` */ var VerifyError = /** @class */ (function (_super) { __extends(VerifyError, _super); function VerifyError(message, reason) { var _this = _super.call(this, message) || this; _this.resultCode = message; _this.reason = reason; // prototype needs to be explicitly set when targeting es3/es5 Object.setPrototypeOf(_this, VerifyError.prototype); return _this; } return VerifyError; }(Error)); function verify(opts) { var _a; return __awaiter(this, void 0, void 0, function () { var responseType, emailRegex, currentJob, client, timedout, mainJob; var _this = this; return __generator(this, function (_b) { responseType = (_a = opts.responseType) !== null && _a !== void 0 ? _a : "simple"; emailRegex = /^(([^<>()\[\]\\.,;:\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,}))$/; if (!emailRegex.test(opts.to)) { return [2 /*return*/, makeResponse(responseType, { resultCode: "INVALID" })]; } timedout = false; mainJob = bluebird_1.default.resolve((function () { return __awaiter(_this, void 0, void 0, function () { var timedoutResult, _a, emailHost, host, result; return __generator(this, function (_b) { switch (_b.label) { case 0: timedoutResult = makeResponse(responseType, { resultCode: "UNKNOWN_TIMEOUT" }); _a = opts.to.split('@'), emailHost = _a[1]; if (timedout) return [2 /*return*/, timedoutResult]; currentJob = "MXRECORD"; return [4 /*yield*/, resolveMx(emailHost)]; case 1: host = _b.sent(); if (timedout) return [2 /*return*/, timedoutResult]; currentJob = "CONN"; client = new smtp_1.SmtpClient({ host: host }); if (timedout) return [2 /*return*/, timedoutResult]; currentJob = "VERIFY"; return [4 /*yield*/, verifySMTP(client, opts, emailHost)]; case 2: result = _b.sent(); return [2 /*return*/, makeResponse(responseType, result)]; } }); }); })()); return [2 /*return*/, new Promise(function (resolve) { var timeout = opts.timeout ? opts.timeout : 5000; setTimeout(function () { debug("TIMEOUT"); timedout = true; if (mainJob.isResolved()) return; if (client) client.close(); if (currentJob) { return resolve(makeResponse(responseType, { resultCode: currentJob + "_TIMEOUT" })); } return resolve(makeResponse(responseType, { resultCode: "UNKNOWN_TIMEOUT" })); }, timeout); (function () { return __awaiter(_this, void 0, void 0, function () { var _a, e_1; return __generator(this, function (_b) { switch (_b.label) { case 0: _b.trys.push([0, 2, 3, 4]); _a = resolve; return [4 /*yield*/, mainJob]; case 1: _a.apply(void 0, [_b.sent()]); return [3 /*break*/, 4]; case 2: e_1 = _b.sent(); if (e_1 instanceof VerifyError) { resolve(makeResponse(responseType, { resultCode: e_1.resultCode, reason: e_1.reason })); } else { resolve(makeResponse(responseType, { resultCode: e_1.message })); } return [3 /*break*/, 4]; case 3: if (client) client.close(); return [7 /*endfinally*/]; case 4: return [2 /*return*/]; } }); }); })(); })]; }); }); } exports.verify = verify; function resolveMx(emailHost) { return __awaiter(this, void 0, void 0, function () { var results, dnsResolveMx, e_2, exchange; return __generator(this, function (_a) { switch (_a.label) { case 0: debug('RESOLVE MX RECORD'); _a.label = 1; case 1: _a.trys.push([1, 3, , 4]); dnsResolveMx = bluebird_1.default.promisify(dns_1.default.resolveMx, { context: dns_1.default }); return [4 /*yield*/, dnsResolveMx(emailHost)]; case 2: results = _a.sent(); if (lodash_1.default.isEmpty(results)) { throw new VerifyError("MXRECORD_FAIL", "failed to dns resolve mx record for " + emailHost); } return [3 /*break*/, 4]; case 3: e_2 = _a.sent(); if (debug_1.default.enabled(debug.namespace)) console.error(e_2); throw new VerifyError("MXRECORD_FAIL", e_2.message); case 4: exchange = lodash_1.default(results).sortBy(function (v) { return v.priority; }).take(1).value()[0].exchange; debug(exchange); return [2 /*return*/, exchange]; } }); }); } function verifySMTP(netConn, opts, emailHost) { return __awaiter(this, void 0, void 0, function () { var ensure, response, randomEmail, response_1, e_3; var _this = this; return __generator(this, function (_a) { switch (_a.label) { case 0: debug('VERIFY USING SMTP'); _a.label = 1; case 1: _a.trys.push([1, 8, 9, 10]); ensure = function (promise, value) { return __awaiter(_this, void 0, void 0, function () { var response; return __generator(this, function (_a) { switch (_a.label) { case 0: return [4 /*yield*/, promise]; case 1: response = _a.sent(); if (response.code !== value) throw new Error(JSON.stringify(response)); return [2 /*return*/, response]; } }); }); }; return [4 /*yield*/, ensure(netConn.connect(), 220)]; case 2: _a.sent(); return [4 /*yield*/, ensure(netConn.helo(opts.helo), 250)]; case 3: _a.sent(); return [4 /*yield*/, ensure(netConn.from(opts.from), 250)]; case 4: _a.sent(); return [4 /*yield*/, netConn.to(opts.to)]; case 5: response = _a.sent(); if (response.code !== 250) { return [2 /*return*/, { resultCode: "NOT_EXIST", }]; } if (!(opts.catchalltest === true)) return [3 /*break*/, 7]; debug('MAILBOX EXIST..CHECKING FOR CATCHALL'); randomEmail = randomstring_1.default.generate(32) + "@" + emailHost; return [4 /*yield*/, netConn.to(randomEmail)]; case 6: response_1 = _a.sent(); if (response_1.code === 250) { return [2 /*return*/, { resultCode: "CATCH_ALL" }]; } _a.label = 7; case 7: return [2 /*return*/, { resultCode: "EXIST", }]; case 8: e_3 = _a.sent(); if (debug_1.default.enabled(debug.namespace)) console.error(e_3); throw new VerifyError("VERIFY_FAIL", e_3.message); case 9: netConn.close(); return [7 /*endfinally*/]; case 10: return [2 /*return*/]; } }); }); } function delay(ms) { return new Promise(function (resolve) { return setTimeout(resolve, ms); }); } exports.delay = delay; function makeResponse(responseType, result) { switch (responseType) { case "simple": return result.resultCode; case "reason": return result; default: throw new Error("no such reasponse type"); } }