1
0
Fork 0
management/front/dkha-web-sz-main/node_modules/@hapi/address/lib/index.js

249 lines
6.8 KiB
JavaScript

'use strict';
const Punycode = require('punycode');
const Abnf = require('./abnf');
const Tlds = require('./tlds');
const internals = {
nonAsciiRx: /[^\x00-\x7f]/,
minDomainSegments: 2,
defaultTlds: { allow: Tlds, deny: null }
};
module.exports = {
email: {
analyze: function (email, options) {
return internals.email(email, options);
},
isValid: function (email, options) {
return !internals.email(email, options);
}
},
domain: {
analyze: function (domain, options = {}) {
internals.options(domain, options);
if (!domain) {
return internals.error('Domain must be a non-empty string');
}
if (domain.length > 256) {
return internals.error('Domain too long');
}
const ascii = !internals.nonAsciiRx.test(domain);
if (!ascii) {
if (options.allowUnicode === false) { // Defaults to true
return internals.error('Domain contains forbidden Unicode characters');
}
const normalized = domain.normalize('NFC');
domain = Punycode.toASCII(normalized);
}
return internals.domain(domain, options);
},
isValid: function (domain, options) {
return !module.exports.domain.analyze(domain, options);
}
}
};
internals.email = function (email, options = {}) {
internals.options(email, options);
if (!email) {
return internals.error('Address must be a non-empty string');
}
// Unicode
const ascii = !internals.nonAsciiRx.test(email);
if (!ascii) {
if (options.allowUnicode === false) { // Defaults to true
return internals.error('Address contains forbidden Unicode characters');
}
const normalized = email.normalize('NFC');
email = Punycode.toASCII(normalized);
}
// Basic structure
const parts = email.split('@');
if (parts.length !== 2) {
return internals.error(parts.length > 2 ? 'Address cannot contain more than one @ character' : 'Address must contain one @ character');
}
const local = parts[0];
const domain = parts[1];
if (!local) {
return internals.error('Address local part cannot be empty');
}
if (!domain) {
return internals.error('Domain cannot be empty');
}
if (email.length > 254) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.3
return internals.error('Address too long');
}
if (Buffer.byteLength(local, 'utf-8') > 64) { // http://tools.ietf.org/html/rfc5321#section-4.5.3.1.1
return internals.error('Address local part too long');
}
// Validate parts
return internals.local(local, ascii) || internals.domain(domain, options);
};
internals.options = function (value, options) {
// Options validation
if (options.tlds &&
options.tlds !== true) {
if (typeof options.tlds !== 'object') {
throw new Error('Invalid options: tlds must be a boolean or an object');
}
if (options.tlds.allow !== undefined &&
options.tlds.allow !== true &&
options.tlds.allow instanceof Set === false) {
throw new Error('Invalid options: tlds.allow must be a Set object or true');
}
if (options.tlds.deny) {
if (options.tlds.deny instanceof Set === false) {
throw new Error('Invalid options: tlds.deny must be a Set object');
}
if (options.tlds.allow instanceof Set) {
throw new Error('Invalid options: cannot specify both tlds.allow and tlds.deny lists');
}
}
}
// Input validation
if (typeof value !== 'string') {
throw new Error('Invalid input: value must be a string');
}
};
internals.local = function (local, ascii) {
const segments = local.split('.');
for (const segment of segments) {
if (!segment.length) {
return internals.error('Address local part contains empty dot-separated segment');
}
if (ascii) {
if (!Abnf.atextRx.test(segment)) {
return internals.error('Address local part contains invalid character');
}
}
else {
for (const char of segment) {
const binary = Buffer.from(char).toString('binary');
if (!Abnf.atomRx.test(binary)) {
return internals.error('Address local part contains invalid character');
}
}
}
}
};
internals.tldSegmentRx = /^[a-zA-Z](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
internals.domainSegmentRx = /^[a-zA-Z0-9](?:[a-zA-Z0-9\-]*[a-zA-Z0-9])?$/;
internals.domain = function (domain, options) {
// https://tools.ietf.org/html/rfc1035 section 2.3.1
const minDomainSegments = (options.minDomainSegments || internals.minDomainSegments);
const segments = domain.split('.');
if (segments.length < minDomainSegments) {
return internals.error('Domain lacks the minimum required number of segments');
}
const tlds = internals.tlds(options);
if (tlds) {
const tld = segments[segments.length - 1].toLowerCase();
if (tlds.deny && tlds.deny.has(tld) ||
tlds.allow && !tlds.allow.has(tld)) {
return internals.error('Domain uses forbidden TLD');
}
}
for (let i = 0; i < segments.length; ++i) {
const segment = segments[i];
if (!segment.length) {
return internals.error('Domain contains empty dot-separated segment');
}
if (segment.length > 63) {
return internals.error('Domain contains dot-separated segment that is too long');
}
if (i < segments.length - 1) {
if (!internals.domainSegmentRx.test(segment)) {
return internals.error('Domain contains invalid character');
}
}
else {
if (!internals.tldSegmentRx.test(segment)) {
return internals.error('Domain contains invalid tld character');
}
}
}
};
internals.tlds = function (options) {
if (options.tlds === false) { // Defaults to true
return null;
}
if (!options.tlds ||
options.tlds === true) {
return internals.defaultTlds;
}
return {
allow: options.tlds.allow === true ? null : options.tlds.allow || Tlds,
deny: options.tlds.deny || null
};
};
internals.error = function (reason) {
return { error: reason };
};