publicsuffixlist
Version:
A JavaScript domain name parser for the validation of domain names and top level domains, driven by TLDs data from Mozilla's publicsuffixlist.org.
174 lines (143 loc) • 4.69 kB
JavaScript
module.exports = PublicSuffixList;
var __slice = [].slice;
var is = require('./lib/is');
var bind = require('./lib/bind');
var waterfall = require('./lib/waterfall');
var RuleLoader = require('./lib/rule-loader');
var TYPE_EXCEPTION = 1;
var TYPE_WILDCARD = 2;
var TYPE_RULE = 3;
var TYPES = [TYPE_RULE, TYPE_WILDCARD, TYPE_EXCEPTION];
var ruleLoader = null;
function PublicSuffixList (options) {
options = is.Object(options) ? options : {};
// load Mozilla's effective_tld_names.dat by default
if (!(is.String(options.filename) || is.Buffer(options.buffer) || is.Array(options.lines))) {
options.filename = __dirname + '/effective_tld_names.dat';
}
ruleLoader = new RuleLoader(options);
var psl = {
options : options,
initialize : bind(_invoke, null, 'initialize'),
initializeSync : bind(_invoke, null, 'initializeSync'),
lookup : _lookup,
domain : _domain,
validate : _validate,
validateTLD : _validateTLD,
ruleLoader : ruleLoader,
tld : _tld
};
return psl;
}
/**
* @function
* @param {string} fnId function to call
* @returns {*}
*/
function _invoke (fnId) {
var args = __slice.apply(arguments).slice(1);
return ruleLoader[fnId].apply(ruleLoader, args);
}
/**
* @function
* @param {string} domainString
* @returns {string} fqdn
*/
function _domain (domainString) {
var result = this.lookup(domainString);
if (!result) return null;
if (result.exception) return null;
var fqdn = result.domain + '.' + result.tld;
return fqdn;
}
/**
* @function
* @param {string} tldString
* @returns {string} tld
*/
function _tld (tldString) {
var result = this.lookup(tldString, true);
if (!result) return null;
if (result.type === TYPE_EXCEPTION) return null;
var rgx = null;
if (result.type === TYPE_WILDCARD) rgx = new RegExp('(.*)(\.)?('+result.rule+')','i');
else if (result.type === TYPE_RULE) rgx = new RegExp('^(\.)?('+result.rule+')','i');
return rgx.test(tldString) ? result.rule : null;
}
/**
* @function
* @param {string} domainString
* @returns {boolean}
*/
function _validate (domainString) {
var result = this.lookup(domainString);
return !!result && result.exception !== true;
}
/**
* @function
* @param {string} domainString
* @returns {boolean}
*/
function _validateTLD (tldString) {
var result = this.lookup(tldString, true, true);
if (!result || result.type === TYPE_EXCEPTION) return false;
var rgx = null;
if (result.type === TYPE_WILDCARD) rgx = new RegExp('(.*)(\.)?('+result.rule+')','i');
else if (result.type === TYPE_RULE) rgx = new RegExp('^(\.)?('+result.rule+')','i');
return rgx.test(tldString);
}
/**
* @function
* @param {string} domainString
* @param {boolean} ruleOnly
* @param {boolean} ignoreLeadingDot
* @returns {object} result
*/
function _lookup (domainString, ruleOnly, ignoreLeadingDot) {
if (!is.String(domainString)) return null;
if (!ignoreLeadingDot && domainString.charAt(0) === '.') return null;
domainString = domainString.toLowerCase();
var matchingRules = _invoke('findRules', domainString);
var results = [];
TYPES.forEach(function (type) {
var rules = matchingRules[type] || [];
rules = rules
.map(function (rule) {
return {
type : type,
rule : rule,
length : rule.length
};
});
results = results.concat(rules);
});
if (!results.length) return null;
results.sort(function (a, b) { return b.length - a.length; });
var matchingRule = results[0];
if (ruleOnly) return matchingRule;
var tldIndex = -1;
if (matchingRule.type === TYPE_RULE || matchingRule.type === TYPE_WILDCARD) {
tldIndex = domainString.lastIndexOf('.' + matchingRule.rule);
} else if (matchingRule.type === TYPE_EXCEPTION) {
tldIndex = domainString.lastIndexOf('.');
}
var domainPart = domainString.substring(0, tldIndex);
if (typeof domainPart !== 'string') return null;
if (matchingRule.type === TYPE_WILDCARD) {
tldIndex = domainPart.lastIndexOf('.');
domainPart = domainString.substring(0, tldIndex);
}
var remainingParts = domainPart.split(/\./).filter(function(p) { return !!p.length; });
if (!remainingParts.length) return null;
var result = {};
if (matchingRule.type === TYPE_EXCEPTION) {
result.rule = matchingRule.rule;
result.exception = true;
} else if (matchingRule.type === TYPE_WILDCARD) {
result.rule = matchingRule.rule;
}
result.domain = remainingParts[remainingParts.length-1];
result.tld = domainString.substring(tldIndex + 1);
if (remainingParts.length > 1) result.subdomain = domainPart.substring(0, domainPart.length - result.domain.length - 1);
return result;
}