source: trip-planner-front/node_modules/psl/index.js@ 8d391a1

Last change on this file since 8d391a1 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 5.9 KB
Line 
1/*eslint no-var:0, prefer-arrow-callback: 0, object-shorthand: 0 */
2'use strict';
3
4
5var Punycode = require('punycode');
6
7
8var internals = {};
9
10
11//
12// Read rules from file.
13//
14internals.rules = require('./data/rules.json').map(function (rule) {
15
16 return {
17 rule: rule,
18 suffix: rule.replace(/^(\*\.|\!)/, ''),
19 punySuffix: -1,
20 wildcard: rule.charAt(0) === '*',
21 exception: rule.charAt(0) === '!'
22 };
23});
24
25
26//
27// Check is given string ends with `suffix`.
28//
29internals.endsWith = function (str, suffix) {
30
31 return str.indexOf(suffix, str.length - suffix.length) !== -1;
32};
33
34
35//
36// Find rule for a given domain.
37//
38internals.findRule = function (domain) {
39
40 var punyDomain = Punycode.toASCII(domain);
41 return internals.rules.reduce(function (memo, rule) {
42
43 if (rule.punySuffix === -1){
44 rule.punySuffix = Punycode.toASCII(rule.suffix);
45 }
46 if (!internals.endsWith(punyDomain, '.' + rule.punySuffix) && punyDomain !== rule.punySuffix) {
47 return memo;
48 }
49 // This has been commented out as it never seems to run. This is because
50 // sub tlds always appear after their parents and we never find a shorter
51 // match.
52 //if (memo) {
53 // var memoSuffix = Punycode.toASCII(memo.suffix);
54 // if (memoSuffix.length >= punySuffix.length) {
55 // return memo;
56 // }
57 //}
58 return rule;
59 }, null);
60};
61
62
63//
64// Error codes and messages.
65//
66exports.errorCodes = {
67 DOMAIN_TOO_SHORT: 'Domain name too short.',
68 DOMAIN_TOO_LONG: 'Domain name too long. It should be no more than 255 chars.',
69 LABEL_STARTS_WITH_DASH: 'Domain name label can not start with a dash.',
70 LABEL_ENDS_WITH_DASH: 'Domain name label can not end with a dash.',
71 LABEL_TOO_LONG: 'Domain name label should be at most 63 chars long.',
72 LABEL_TOO_SHORT: 'Domain name label should be at least 1 character long.',
73 LABEL_INVALID_CHARS: 'Domain name label can only contain alphanumeric characters or dashes.'
74};
75
76
77//
78// Validate domain name and throw if not valid.
79//
80// From wikipedia:
81//
82// Hostnames are composed of series of labels concatenated with dots, as are all
83// domain names. Each label must be between 1 and 63 characters long, and the
84// entire hostname (including the delimiting dots) has a maximum of 255 chars.
85//
86// Allowed chars:
87//
88// * `a-z`
89// * `0-9`
90// * `-` but not as a starting or ending character
91// * `.` as a separator for the textual portions of a domain name
92//
93// * http://en.wikipedia.org/wiki/Domain_name
94// * http://en.wikipedia.org/wiki/Hostname
95//
96internals.validate = function (input) {
97
98 // Before we can validate we need to take care of IDNs with unicode chars.
99 var ascii = Punycode.toASCII(input);
100
101 if (ascii.length < 1) {
102 return 'DOMAIN_TOO_SHORT';
103 }
104 if (ascii.length > 255) {
105 return 'DOMAIN_TOO_LONG';
106 }
107
108 // Check each part's length and allowed chars.
109 var labels = ascii.split('.');
110 var label;
111
112 for (var i = 0; i < labels.length; ++i) {
113 label = labels[i];
114 if (!label.length) {
115 return 'LABEL_TOO_SHORT';
116 }
117 if (label.length > 63) {
118 return 'LABEL_TOO_LONG';
119 }
120 if (label.charAt(0) === '-') {
121 return 'LABEL_STARTS_WITH_DASH';
122 }
123 if (label.charAt(label.length - 1) === '-') {
124 return 'LABEL_ENDS_WITH_DASH';
125 }
126 if (!/^[a-z0-9\-]+$/.test(label)) {
127 return 'LABEL_INVALID_CHARS';
128 }
129 }
130};
131
132
133//
134// Public API
135//
136
137
138//
139// Parse domain.
140//
141exports.parse = function (input) {
142
143 if (typeof input !== 'string') {
144 throw new TypeError('Domain name must be a string.');
145 }
146
147 // Force domain to lowercase.
148 var domain = input.slice(0).toLowerCase();
149
150 // Handle FQDN.
151 // TODO: Simply remove trailing dot?
152 if (domain.charAt(domain.length - 1) === '.') {
153 domain = domain.slice(0, domain.length - 1);
154 }
155
156 // Validate and sanitise input.
157 var error = internals.validate(domain);
158 if (error) {
159 return {
160 input: input,
161 error: {
162 message: exports.errorCodes[error],
163 code: error
164 }
165 };
166 }
167
168 var parsed = {
169 input: input,
170 tld: null,
171 sld: null,
172 domain: null,
173 subdomain: null,
174 listed: false
175 };
176
177 var domainParts = domain.split('.');
178
179 // Non-Internet TLD
180 if (domainParts[domainParts.length - 1] === 'local') {
181 return parsed;
182 }
183
184 var handlePunycode = function () {
185
186 if (!/xn--/.test(domain)) {
187 return parsed;
188 }
189 if (parsed.domain) {
190 parsed.domain = Punycode.toASCII(parsed.domain);
191 }
192 if (parsed.subdomain) {
193 parsed.subdomain = Punycode.toASCII(parsed.subdomain);
194 }
195 return parsed;
196 };
197
198 var rule = internals.findRule(domain);
199
200 // Unlisted tld.
201 if (!rule) {
202 if (domainParts.length < 2) {
203 return parsed;
204 }
205 parsed.tld = domainParts.pop();
206 parsed.sld = domainParts.pop();
207 parsed.domain = [parsed.sld, parsed.tld].join('.');
208 if (domainParts.length) {
209 parsed.subdomain = domainParts.pop();
210 }
211 return handlePunycode();
212 }
213
214 // At this point we know the public suffix is listed.
215 parsed.listed = true;
216
217 var tldParts = rule.suffix.split('.');
218 var privateParts = domainParts.slice(0, domainParts.length - tldParts.length);
219
220 if (rule.exception) {
221 privateParts.push(tldParts.shift());
222 }
223
224 parsed.tld = tldParts.join('.');
225
226 if (!privateParts.length) {
227 return handlePunycode();
228 }
229
230 if (rule.wildcard) {
231 tldParts.unshift(privateParts.pop());
232 parsed.tld = tldParts.join('.');
233 }
234
235 if (!privateParts.length) {
236 return handlePunycode();
237 }
238
239 parsed.sld = privateParts.pop();
240 parsed.domain = [parsed.sld, parsed.tld].join('.');
241
242 if (privateParts.length) {
243 parsed.subdomain = privateParts.join('.');
244 }
245
246 return handlePunycode();
247};
248
249
250//
251// Get domain.
252//
253exports.get = function (domain) {
254
255 if (!domain) {
256 return null;
257 }
258 return exports.parse(domain).domain || null;
259};
260
261
262//
263// Check whether domain belongs to a known public suffix.
264//
265exports.isValid = function (domain) {
266
267 var parsed = exports.parse(domain);
268 return Boolean(parsed.domain && parsed.listed);
269};
Note: See TracBrowser for help on using the repository browser.