source: node_modules/nodemailer/lib/punycode/index.js@ 2d1ec46

main
Last change on this file since 2d1ec46 was 81bc7da, checked in by Klimentina Efremova <klimentina08642@…>, 3 months ago

Initial commit

  • Property mode set to 100644
File size: 15.0 KB
Line 
1/*
2
3Copied from https://github.com/mathiasbynens/punycode.js/blob/ef3505c8abb5143a00d53ce59077c9f7f4b2ac47/punycode.js
4
5Copyright Mathias Bynens <https://mathiasbynens.be/>
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26*/
27/* eslint callback-return: 0, no-bitwise: 0, eqeqeq: 0, prefer-arrow-callback: 0, object-shorthand: 0 */
28
29'use strict';
30
31/** Highest positive signed 32-bit float value */
32const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
33
34/** Bootstring parameters */
35const base = 36;
36const tMin = 1;
37const tMax = 26;
38const skew = 38;
39const damp = 700;
40const initialBias = 72;
41const initialN = 128; // 0x80
42const delimiter = '-'; // '\x2D'
43
44/** Regular expressions */
45const regexPunycode = /^xn--/;
46const regexNonASCII = /[^\0-\x7F]/; // Note: U+007F DEL is excluded too.
47const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
48
49/** Error messages */
50const errors = {
51 overflow: 'Overflow: input needs wider integers to process',
52 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
53 'invalid-input': 'Invalid input'
54};
55
56/** Convenience shortcuts */
57const baseMinusTMin = base - tMin;
58const floor = Math.floor;
59const stringFromCharCode = String.fromCharCode;
60
61/*--------------------------------------------------------------------------*/
62
63/**
64 * A generic error utility function.
65 * @private
66 * @param {String} type The error type.
67 * @returns {Error} Throws a `RangeError` with the applicable error message.
68 */
69function error(type) {
70 throw new RangeError(errors[type]);
71}
72
73/**
74 * A generic `Array#map` utility function.
75 * @private
76 * @param {Array} array The array to iterate over.
77 * @param {Function} callback The function that gets called for every array
78 * item.
79 * @returns {Array} A new array of values returned by the callback function.
80 */
81function map(array, callback) {
82 const result = [];
83 let length = array.length;
84 while (length--) {
85 result[length] = callback(array[length]);
86 }
87 return result;
88}
89
90/**
91 * A simple `Array#map`-like wrapper to work with domain name strings or email
92 * addresses.
93 * @private
94 * @param {String} domain The domain name or email address.
95 * @param {Function} callback The function that gets called for every
96 * character.
97 * @returns {String} A new string of characters returned by the callback
98 * function.
99 */
100function mapDomain(domain, callback) {
101 const parts = domain.split('@');
102 let result = '';
103 if (parts.length > 1) {
104 // In email addresses, only the domain name should be punycoded. Leave
105 // the local part (i.e. everything up to `@`) intact.
106 result = parts[0] + '@';
107 domain = parts[1];
108 }
109 // Avoid `split(regex)` for IE8 compatibility. See #17.
110 domain = domain.replace(regexSeparators, '\x2E');
111 const labels = domain.split('.');
112 const encoded = map(labels, callback).join('.');
113 return result + encoded;
114}
115
116/**
117 * Creates an array containing the numeric code points of each Unicode
118 * character in the string. While JavaScript uses UCS-2 internally,
119 * this function will convert a pair of surrogate halves (each of which
120 * UCS-2 exposes as separate characters) into a single code point,
121 * matching UTF-16.
122 * @see `punycode.ucs2.encode`
123 * @see <https://mathiasbynens.be/notes/javascript-encoding>
124 * @memberOf punycode.ucs2
125 * @name decode
126 * @param {String} string The Unicode input string (UCS-2).
127 * @returns {Array} The new array of code points.
128 */
129function ucs2decode(string) {
130 const output = [];
131 let counter = 0;
132 const length = string.length;
133 while (counter < length) {
134 const value = string.charCodeAt(counter++);
135 if (value >= 0xd800 && value <= 0xdbff && counter < length) {
136 // It's a high surrogate, and there is a next character.
137 const extra = string.charCodeAt(counter++);
138 if ((extra & 0xfc00) == 0xdc00) {
139 // Low surrogate.
140 output.push(((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000);
141 } else {
142 // It's an unmatched surrogate; only append this code unit, in case the
143 // next code unit is the high surrogate of a surrogate pair.
144 output.push(value);
145 counter--;
146 }
147 } else {
148 output.push(value);
149 }
150 }
151 return output;
152}
153
154/**
155 * Creates a string based on an array of numeric code points.
156 * @see `punycode.ucs2.decode`
157 * @memberOf punycode.ucs2
158 * @name encode
159 * @param {Array} codePoints The array of numeric code points.
160 * @returns {String} The new Unicode string (UCS-2).
161 */
162const ucs2encode = codePoints => String.fromCodePoint(...codePoints);
163
164/**
165 * Converts a basic code point into a digit/integer.
166 * @see `digitToBasic()`
167 * @private
168 * @param {Number} codePoint The basic numeric code point value.
169 * @returns {Number} The numeric value of a basic code point (for use in
170 * representing integers) in the range `0` to `base - 1`, or `base` if
171 * the code point does not represent a value.
172 */
173const basicToDigit = function (codePoint) {
174 if (codePoint >= 0x30 && codePoint < 0x3a) {
175 return 26 + (codePoint - 0x30);
176 }
177 if (codePoint >= 0x41 && codePoint < 0x5b) {
178 return codePoint - 0x41;
179 }
180 if (codePoint >= 0x61 && codePoint < 0x7b) {
181 return codePoint - 0x61;
182 }
183 return base;
184};
185
186/**
187 * Converts a digit/integer into a basic code point.
188 * @see `basicToDigit()`
189 * @private
190 * @param {Number} digit The numeric value of a basic code point.
191 * @returns {Number} The basic code point whose value (when used for
192 * representing integers) is `digit`, which needs to be in the range
193 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
194 * used; else, the lowercase form is used. The behavior is undefined
195 * if `flag` is non-zero and `digit` has no uppercase form.
196 */
197const digitToBasic = function (digit, flag) {
198 // 0..25 map to ASCII a..z or A..Z
199 // 26..35 map to ASCII 0..9
200 return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
201};
202
203/**
204 * Bias adaptation function as per section 3.4 of RFC 3492.
205 * https://tools.ietf.org/html/rfc3492#section-3.4
206 * @private
207 */
208const adapt = function (delta, numPoints, firstTime) {
209 let k = 0;
210 delta = firstTime ? floor(delta / damp) : delta >> 1;
211 delta += floor(delta / numPoints);
212 for (; /* no initialization */ delta > (baseMinusTMin * tMax) >> 1; k += base) {
213 delta = floor(delta / baseMinusTMin);
214 }
215 return floor(k + ((baseMinusTMin + 1) * delta) / (delta + skew));
216};
217
218/**
219 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
220 * symbols.
221 * @memberOf punycode
222 * @param {String} input The Punycode string of ASCII-only symbols.
223 * @returns {String} The resulting string of Unicode symbols.
224 */
225const decode = function (input) {
226 // Don't use UCS-2.
227 const output = [];
228 const inputLength = input.length;
229 let i = 0;
230 let n = initialN;
231 let bias = initialBias;
232
233 // Handle the basic code points: let `basic` be the number of input code
234 // points before the last delimiter, or `0` if there is none, then copy
235 // the first basic code points to the output.
236
237 let basic = input.lastIndexOf(delimiter);
238 if (basic < 0) {
239 basic = 0;
240 }
241
242 for (let j = 0; j < basic; ++j) {
243 // if it's not a basic code point
244 if (input.charCodeAt(j) >= 0x80) {
245 error('not-basic');
246 }
247 output.push(input.charCodeAt(j));
248 }
249
250 // Main decoding loop: start just after the last delimiter if any basic code
251 // points were copied; start at the beginning otherwise.
252
253 for (let index = basic > 0 ? basic + 1 : 0; index < inputLength /* no final expression */; ) {
254 // `index` is the index of the next character to be consumed.
255 // Decode a generalized variable-length integer into `delta`,
256 // which gets added to `i`. The overflow checking is easier
257 // if we increase `i` as we go, then subtract off its starting
258 // value at the end to obtain `delta`.
259 const oldi = i;
260 for (let w = 1, k = base /* no condition */; ; k += base) {
261 if (index >= inputLength) {
262 error('invalid-input');
263 }
264
265 const digit = basicToDigit(input.charCodeAt(index++));
266
267 if (digit >= base) {
268 error('invalid-input');
269 }
270 if (digit > floor((maxInt - i) / w)) {
271 error('overflow');
272 }
273
274 i += digit * w;
275 const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
276
277 if (digit < t) {
278 break;
279 }
280
281 const baseMinusT = base - t;
282 if (w > floor(maxInt / baseMinusT)) {
283 error('overflow');
284 }
285
286 w *= baseMinusT;
287 }
288
289 const out = output.length + 1;
290 bias = adapt(i - oldi, out, oldi == 0);
291
292 // `i` was supposed to wrap around from `out` to `0`,
293 // incrementing `n` each time, so we'll fix that now:
294 if (floor(i / out) > maxInt - n) {
295 error('overflow');
296 }
297
298 n += floor(i / out);
299 i %= out;
300
301 // Insert `n` at position `i` of the output.
302 output.splice(i++, 0, n);
303 }
304
305 return String.fromCodePoint(...output);
306};
307
308/**
309 * Converts a string of Unicode symbols (e.g. a domain name label) to a
310 * Punycode string of ASCII-only symbols.
311 * @memberOf punycode
312 * @param {String} input The string of Unicode symbols.
313 * @returns {String} The resulting Punycode string of ASCII-only symbols.
314 */
315const encode = function (input) {
316 const output = [];
317
318 // Convert the input in UCS-2 to an array of Unicode code points.
319 input = ucs2decode(input);
320
321 // Cache the length.
322 const inputLength = input.length;
323
324 // Initialize the state.
325 let n = initialN;
326 let delta = 0;
327 let bias = initialBias;
328
329 // Handle the basic code points.
330 for (const currentValue of input) {
331 if (currentValue < 0x80) {
332 output.push(stringFromCharCode(currentValue));
333 }
334 }
335
336 const basicLength = output.length;
337 let handledCPCount = basicLength;
338
339 // `handledCPCount` is the number of code points that have been handled;
340 // `basicLength` is the number of basic code points.
341
342 // Finish the basic string with a delimiter unless it's empty.
343 if (basicLength) {
344 output.push(delimiter);
345 }
346
347 // Main encoding loop:
348 while (handledCPCount < inputLength) {
349 // All non-basic code points < n have been handled already. Find the next
350 // larger one:
351 let m = maxInt;
352 for (const currentValue of input) {
353 if (currentValue >= n && currentValue < m) {
354 m = currentValue;
355 }
356 }
357
358 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
359 // but guard against overflow.
360 const handledCPCountPlusOne = handledCPCount + 1;
361 if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
362 error('overflow');
363 }
364
365 delta += (m - n) * handledCPCountPlusOne;
366 n = m;
367
368 for (const currentValue of input) {
369 if (currentValue < n && ++delta > maxInt) {
370 error('overflow');
371 }
372 if (currentValue === n) {
373 // Represent delta as a generalized variable-length integer.
374 let q = delta;
375 for (let k = base /* no condition */; ; k += base) {
376 const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
377 if (q < t) {
378 break;
379 }
380 const qMinusT = q - t;
381 const baseMinusT = base - t;
382 output.push(stringFromCharCode(digitToBasic(t + (qMinusT % baseMinusT), 0)));
383 q = floor(qMinusT / baseMinusT);
384 }
385
386 output.push(stringFromCharCode(digitToBasic(q, 0)));
387 bias = adapt(delta, handledCPCountPlusOne, handledCPCount === basicLength);
388 delta = 0;
389 ++handledCPCount;
390 }
391 }
392
393 ++delta;
394 ++n;
395 }
396 return output.join('');
397};
398
399/**
400 * Converts a Punycode string representing a domain name or an email address
401 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
402 * it doesn't matter if you call it on a string that has already been
403 * converted to Unicode.
404 * @memberOf punycode
405 * @param {String} input The Punycoded domain name or email address to
406 * convert to Unicode.
407 * @returns {String} The Unicode representation of the given Punycode
408 * string.
409 */
410const toUnicode = function (input) {
411 return mapDomain(input, function (string) {
412 return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string;
413 });
414};
415
416/**
417 * Converts a Unicode string representing a domain name or an email address to
418 * Punycode. Only the non-ASCII parts of the domain name will be converted,
419 * i.e. it doesn't matter if you call it with a domain that's already in
420 * ASCII.
421 * @memberOf punycode
422 * @param {String} input The domain name or email address to convert, as a
423 * Unicode string.
424 * @returns {String} The Punycode representation of the given domain name or
425 * email address.
426 */
427const toASCII = function (input) {
428 return mapDomain(input, function (string) {
429 return regexNonASCII.test(string) ? 'xn--' + encode(string) : string;
430 });
431};
432
433/*--------------------------------------------------------------------------*/
434
435/** Define the public API */
436const punycode = {
437 /**
438 * A string representing the current Punycode.js version number.
439 * @memberOf punycode
440 * @type String
441 */
442 version: '2.3.1',
443 /**
444 * An object of methods to convert from JavaScript's internal character
445 * representation (UCS-2) to Unicode code points, and back.
446 * @see <https://mathiasbynens.be/notes/javascript-encoding>
447 * @memberOf punycode
448 * @type Object
449 */
450 ucs2: {
451 decode: ucs2decode,
452 encode: ucs2encode
453 },
454 decode: decode,
455 encode: encode,
456 toASCII: toASCII,
457 toUnicode: toUnicode
458};
459
460module.exports = punycode;
Note: See TracBrowser for help on using the repository browser.