diff options
author | LinuxWizard42 <computerwizard@linuxmail.org> | 2022-10-12 23:08:57 +0300 |
---|---|---|
committer | LinuxWizard42 <computerwizard@linuxmail.org> | 2022-10-12 23:08:57 +0300 |
commit | 726b81b19251674e149ccfbb1abacbd837fc6db0 (patch) | |
tree | fbdbb227dc01357eb76e8222d76185bc124c5ca6 /node_modules/tough-cookie/lib/cookie.js | |
parent | 34f0890e175698940d49238097579f44e4d78c89 (diff) | |
download | FlashRunner-726b81b19251674e149ccfbb1abacbd837fc6db0.tar.gz FlashRunner-726b81b19251674e149ccfbb1abacbd837fc6db0.tar.zst |
Removed files that should not have been included in git
Diffstat (limited to 'node_modules/tough-cookie/lib/cookie.js')
-rw-r--r-- | node_modules/tough-cookie/lib/cookie.js | 1482 |
1 files changed, 0 insertions, 1482 deletions
diff --git a/node_modules/tough-cookie/lib/cookie.js b/node_modules/tough-cookie/lib/cookie.js deleted file mode 100644 index 32dc0f8..0000000 --- a/node_modules/tough-cookie/lib/cookie.js +++ /dev/null @@ -1,1482 +0,0 @@ -/*! - * Copyright (c) 2015, Salesforce.com, Inc. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * 3. Neither the name of Salesforce.com nor the names of its contributors may - * be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -'use strict'; -var net = require('net'); -var urlParse = require('url').parse; -var util = require('util'); -var pubsuffix = require('./pubsuffix-psl'); -var Store = require('./store').Store; -var MemoryCookieStore = require('./memstore').MemoryCookieStore; -var pathMatch = require('./pathMatch').pathMatch; -var VERSION = require('./version'); - -var punycode; -try { - punycode = require('punycode'); -} catch(e) { - console.warn("tough-cookie: can't load punycode; won't use punycode for domain normalization"); -} - -// From RFC6265 S4.1.1 -// note that it excludes \x3B ";" -var COOKIE_OCTETS = /^[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]+$/; - -var CONTROL_CHARS = /[\x00-\x1F]/; - -// From Chromium // '\r', '\n' and '\0' should be treated as a terminator in -// the "relaxed" mode, see: -// https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60 -var TERMINATORS = ['\n', '\r', '\0']; - -// RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"' -// Note ';' is \x3B -var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/; - -// date-time parsing constants (RFC6265 S5.1.1) - -var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/; - -var MONTH_TO_NUM = { - jan:0, feb:1, mar:2, apr:3, may:4, jun:5, - jul:6, aug:7, sep:8, oct:9, nov:10, dec:11 -}; -var NUM_TO_MONTH = [ - 'Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec' -]; -var NUM_TO_DAY = [ - 'Sun','Mon','Tue','Wed','Thu','Fri','Sat' -]; - -var MAX_TIME = 2147483647000; // 31-bit max -var MIN_TIME = 0; // 31-bit min - -/* - * Parses a Natural number (i.e., non-negative integer) with either the - * <min>*<max>DIGIT ( non-digit *OCTET ) - * or - * <min>*<max>DIGIT - * grammar (RFC6265 S5.1.1). - * - * The "trailingOK" boolean controls if the grammar accepts a - * "( non-digit *OCTET )" trailer. - */ -function parseDigits(token, minDigits, maxDigits, trailingOK) { - var count = 0; - while (count < token.length) { - var c = token.charCodeAt(count); - // "non-digit = %x00-2F / %x3A-FF" - if (c <= 0x2F || c >= 0x3A) { - break; - } - count++; - } - - // constrain to a minimum and maximum number of digits. - if (count < minDigits || count > maxDigits) { - return null; - } - - if (!trailingOK && count != token.length) { - return null; - } - - return parseInt(token.substr(0,count), 10); -} - -function parseTime(token) { - var parts = token.split(':'); - var result = [0,0,0]; - - /* RF6256 S5.1.1: - * time = hms-time ( non-digit *OCTET ) - * hms-time = time-field ":" time-field ":" time-field - * time-field = 1*2DIGIT - */ - - if (parts.length !== 3) { - return null; - } - - for (var i = 0; i < 3; i++) { - // "time-field" must be strictly "1*2DIGIT", HOWEVER, "hms-time" can be - // followed by "( non-digit *OCTET )" so therefore the last time-field can - // have a trailer - var trailingOK = (i == 2); - var num = parseDigits(parts[i], 1, 2, trailingOK); - if (num === null) { - return null; - } - result[i] = num; - } - - return result; -} - -function parseMonth(token) { - token = String(token).substr(0,3).toLowerCase(); - var num = MONTH_TO_NUM[token]; - return num >= 0 ? num : null; -} - -/* - * RFC6265 S5.1.1 date parser (see RFC for full grammar) - */ -function parseDate(str) { - if (!str) { - return; - } - - /* RFC6265 S5.1.1: - * 2. Process each date-token sequentially in the order the date-tokens - * appear in the cookie-date - */ - var tokens = str.split(DATE_DELIM); - if (!tokens) { - return; - } - - var hour = null; - var minute = null; - var second = null; - var dayOfMonth = null; - var month = null; - var year = null; - - for (var i=0; i<tokens.length; i++) { - var token = tokens[i].trim(); - if (!token.length) { - continue; - } - - var result; - - /* 2.1. If the found-time flag is not set and the token matches the time - * production, set the found-time flag and set the hour- value, - * minute-value, and second-value to the numbers denoted by the digits in - * the date-token, respectively. Skip the remaining sub-steps and continue - * to the next date-token. - */ - if (second === null) { - result = parseTime(token); - if (result) { - hour = result[0]; - minute = result[1]; - second = result[2]; - continue; - } - } - - /* 2.2. If the found-day-of-month flag is not set and the date-token matches - * the day-of-month production, set the found-day-of- month flag and set - * the day-of-month-value to the number denoted by the date-token. Skip - * the remaining sub-steps and continue to the next date-token. - */ - if (dayOfMonth === null) { - // "day-of-month = 1*2DIGIT ( non-digit *OCTET )" - result = parseDigits(token, 1, 2, true); - if (result !== null) { - dayOfMonth = result; - continue; - } - } - - /* 2.3. If the found-month flag is not set and the date-token matches the - * month production, set the found-month flag and set the month-value to - * the month denoted by the date-token. Skip the remaining sub-steps and - * continue to the next date-token. - */ - if (month === null) { - result = parseMonth(token); - if (result !== null) { - month = result; - continue; - } - } - - /* 2.4. If the found-year flag is not set and the date-token matches the - * year production, set the found-year flag and set the year-value to the - * number denoted by the date-token. Skip the remaining sub-steps and - * continue to the next date-token. - */ - if (year === null) { - // "year = 2*4DIGIT ( non-digit *OCTET )" - result = parseDigits(token, 2, 4, true); - if (result !== null) { - year = result; - /* From S5.1.1: - * 3. If the year-value is greater than or equal to 70 and less - * than or equal to 99, increment the year-value by 1900. - * 4. If the year-value is greater than or equal to 0 and less - * than or equal to 69, increment the year-value by 2000. - */ - if (year >= 70 && year <= 99) { - year += 1900; - } else if (year >= 0 && year <= 69) { - year += 2000; - } - } - } - } - - /* RFC 6265 S5.1.1 - * "5. Abort these steps and fail to parse the cookie-date if: - * * at least one of the found-day-of-month, found-month, found- - * year, or found-time flags is not set, - * * the day-of-month-value is less than 1 or greater than 31, - * * the year-value is less than 1601, - * * the hour-value is greater than 23, - * * the minute-value is greater than 59, or - * * the second-value is greater than 59. - * (Note that leap seconds cannot be represented in this syntax.)" - * - * So, in order as above: - */ - if ( - dayOfMonth === null || month === null || year === null || second === null || - dayOfMonth < 1 || dayOfMonth > 31 || - year < 1601 || - hour > 23 || - minute > 59 || - second > 59 - ) { - return; - } - - return new Date(Date.UTC(year, month, dayOfMonth, hour, minute, second)); -} - -function formatDate(date) { - var d = date.getUTCDate(); d = d >= 10 ? d : '0'+d; - var h = date.getUTCHours(); h = h >= 10 ? h : '0'+h; - var m = date.getUTCMinutes(); m = m >= 10 ? m : '0'+m; - var s = date.getUTCSeconds(); s = s >= 10 ? s : '0'+s; - return NUM_TO_DAY[date.getUTCDay()] + ', ' + - d+' '+ NUM_TO_MONTH[date.getUTCMonth()] +' '+ date.getUTCFullYear() +' '+ - h+':'+m+':'+s+' GMT'; -} - -// S5.1.2 Canonicalized Host Names -function canonicalDomain(str) { - if (str == null) { - return null; - } - str = str.trim().replace(/^\./,''); // S4.1.2.3 & S5.2.3: ignore leading . - - // convert to IDN if any non-ASCII characters - if (punycode && /[^\u0001-\u007f]/.test(str)) { - str = punycode.toASCII(str); - } - - return str.toLowerCase(); -} - -// S5.1.3 Domain Matching -function domainMatch(str, domStr, canonicalize) { - if (str == null || domStr == null) { - return null; - } - if (canonicalize !== false) { - str = canonicalDomain(str); - domStr = canonicalDomain(domStr); - } - - /* - * "The domain string and the string are identical. (Note that both the - * domain string and the string will have been canonicalized to lower case at - * this point)" - */ - if (str == domStr) { - return true; - } - - /* "All of the following [three] conditions hold:" (order adjusted from the RFC) */ - - /* "* The string is a host name (i.e., not an IP address)." */ - if (net.isIP(str)) { - return false; - } - - /* "* The domain string is a suffix of the string" */ - var idx = str.indexOf(domStr); - if (idx <= 0) { - return false; // it's a non-match (-1) or prefix (0) - } - - // e.g "a.b.c".indexOf("b.c") === 2 - // 5 === 3+2 - if (str.length !== domStr.length + idx) { // it's not a suffix - return false; - } - - /* "* The last character of the string that is not included in the domain - * string is a %x2E (".") character." */ - if (str.substr(idx-1,1) !== '.') { - return false; - } - - return true; -} - - -// RFC6265 S5.1.4 Paths and Path-Match - -/* - * "The user agent MUST use an algorithm equivalent to the following algorithm - * to compute the default-path of a cookie:" - * - * Assumption: the path (and not query part or absolute uri) is passed in. - */ -function defaultPath(path) { - // "2. If the uri-path is empty or if the first character of the uri-path is not - // a %x2F ("/") character, output %x2F ("/") and skip the remaining steps. - if (!path || path.substr(0,1) !== "/") { - return "/"; - } - - // "3. If the uri-path contains no more than one %x2F ("/") character, output - // %x2F ("/") and skip the remaining step." - if (path === "/") { - return path; - } - - var rightSlash = path.lastIndexOf("/"); - if (rightSlash === 0) { - return "/"; - } - - // "4. Output the characters of the uri-path from the first character up to, - // but not including, the right-most %x2F ("/")." - return path.slice(0, rightSlash); -} - -function trimTerminator(str) { - for (var t = 0; t < TERMINATORS.length; t++) { - var terminatorIdx = str.indexOf(TERMINATORS[t]); - if (terminatorIdx !== -1) { - str = str.substr(0,terminatorIdx); - } - } - - return str; -} - -function parseCookiePair(cookiePair, looseMode) { - cookiePair = trimTerminator(cookiePair); - - var firstEq = cookiePair.indexOf('='); - if (looseMode) { - if (firstEq === 0) { // '=' is immediately at start - cookiePair = cookiePair.substr(1); - firstEq = cookiePair.indexOf('='); // might still need to split on '=' - } - } else { // non-loose mode - if (firstEq <= 0) { // no '=' or is at start - return; // needs to have non-empty "cookie-name" - } - } - - var cookieName, cookieValue; - if (firstEq <= 0) { - cookieName = ""; - cookieValue = cookiePair.trim(); - } else { - cookieName = cookiePair.substr(0, firstEq).trim(); - cookieValue = cookiePair.substr(firstEq+1).trim(); - } - - if (CONTROL_CHARS.test(cookieName) || CONTROL_CHARS.test(cookieValue)) { - return; - } - - var c = new Cookie(); - c.key = cookieName; - c.value = cookieValue; - return c; -} - -function parse(str, options) { - if (!options || typeof options !== 'object') { - options = {}; - } - str = str.trim(); - - // We use a regex to parse the "name-value-pair" part of S5.2 - var firstSemi = str.indexOf(';'); // S5.2 step 1 - var cookiePair = (firstSemi === -1) ? str : str.substr(0, firstSemi); - var c = parseCookiePair(cookiePair, !!options.loose); - if (!c) { - return; - } - - if (firstSemi === -1) { - return c; - } - - // S5.2.3 "unparsed-attributes consist of the remainder of the set-cookie-string - // (including the %x3B (";") in question)." plus later on in the same section - // "discard the first ";" and trim". - var unparsed = str.slice(firstSemi + 1).trim(); - - // "If the unparsed-attributes string is empty, skip the rest of these - // steps." - if (unparsed.length === 0) { - return c; - } - - /* - * S5.2 says that when looping over the items "[p]rocess the attribute-name - * and attribute-value according to the requirements in the following - * subsections" for every item. Plus, for many of the individual attributes - * in S5.3 it says to use the "attribute-value of the last attribute in the - * cookie-attribute-list". Therefore, in this implementation, we overwrite - * the previous value. - */ - var cookie_avs = unparsed.split(';'); - while (cookie_avs.length) { - var av = cookie_avs.shift().trim(); - if (av.length === 0) { // happens if ";;" appears - continue; - } - var av_sep = av.indexOf('='); - var av_key, av_value; - - if (av_sep === -1) { - av_key = av; - av_value = null; - } else { - av_key = av.substr(0,av_sep); - av_value = av.substr(av_sep+1); - } - - av_key = av_key.trim().toLowerCase(); - - if (av_value) { - av_value = av_value.trim(); - } - - switch(av_key) { - case 'expires': // S5.2.1 - if (av_value) { - var exp = parseDate(av_value); - // "If the attribute-value failed to parse as a cookie date, ignore the - // cookie-av." - if (exp) { - // over and underflow not realistically a concern: V8's getTime() seems to - // store something larger than a 32-bit time_t (even with 32-bit node) - c.expires = exp; - } - } - break; - - case 'max-age': // S5.2.2 - if (av_value) { - // "If the first character of the attribute-value is not a DIGIT or a "-" - // character ...[or]... If the remainder of attribute-value contains a - // non-DIGIT character, ignore the cookie-av." - if (/^-?[0-9]+$/.test(av_value)) { - var delta = parseInt(av_value, 10); - // "If delta-seconds is less than or equal to zero (0), let expiry-time - // be the earliest representable date and time." - c.setMaxAge(delta); - } - } - break; - - case 'domain': // S5.2.3 - // "If the attribute-value is empty, the behavior is undefined. However, - // the user agent SHOULD ignore the cookie-av entirely." - if (av_value) { - // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E - // (".") character." - var domain = av_value.trim().replace(/^\./, ''); - if (domain) { - // "Convert the cookie-domain to lower case." - c.domain = domain.toLowerCase(); - } - } - break; - - case 'path': // S5.2.4 - /* - * "If the attribute-value is empty or if the first character of the - * attribute-value is not %x2F ("/"): - * Let cookie-path be the default-path. - * Otherwise: - * Let cookie-path be the attribute-value." - * - * We'll represent the default-path as null since it depends on the - * context of the parsing. - */ - c.path = av_value && av_value[0] === "/" ? av_value : null; - break; - - case 'secure': // S5.2.5 - /* - * "If the attribute-name case-insensitively matches the string "Secure", - * the user agent MUST append an attribute to the cookie-attribute-list - * with an attribute-name of Secure and an empty attribute-value." - */ - c.secure = true; - break; - - case 'httponly': // S5.2.6 -- effectively the same as 'secure' - c.httpOnly = true; - break; - - default: - c.extensions = c.extensions || []; - c.extensions.push(av); - break; - } - } - - return c; -} - -// avoid the V8 deoptimization monster! -function jsonParse(str) { - var obj; - try { - obj = JSON.parse(str); - } catch (e) { - return e; - } - return obj; -} - -function fromJSON(str) { - if (!str) { - return null; - } - - var obj; - if (typeof str === 'string') { - obj = jsonParse(str); - if (obj instanceof Error) { - return null; - } - } else { - // assume it's an Object - obj = str; - } - - var c = new Cookie(); - for (var i=0; i<Cookie.serializableProperties.length; i++) { - var prop = Cookie.serializableProperties[i]; - if (obj[prop] === undefined || - obj[prop] === Cookie.prototype[prop]) - { - continue; // leave as prototype default - } - - if (prop === 'expires' || - prop === 'creation' || - prop === 'lastAccessed') - { - if (obj[prop] === null) { - c[prop] = null; - } else { - c[prop] = obj[prop] == "Infinity" ? - "Infinity" : new Date(obj[prop]); - } - } else { - c[prop] = obj[prop]; - } - } - - return c; -} - -/* Section 5.4 part 2: - * "* Cookies with longer paths are listed before cookies with - * shorter paths. - * - * * Among cookies that have equal-length path fields, cookies with - * earlier creation-times are listed before cookies with later - * creation-times." - */ - -function cookieCompare(a,b) { - var cmp = 0; - - // descending for length: b CMP a - var aPathLen = a.path ? a.path.length : 0; - var bPathLen = b.path ? b.path.length : 0; - cmp = bPathLen - aPathLen; - if (cmp !== 0) { - return cmp; - } - - // ascending for time: a CMP b - var aTime = a.creation ? a.creation.getTime() : MAX_TIME; - var bTime = b.creation ? b.creation.getTime() : MAX_TIME; - cmp = aTime - bTime; - if (cmp !== 0) { - return cmp; - } - - // break ties for the same millisecond (precision of JavaScript's clock) - cmp = a.creationIndex - b.creationIndex; - - return cmp; -} - -// Gives the permutation of all possible pathMatch()es of a given path. The -// array is in longest-to-shortest order. Handy for indexing. -function permutePath(path) { - if (path === '/') { - return ['/']; - } - if (path.lastIndexOf('/') === path.length-1) { - path = path.substr(0,path.length-1); - } - var permutations = [path]; - while (path.length > 1) { - var lindex = path.lastIndexOf('/'); - if (lindex === 0) { - break; - } - path = path.substr(0,lindex); - permutations.push(path); - } - permutations.push('/'); - return permutations; -} - -function getCookieContext(url) { - if (url instanceof Object) { - return url; - } - // NOTE: decodeURI will throw on malformed URIs (see GH-32). - // Therefore, we will just skip decoding for such URIs. - try { - url = decodeURI(url); - } - catch(err) { - // Silently swallow error - } - - return urlParse(url); -} - -function Cookie(options) { - options = options || {}; - - Object.keys(options).forEach(function(prop) { - if (Cookie.prototype.hasOwnProperty(prop) && - Cookie.prototype[prop] !== options[prop] && - prop.substr(0,1) !== '_') - { - this[prop] = options[prop]; - } - }, this); - - this.creation = this.creation || new Date(); - - // used to break creation ties in cookieCompare(): - Object.defineProperty(this, 'creationIndex', { - configurable: false, - enumerable: false, // important for assert.deepEqual checks - writable: true, - value: ++Cookie.cookiesCreated - }); -} - -Cookie.cookiesCreated = 0; // incremented each time a cookie is created - -Cookie.parse = parse; -Cookie.fromJSON = fromJSON; - -Cookie.prototype.key = ""; -Cookie.prototype.value = ""; - -// the order in which the RFC has them: -Cookie.prototype.expires = "Infinity"; // coerces to literal Infinity -Cookie.prototype.maxAge = null; // takes precedence over expires for TTL -Cookie.prototype.domain = null; -Cookie.prototype.path = null; -Cookie.prototype.secure = false; -Cookie.prototype.httpOnly = false; -Cookie.prototype.extensions = null; - -// set by the CookieJar: -Cookie.prototype.hostOnly = null; // boolean when set -Cookie.prototype.pathIsDefault = null; // boolean when set -Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse -Cookie.prototype.lastAccessed = null; // Date when set -Object.defineProperty(Cookie.prototype, 'creationIndex', { - configurable: true, - enumerable: false, - writable: true, - value: 0 -}); - -Cookie.serializableProperties = Object.keys(Cookie.prototype) - .filter(function(prop) { - return !( - Cookie.prototype[prop] instanceof Function || - prop === 'creationIndex' || - prop.substr(0,1) === '_' - ); - }); - -Cookie.prototype.inspect = function inspect() { - var now = Date.now(); - return 'Cookie="'+this.toString() + - '; hostOnly='+(this.hostOnly != null ? this.hostOnly : '?') + - '; aAge='+(this.lastAccessed ? (now-this.lastAccessed.getTime())+'ms' : '?') + - '; cAge='+(this.creation ? (now-this.creation.getTime())+'ms' : '?') + - '"'; -}; - -// Use the new custom inspection symbol to add the custom inspect function if -// available. -if (util.inspect.custom) { - Cookie.prototype[util.inspect.custom] = Cookie.prototype.inspect; -} - -Cookie.prototype.toJSON = function() { - var obj = {}; - - var props = Cookie.serializableProperties; - for (var i=0; i<props.length; i++) { - var prop = props[i]; - if (this[prop] === Cookie.prototype[prop]) { - continue; // leave as prototype default - } - - if (prop === 'expires' || - prop === 'creation' || - prop === 'lastAccessed') - { - if (this[prop] === null) { - obj[prop] = null; - } else { - obj[prop] = this[prop] == "Infinity" ? // intentionally not === - "Infinity" : this[prop].toISOString(); - } - } else if (prop === 'maxAge') { - if (this[prop] !== null) { - // again, intentionally not === - obj[prop] = (this[prop] == Infinity || this[prop] == -Infinity) ? - this[prop].toString() : this[prop]; - } - } else { - if (this[prop] !== Cookie.prototype[prop]) { - obj[prop] = this[prop]; - } - } - } - - return obj; -}; - -Cookie.prototype.clone = function() { - return fromJSON(this.toJSON()); -}; - -Cookie.prototype.validate = function validate() { - if (!COOKIE_OCTETS.test(this.value)) { - return false; - } - if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) { - return false; - } - if (this.maxAge != null && this.maxAge <= 0) { - return false; // "Max-Age=" non-zero-digit *DIGIT - } - if (this.path != null && !PATH_VALUE.test(this.path)) { - return false; - } - - var cdomain = this.cdomain(); - if (cdomain) { - if (cdomain.match(/\.$/)) { - return false; // S4.1.2.3 suggests that this is bad. domainMatch() tests confirm this - } - var suffix = pubsuffix.getPublicSuffix(cdomain); - if (suffix == null) { // it's a public suffix - return false; - } - } - return true; -}; - -Cookie.prototype.setExpires = function setExpires(exp) { - if (exp instanceof Date) { - this.expires = exp; - } else { - this.expires = parseDate(exp) || "Infinity"; - } -}; - -Cookie.prototype.setMaxAge = function setMaxAge(age) { - if (age === Infinity || age === -Infinity) { - this.maxAge = age.toString(); // so JSON.stringify() works - } else { - this.maxAge = age; - } -}; - -// gives Cookie header format -Cookie.prototype.cookieString = function cookieString() { - var val = this.value; - if (val == null) { - val = ''; - } - if (this.key === '') { - return val; - } - return this.key+'='+val; -}; - -// gives Set-Cookie header format -Cookie.prototype.toString = function toString() { - var str = this.cookieString(); - - if (this.expires != Infinity) { - if (this.expires instanceof Date) { - str += '; Expires='+formatDate(this.expires); - } else { - str += '; Expires='+this.expires; - } - } - - if (this.maxAge != null && this.maxAge != Infinity) { - str += '; Max-Age='+this.maxAge; - } - - if (this.domain && !this.hostOnly) { - str += '; Domain='+this.domain; - } - if (this.path) { - str += '; Path='+this.path; - } - - if (this.secure) { - str += '; Secure'; - } - if (this.httpOnly) { - str += '; HttpOnly'; - } - if (this.extensions) { - this.extensions.forEach(function(ext) { - str += '; '+ext; - }); - } - - return str; -}; - -// TTL() partially replaces the "expiry-time" parts of S5.3 step 3 (setCookie() -// elsewhere) -// S5.3 says to give the "latest representable date" for which we use Infinity -// For "expired" we use 0 -Cookie.prototype.TTL = function TTL(now) { - /* RFC6265 S4.1.2.2 If a cookie has both the Max-Age and the Expires - * attribute, the Max-Age attribute has precedence and controls the - * expiration date of the cookie. - * (Concurs with S5.3 step 3) - */ - if (this.maxAge != null) { - return this.maxAge<=0 ? 0 : this.maxAge*1000; - } - - var expires = this.expires; - if (expires != Infinity) { - if (!(expires instanceof Date)) { - expires = parseDate(expires) || Infinity; - } - - if (expires == Infinity) { - return Infinity; - } - - return expires.getTime() - (now || Date.now()); - } - - return Infinity; -}; - -// expiryTime() replaces the "expiry-time" parts of S5.3 step 3 (setCookie() -// elsewhere) -Cookie.prototype.expiryTime = function expiryTime(now) { - if (this.maxAge != null) { - var relativeTo = now || this.creation || new Date(); - var age = (this.maxAge <= 0) ? -Infinity : this.maxAge*1000; - return relativeTo.getTime() + age; - } - - if (this.expires == Infinity) { - return Infinity; - } - return this.expires.getTime(); -}; - -// expiryDate() replaces the "expiry-time" parts of S5.3 step 3 (setCookie() -// elsewhere), except it returns a Date -Cookie.prototype.expiryDate = function expiryDate(now) { - var millisec = this.expiryTime(now); - if (millisec == Infinity) { - return new Date(MAX_TIME); - } else if (millisec == -Infinity) { - return new Date(MIN_TIME); - } else { - return new Date(millisec); - } -}; - -// This replaces the "persistent-flag" parts of S5.3 step 3 -Cookie.prototype.isPersistent = function isPersistent() { - return (this.maxAge != null || this.expires != Infinity); -}; - -// Mostly S5.1.2 and S5.2.3: -Cookie.prototype.cdomain = -Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() { - if (this.domain == null) { - return null; - } - return canonicalDomain(this.domain); -}; - -function CookieJar(store, options) { - if (typeof options === "boolean") { - options = {rejectPublicSuffixes: options}; - } else if (options == null) { - options = {}; - } - if (options.rejectPublicSuffixes != null) { - this.rejectPublicSuffixes = options.rejectPublicSuffixes; - } - if (options.looseMode != null) { - this.enableLooseMode = options.looseMode; - } - - if (!store) { - store = new MemoryCookieStore(); - } - this.store = store; -} -CookieJar.prototype.store = null; -CookieJar.prototype.rejectPublicSuffixes = true; -CookieJar.prototype.enableLooseMode = false; -var CAN_BE_SYNC = []; - -CAN_BE_SYNC.push('setCookie'); -CookieJar.prototype.setCookie = function(cookie, url, options, cb) { - var err; - var context = getCookieContext(url); - if (options instanceof Function) { - cb = options; - options = {}; - } - - var host = canonicalDomain(context.hostname); - var loose = this.enableLooseMode; - if (options.loose != null) { - loose = options.loose; - } - - // S5.3 step 1 - if (!(cookie instanceof Cookie)) { - cookie = Cookie.parse(cookie, { loose: loose }); - } - if (!cookie) { - err = new Error("Cookie failed to parse"); - return cb(options.ignoreError ? null : err); - } - - // S5.3 step 2 - var now = options.now || new Date(); // will assign later to save effort in the face of errors - - // S5.3 step 3: NOOP; persistent-flag and expiry-time is handled by getCookie() - - // S5.3 step 4: NOOP; domain is null by default - - // S5.3 step 5: public suffixes - if (this.rejectPublicSuffixes && cookie.domain) { - var suffix = pubsuffix.getPublicSuffix(cookie.cdomain()); - if (suffix == null) { // e.g. "com" - err = new Error("Cookie has domain set to a public suffix"); - return cb(options.ignoreError ? null : err); - } - } - - // S5.3 step 6: - if (cookie.domain) { - if (!domainMatch(host, cookie.cdomain(), false)) { - err = new Error("Cookie not in this host's domain. Cookie:"+cookie.cdomain()+" Request:"+host); - return cb(options.ignoreError ? null : err); - } - - if (cookie.hostOnly == null) { // don't reset if already set - cookie.hostOnly = false; - } - - } else { - cookie.hostOnly = true; - cookie.domain = host; - } - - //S5.2.4 If the attribute-value is empty or if the first character of the - //attribute-value is not %x2F ("/"): - //Let cookie-path be the default-path. - if (!cookie.path || cookie.path[0] !== '/') { - cookie.path = defaultPath(context.pathname); - cookie.pathIsDefault = true; - } - - // S5.3 step 8: NOOP; secure attribute - // S5.3 step 9: NOOP; httpOnly attribute - - // S5.3 step 10 - if (options.http === false && cookie.httpOnly) { - err = new Error("Cookie is HttpOnly and this isn't an HTTP API"); - return cb(options.ignoreError ? null : err); - } - - var store = this.store; - - if (!store.updateCookie) { - store.updateCookie = function(oldCookie, newCookie, cb) { - this.putCookie(newCookie, cb); - }; - } - - function withCookie(err, oldCookie) { - if (err) { - return cb(err); - } - - var next = function(err) { - if (err) { - return cb(err); - } else { - cb(null, cookie); - } - }; - - if (oldCookie) { - // S5.3 step 11 - "If the cookie store contains a cookie with the same name, - // domain, and path as the newly created cookie:" - if (options.http === false && oldCookie.httpOnly) { // step 11.2 - err = new Error("old Cookie is HttpOnly and this isn't an HTTP API"); - return cb(options.ignoreError ? null : err); - } - cookie.creation = oldCookie.creation; // step 11.3 - cookie.creationIndex = oldCookie.creationIndex; // preserve tie-breaker - cookie.lastAccessed = now; - // Step 11.4 (delete cookie) is implied by just setting the new one: - store.updateCookie(oldCookie, cookie, next); // step 12 - - } else { - cookie.creation = cookie.lastAccessed = now; - store.putCookie(cookie, next); // step 12 - } - } - - store.findCookie(cookie.domain, cookie.path, cookie.key, withCookie); -}; - -// RFC6365 S5.4 -CAN_BE_SYNC.push('getCookies'); -CookieJar.prototype.getCookies = function(url, options, cb) { - var context = getCookieContext(url); - if (options instanceof Function) { - cb = options; - options = {}; - } - - var host = canonicalDomain(context.hostname); - var path = context.pathname || '/'; - - var secure = options.secure; - if (secure == null && context.protocol && - (context.protocol == 'https:' || context.protocol == 'wss:')) - { - secure = true; - } - - var http = options.http; - if (http == null) { - http = true; - } - - var now = options.now || Date.now(); - var expireCheck = options.expire !== false; - var allPaths = !!options.allPaths; - var store = this.store; - - function matchingCookie(c) { - // "Either: - // The cookie's host-only-flag is true and the canonicalized - // request-host is identical to the cookie's domain. - // Or: - // The cookie's host-only-flag is false and the canonicalized - // request-host domain-matches the cookie's domain." - if (c.hostOnly) { - if (c.domain != host) { - return false; - } - } else { - if (!domainMatch(host, c.domain, false)) { - return false; - } - } - - // "The request-uri's path path-matches the cookie's path." - if (!allPaths && !pathMatch(path, c.path)) { - return false; - } - - // "If the cookie's secure-only-flag is true, then the request-uri's - // scheme must denote a "secure" protocol" - if (c.secure && !secure) { - return false; - } - - // "If the cookie's http-only-flag is true, then exclude the cookie if the - // cookie-string is being generated for a "non-HTTP" API" - if (c.httpOnly && !http) { - return false; - } - - // deferred from S5.3 - // non-RFC: allow retention of expired cookies by choice - if (expireCheck && c.expiryTime() <= now) { - store.removeCookie(c.domain, c.path, c.key, function(){}); // result ignored - return false; - } - - return true; - } - - store.findCookies(host, allPaths ? null : path, function(err,cookies) { - if (err) { - return cb(err); - } - - cookies = cookies.filter(matchingCookie); - - // sorting of S5.4 part 2 - if (options.sort !== false) { - cookies = cookies.sort(cookieCompare); - } - - // S5.4 part 3 - var now = new Date(); - cookies.forEach(function(c) { - c.lastAccessed = now; - }); - // TODO persist lastAccessed - - cb(null,cookies); - }); -}; - -CAN_BE_SYNC.push('getCookieString'); -CookieJar.prototype.getCookieString = function(/*..., cb*/) { - var args = Array.prototype.slice.call(arguments,0); - var cb = args.pop(); - var next = function(err,cookies) { - if (err) { - cb(err); - } else { - cb(null, cookies - .sort(cookieCompare) - .map(function(c){ - return c.cookieString(); - }) - .join('; ')); - } - }; - args.push(next); - this.getCookies.apply(this,args); -}; - -CAN_BE_SYNC.push('getSetCookieStrings'); -CookieJar.prototype.getSetCookieStrings = function(/*..., cb*/) { - var args = Array.prototype.slice.call(arguments,0); - var cb = args.pop(); - var next = function(err,cookies) { - if (err) { - cb(err); - } else { - cb(null, cookies.map(function(c){ - return c.toString(); - })); - } - }; - args.push(next); - this.getCookies.apply(this,args); -}; - -CAN_BE_SYNC.push('serialize'); -CookieJar.prototype.serialize = function(cb) { - var type = this.store.constructor.name; - if (type === 'Object') { - type = null; - } - - // update README.md "Serialization Format" if you change this, please! - var serialized = { - // The version of tough-cookie that serialized this jar. Generally a good - // practice since future versions can make data import decisions based on - // known past behavior. When/if this matters, use `semver`. - version: 'tough-cookie@'+VERSION, - - // add the store type, to make humans happy: - storeType: type, - - // CookieJar configuration: - rejectPublicSuffixes: !!this.rejectPublicSuffixes, - - // this gets filled from getAllCookies: - cookies: [] - }; - - if (!(this.store.getAllCookies && - typeof this.store.getAllCookies === 'function')) - { - return cb(new Error('store does not support getAllCookies and cannot be serialized')); - } - - this.store.getAllCookies(function(err,cookies) { - if (err) { - return cb(err); - } - - serialized.cookies = cookies.map(function(cookie) { - // convert to serialized 'raw' cookies - cookie = (cookie instanceof Cookie) ? cookie.toJSON() : cookie; - - // Remove the index so new ones get assigned during deserialization - delete cookie.creationIndex; - - return cookie; - }); - - return cb(null, serialized); - }); -}; - -// well-known name that JSON.stringify calls -CookieJar.prototype.toJSON = function() { - return this.serializeSync(); -}; - -// use the class method CookieJar.deserialize instead of calling this directly -CAN_BE_SYNC.push('_importCookies'); -CookieJar.prototype._importCookies = function(serialized, cb) { - var jar = this; - var cookies = serialized.cookies; - if (!cookies || !Array.isArray(cookies)) { - return cb(new Error('serialized jar has no cookies array')); - } - cookies = cookies.slice(); // do not modify the original - - function putNext(err) { - if (err) { - return cb(err); - } - - if (!cookies.length) { - return cb(err, jar); - } - - var cookie; - try { - cookie = fromJSON(cookies.shift()); - } catch (e) { - return cb(e); - } - - if (cookie === null) { - return putNext(null); // skip this cookie - } - - jar.store.putCookie(cookie, putNext); - } - - putNext(); -}; - -CookieJar.deserialize = function(strOrObj, store, cb) { - if (arguments.length !== 3) { - // store is optional - cb = store; - store = null; - } - - var serialized; - if (typeof strOrObj === 'string') { - serialized = jsonParse(strOrObj); - if (serialized instanceof Error) { - return cb(serialized); - } - } else { - serialized = strOrObj; - } - - var jar = new CookieJar(store, serialized.rejectPublicSuffixes); - jar._importCookies(serialized, function(err) { - if (err) { - return cb(err); - } - cb(null, jar); - }); -}; - -CookieJar.deserializeSync = function(strOrObj, store) { - var serialized = typeof strOrObj === 'string' ? - JSON.parse(strOrObj) : strOrObj; - var jar = new CookieJar(store, serialized.rejectPublicSuffixes); - - // catch this mistake early: - if (!jar.store.synchronous) { - throw new Error('CookieJar store is not synchronous; use async API instead.'); - } - - jar._importCookiesSync(serialized); - return jar; -}; -CookieJar.fromJSON = CookieJar.deserializeSync; - -CookieJar.prototype.clone = function(newStore, cb) { - if (arguments.length === 1) { - cb = newStore; - newStore = null; - } - - this.serialize(function(err,serialized) { - if (err) { - return cb(err); - } - CookieJar.deserialize(serialized, newStore, cb); - }); -}; - -CAN_BE_SYNC.push('removeAllCookies'); -CookieJar.prototype.removeAllCookies = function(cb) { - var store = this.store; - - // Check that the store implements its own removeAllCookies(). The default - // implementation in Store will immediately call the callback with a "not - // implemented" Error. - if (store.removeAllCookies instanceof Function && - store.removeAllCookies !== Store.prototype.removeAllCookies) - { - return store.removeAllCookies(cb); - } - - store.getAllCookies(function(err, cookies) { - if (err) { - return cb(err); - } - - if (cookies.length === 0) { - return cb(null); - } - - var completedCount = 0; - var removeErrors = []; - - function removeCookieCb(removeErr) { - if (removeErr) { - removeErrors.push(removeErr); - } - - completedCount++; - - if (completedCount === cookies.length) { - return cb(removeErrors.length ? removeErrors[0] : null); - } - } - - cookies.forEach(function(cookie) { - store.removeCookie(cookie.domain, cookie.path, cookie.key, removeCookieCb); - }); - }); -}; - -CookieJar.prototype._cloneSync = syncWrap('clone'); -CookieJar.prototype.cloneSync = function(newStore) { - if (!newStore.synchronous) { - throw new Error('CookieJar clone destination store is not synchronous; use async API instead.'); - } - return this._cloneSync(newStore); -}; - -// Use a closure to provide a true imperative API for synchronous stores. -function syncWrap(method) { - return function() { - if (!this.store.synchronous) { - throw new Error('CookieJar store is not synchronous; use async API instead.'); - } - - var args = Array.prototype.slice.call(arguments); - var syncErr, syncResult; - args.push(function syncCb(err, result) { - syncErr = err; - syncResult = result; - }); - this[method].apply(this, args); - - if (syncErr) { - throw syncErr; - } - return syncResult; - }; -} - -// wrap all declared CAN_BE_SYNC methods in the sync wrapper -CAN_BE_SYNC.forEach(function(method) { - CookieJar.prototype[method+'Sync'] = syncWrap(method); -}); - -exports.version = VERSION; -exports.CookieJar = CookieJar; -exports.Cookie = Cookie; -exports.Store = Store; -exports.MemoryCookieStore = MemoryCookieStore; -exports.parseDate = parseDate; -exports.formatDate = formatDate; -exports.parse = parse; -exports.fromJSON = fromJSON; -exports.domainMatch = domainMatch; -exports.defaultPath = defaultPath; -exports.pathMatch = pathMatch; -exports.getPublicSuffix = pubsuffix.getPublicSuffix; -exports.cookieCompare = cookieCompare; -exports.permuteDomain = require('./permuteDomain').permuteDomain; -exports.permutePath = permutePath; -exports.canonicalDomain = canonicalDomain; |