quickjs-tart

quickjs-based runtime for wallet-core logic
Log | Files | Refs | README | LICENSE

cookie.c (48862B)


      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
      9  *
     10  * This software is licensed as described in the file COPYING, which
     11  * you should have received as part of this distribution. The terms
     12  * are also available at https://curl.se/docs/copyright.html.
     13  *
     14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
     15  * copies of the Software, and permit persons to whom the Software is
     16  * furnished to do so, under the terms of the COPYING file.
     17  *
     18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
     19  * KIND, either express or implied.
     20  *
     21  * SPDX-License-Identifier: curl
     22  *
     23  ***************************************************************************/
     24 
     25 /***
     26 
     27 
     28 RECEIVING COOKIE INFORMATION
     29 ============================
     30 
     31 Curl_cookie_init()
     32 
     33         Inits a cookie struct to store data in a local file. This is always
     34         called before any cookies are set.
     35 
     36 Curl_cookie_add()
     37 
     38         Adds a cookie to the in-memory cookie jar.
     39 
     40 
     41 SENDING COOKIE INFORMATION
     42 ==========================
     43 
     44 Curl_cookie_getlist()
     45 
     46         For a given host and path, return a linked list of cookies that
     47         the client should send to the server if used now. The secure
     48         boolean informs the cookie if a secure connection is achieved or
     49         not.
     50 
     51         It shall only return cookies that have not expired.
     52 
     53 Example set of cookies:
     54 
     55     Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
     56     Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
     57     domain=.fidelity.com; path=/ftgw; secure
     58     Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
     59     domain=.fidelity.com; path=/; secure
     60     Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
     61     domain=.fidelity.com; path=/; secure
     62     Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
     63     domain=.fidelity.com; path=/; secure
     64     Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
     65     domain=.fidelity.com; path=/; secure
     66     Set-cookie:
     67     Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
     68     13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
     69 ****/
     70 
     71 
     72 #include "curl_setup.h"
     73 
     74 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
     75 
     76 #include "urldata.h"
     77 #include "cookie.h"
     78 #include "psl.h"
     79 #include "sendf.h"
     80 #include "slist.h"
     81 #include "share.h"
     82 #include "strcase.h"
     83 #include "curl_get_line.h"
     84 #include "curl_memrchr.h"
     85 #include "parsedate.h"
     86 #include "rename.h"
     87 #include "fopen.h"
     88 #include "strdup.h"
     89 #include "llist.h"
     90 #include "curlx/strparse.h"
     91 
     92 /* The last 3 #include files should be in this order */
     93 #include "curl_printf.h"
     94 #include "curl_memory.h"
     95 #include "memdebug.h"
     96 
     97 static void strstore(char **str, const char *newstr, size_t len);
     98 
     99 /* number of seconds in 400 days */
    100 #define COOKIES_MAXAGE (400*24*3600)
    101 
    102 /* Make sure cookies never expire further away in time than 400 days into the
    103    future. (from RFC6265bis draft-19)
    104 
    105    For the sake of easier testing, align the capped time to an even 60 second
    106    boundary.
    107 */
    108 static void cap_expires(time_t now, struct Cookie *co)
    109 {
    110   if((TIME_T_MAX - COOKIES_MAXAGE - 30) > now) {
    111     timediff_t cap = now + COOKIES_MAXAGE;
    112     if(co->expires > cap) {
    113       cap += 30;
    114       co->expires = (cap/60)*60;
    115     }
    116   }
    117 }
    118 
    119 static void freecookie(struct Cookie *co)
    120 {
    121   free(co->domain);
    122   free(co->path);
    123   free(co->spath);
    124   free(co->name);
    125   free(co->value);
    126   free(co);
    127 }
    128 
    129 static bool cookie_tailmatch(const char *cookie_domain,
    130                              size_t cookie_domain_len,
    131                              const char *hostname)
    132 {
    133   size_t hostname_len = strlen(hostname);
    134 
    135   if(hostname_len < cookie_domain_len)
    136     return FALSE;
    137 
    138   if(!curl_strnequal(cookie_domain,
    139                      hostname + hostname_len-cookie_domain_len,
    140                      cookie_domain_len))
    141     return FALSE;
    142 
    143   /*
    144    * A lead char of cookie_domain is not '.'.
    145    * RFC6265 4.1.2.3. The Domain Attribute says:
    146    * For example, if the value of the Domain attribute is
    147    * "example.com", the user agent will include the cookie in the Cookie
    148    * header when making HTTP requests to example.com, www.example.com, and
    149    * www.corp.example.com.
    150    */
    151   if(hostname_len == cookie_domain_len)
    152     return TRUE;
    153   if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
    154     return TRUE;
    155   return FALSE;
    156 }
    157 
    158 /*
    159  * matching cookie path and URL path
    160  * RFC6265 5.1.4 Paths and Path-Match
    161  */
    162 static bool pathmatch(const char *cookie_path, const char *uri_path)
    163 {
    164   size_t cookie_path_len;
    165   size_t uri_path_len;
    166   bool ret = FALSE;
    167 
    168   /* cookie_path must not have last '/' separator. ex: /sample */
    169   cookie_path_len = strlen(cookie_path);
    170   if(1 == cookie_path_len) {
    171     /* cookie_path must be '/' */
    172     return TRUE;
    173   }
    174 
    175   /* #-fragments are already cut off! */
    176   if(0 == strlen(uri_path) || uri_path[0] != '/')
    177     uri_path = "/";
    178 
    179   /*
    180    * here, RFC6265 5.1.4 says
    181    *  4. Output the characters of the uri-path from the first character up
    182    *     to, but not including, the right-most %x2F ("/").
    183    *  but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
    184    *  without redirect.
    185    *  Ignore this algorithm because /hoge is uri path for this case
    186    *  (uri path is not /).
    187    */
    188 
    189   uri_path_len = strlen(uri_path);
    190 
    191   if(uri_path_len < cookie_path_len)
    192     goto pathmatched;
    193 
    194   /* not using checkprefix() because matching should be case-sensitive */
    195   if(strncmp(cookie_path, uri_path, cookie_path_len))
    196     goto pathmatched;
    197 
    198   /* The cookie-path and the uri-path are identical. */
    199   if(cookie_path_len == uri_path_len) {
    200     ret = TRUE;
    201     goto pathmatched;
    202   }
    203 
    204   /* here, cookie_path_len < uri_path_len */
    205   if(uri_path[cookie_path_len] == '/') {
    206     ret = TRUE;
    207     goto pathmatched;
    208   }
    209 
    210 pathmatched:
    211   return ret;
    212 }
    213 
    214 /*
    215  * Return the top-level domain, for optimal hashing.
    216  */
    217 static const char *get_top_domain(const char * const domain, size_t *outlen)
    218 {
    219   size_t len = 0;
    220   const char *first = NULL, *last;
    221 
    222   if(domain) {
    223     len = strlen(domain);
    224     last = memrchr(domain, '.', len);
    225     if(last) {
    226       first = memrchr(domain, '.', (last - domain));
    227       if(first)
    228         len -= (++first - domain);
    229     }
    230   }
    231 
    232   if(outlen)
    233     *outlen = len;
    234 
    235   return first ? first : domain;
    236 }
    237 
    238 /* Avoid C1001, an "internal error" with MSVC14 */
    239 #if defined(_MSC_VER) && (_MSC_VER == 1900)
    240 #pragma optimize("", off)
    241 #endif
    242 
    243 /*
    244  * A case-insensitive hash for the cookie domains.
    245  */
    246 static size_t cookie_hash_domain(const char *domain, const size_t len)
    247 {
    248   const char *end = domain + len;
    249   size_t h = 5381;
    250 
    251   while(domain < end) {
    252     size_t j = (size_t)Curl_raw_toupper(*domain++);
    253     h += h << 5;
    254     h ^= j;
    255   }
    256 
    257   return (h % COOKIE_HASH_SIZE);
    258 }
    259 
    260 #if defined(_MSC_VER) && (_MSC_VER == 1900)
    261 #pragma optimize("", on)
    262 #endif
    263 
    264 /*
    265  * Hash this domain.
    266  */
    267 static size_t cookiehash(const char * const domain)
    268 {
    269   const char *top;
    270   size_t len;
    271 
    272   if(!domain || Curl_host_is_ipnum(domain))
    273     return 0;
    274 
    275   top = get_top_domain(domain, &len);
    276   return cookie_hash_domain(top, len);
    277 }
    278 
    279 /*
    280  * cookie path sanitize
    281  */
    282 static char *sanitize_cookie_path(const char *cookie_path)
    283 {
    284   size_t len = strlen(cookie_path);
    285 
    286   /* some sites send path attribute within '"'. */
    287   if(cookie_path[0] == '\"') {
    288     cookie_path++;
    289     len--;
    290   }
    291   if(len && (cookie_path[len - 1] == '\"'))
    292     len--;
    293 
    294   /* RFC6265 5.2.4 The Path Attribute */
    295   if(cookie_path[0] != '/')
    296     /* Let cookie-path be the default-path. */
    297     return strdup("/");
    298 
    299   /* remove trailing slash */
    300   /* convert /hoge/ to /hoge */
    301   if(len && cookie_path[len - 1] == '/')
    302     len--;
    303 
    304   return Curl_memdup0(cookie_path, len);
    305 }
    306 
    307 /*
    308  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
    309  *
    310  * NOTE: OOM or cookie parsing failures are ignored.
    311  */
    312 void Curl_cookie_loadfiles(struct Curl_easy *data)
    313 {
    314   struct curl_slist *list = data->state.cookielist;
    315   if(list) {
    316     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
    317     while(list) {
    318       struct CookieInfo *ci =
    319         Curl_cookie_init(data, list->data, data->cookies,
    320                          data->set.cookiesession);
    321       if(!ci)
    322         /*
    323          * Failure may be due to OOM or a bad cookie; both are ignored
    324          * but only the first should be
    325          */
    326         infof(data, "ignoring failed cookie_init for %s", list->data);
    327       else
    328         data->cookies = ci;
    329       list = list->next;
    330     }
    331     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
    332   }
    333 }
    334 
    335 /*
    336  * strstore
    337  *
    338  * A thin wrapper around strdup which ensures that any memory allocated at
    339  * *str will be freed before the string allocated by strdup is stored there.
    340  * The intended usecase is repeated assignments to the same variable during
    341  * parsing in a last-wins scenario. The caller is responsible for checking
    342  * for OOM errors.
    343  */
    344 static void strstore(char **str, const char *newstr, size_t len)
    345 {
    346   DEBUGASSERT(str);
    347   free(*str);
    348   if(!len) {
    349     len++;
    350     newstr = "";
    351   }
    352   *str = Curl_memdup0(newstr, len);
    353 }
    354 
    355 /*
    356  * remove_expired
    357  *
    358  * Remove expired cookies from the hash by inspecting the expires timestamp on
    359  * each cookie in the hash, freeing and deleting any where the timestamp is in
    360  * the past. If the cookiejar has recorded the next timestamp at which one or
    361  * more cookies expire, then processing will exit early in case this timestamp
    362  * is in the future.
    363  */
    364 static void remove_expired(struct CookieInfo *ci)
    365 {
    366   struct Cookie *co;
    367   curl_off_t now = (curl_off_t)time(NULL);
    368   unsigned int i;
    369 
    370   /*
    371    * If the earliest expiration timestamp in the jar is in the future we can
    372    * skip scanning the whole jar and instead exit early as there will not be
    373    * any cookies to evict. If we need to evict however, reset the
    374    * next_expiration counter in order to track the next one. In case the
    375    * recorded first expiration is the max offset, then perform the safe
    376    * fallback of checking all cookies.
    377    */
    378   if(now < ci->next_expiration &&
    379      ci->next_expiration != CURL_OFF_T_MAX)
    380     return;
    381   else
    382     ci->next_expiration = CURL_OFF_T_MAX;
    383 
    384   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
    385     struct Curl_llist_node *n;
    386     struct Curl_llist_node *e = NULL;
    387 
    388     for(n = Curl_llist_head(&ci->cookielist[i]); n; n = e) {
    389       co = Curl_node_elem(n);
    390       e = Curl_node_next(n);
    391       if(co->expires && co->expires < now) {
    392         Curl_node_remove(n);
    393         freecookie(co);
    394         ci->numcookies--;
    395       }
    396       else {
    397         /*
    398          * If this cookie has an expiration timestamp earlier than what we
    399          * have seen so far then record it for the next round of expirations.
    400          */
    401         if(co->expires && co->expires < ci->next_expiration)
    402           ci->next_expiration = co->expires;
    403       }
    404     }
    405   }
    406 }
    407 
    408 #ifndef USE_LIBPSL
    409 /* Make sure domain contains a dot or is localhost. */
    410 static bool bad_domain(const char *domain, size_t len)
    411 {
    412   if((len == 9) && curl_strnequal(domain, "localhost", 9))
    413     return FALSE;
    414   else {
    415     /* there must be a dot present, but that dot must not be a trailing dot */
    416     char *dot = memchr(domain, '.', len);
    417     if(dot) {
    418       size_t i = dot - domain;
    419       if((len - i) > 1)
    420         /* the dot is not the last byte */
    421         return FALSE;
    422     }
    423   }
    424   return TRUE;
    425 }
    426 #endif
    427 
    428 /*
    429   RFC 6265 section 4.1.1 says a server should accept this range:
    430 
    431   cookie-octet    = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
    432 
    433   But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
    434   fine. The prime reason for filtering out control bytes is that some HTTP
    435   servers return 400 for requests that contain such.
    436 */
    437 static bool invalid_octets(const char *ptr)
    438 {
    439   const unsigned char *p = (const unsigned char *)ptr;
    440   /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
    441   while(*p) {
    442     if(((*p != 9) && (*p < 0x20)) || (*p == 0x7f))
    443       return TRUE;
    444     p++;
    445   }
    446   return FALSE;
    447 }
    448 
    449 #define CERR_OK            0
    450 #define CERR_TOO_LONG      1 /* input line too long */
    451 #define CERR_TAB           2 /* in a wrong place */
    452 #define CERR_TOO_BIG       3 /* name/value too large */
    453 #define CERR_BAD           4 /* deemed incorrect */
    454 #define CERR_NO_SEP        5 /* semicolon problem */
    455 #define CERR_NO_NAME_VALUE 6 /* name or value problem */
    456 #define CERR_INVALID_OCTET 7 /* bad content */
    457 #define CERR_BAD_SECURE    8 /* secure in a bad place */
    458 #define CERR_OUT_OF_MEMORY 9
    459 #define CERR_NO_TAILMATCH  10
    460 #define CERR_COMMENT       11 /* a commented line */
    461 #define CERR_RANGE         12 /* expire range problem */
    462 #define CERR_FIELDS        13 /* incomplete netscape line */
    463 #ifdef USE_LIBPSL
    464 #define CERR_PSL           14 /* a public suffix */
    465 #endif
    466 #define CERR_LIVE_WINS     15
    467 
    468 /* The maximum length we accept a date string for the 'expire' keyword. The
    469    standard date formats are within the 30 bytes range. This adds an extra
    470    margin just to make sure it realistically works with what is used out
    471    there.
    472 */
    473 #define MAX_DATE_LENGTH 80
    474 
    475 static int
    476 parse_cookie_header(struct Curl_easy *data,
    477                     struct Cookie *co,
    478                     struct CookieInfo *ci,
    479                     const char *ptr,
    480                     const char *domain, /* default domain */
    481                     const char *path,   /* full path used when this cookie is
    482                                            set, used to get default path for
    483                                            the cookie unless set */
    484                     bool secure)  /* TRUE if connection is over secure
    485                                      origin */
    486 {
    487   /* This line was read off an HTTP-header */
    488   time_t now;
    489   size_t linelength = strlen(ptr);
    490   if(linelength > MAX_COOKIE_LINE)
    491     /* discard overly long lines at once */
    492     return CERR_TOO_LONG;
    493 
    494   now = time(NULL);
    495   do {
    496     struct Curl_str name;
    497     struct Curl_str val;
    498 
    499     /* we have a <name>=<value> pair or a stand-alone word here */
    500     if(!curlx_str_cspn(&ptr, &name, ";\t\r\n=")) {
    501       bool done = FALSE;
    502       bool sep = FALSE;
    503       curlx_str_trimblanks(&name);
    504 
    505       if(!curlx_str_single(&ptr, '=')) {
    506         sep = TRUE; /* a '=' was used */
    507         if(!curlx_str_cspn(&ptr, &val, ";\r\n")) {
    508           curlx_str_trimblanks(&val);
    509 
    510           /* Reject cookies with a TAB inside the value */
    511           if(memchr(curlx_str(&val), '\t', curlx_strlen(&val))) {
    512             infof(data, "cookie contains TAB, dropping");
    513             return CERR_TAB;
    514           }
    515         }
    516       }
    517       else {
    518         curlx_str_init(&val);
    519       }
    520 
    521       /*
    522        * Check for too long individual name or contents, or too long
    523        * combination of name + contents. Chrome and Firefox support 4095 or
    524        * 4096 bytes combo
    525        */
    526       if(curlx_strlen(&name) >= (MAX_NAME-1) ||
    527          curlx_strlen(&val) >= (MAX_NAME-1) ||
    528          ((curlx_strlen(&name) + curlx_strlen(&val)) > MAX_NAME)) {
    529         infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
    530               curlx_strlen(&name), curlx_strlen(&val));
    531         return CERR_TOO_BIG;
    532       }
    533 
    534       /*
    535        * Check if we have a reserved prefix set before anything else, as we
    536        * otherwise have to test for the prefix in both the cookie name and
    537        * "the rest". Prefixes must start with '__' and end with a '-', so
    538        * only test for names where that can possibly be true.
    539        */
    540       if(!strncmp("__Secure-", curlx_str(&name), 9))
    541         co->prefix_secure = TRUE;
    542       else if(!strncmp("__Host-", curlx_str(&name), 7))
    543         co->prefix_host = TRUE;
    544 
    545       /*
    546        * Use strstore() below to properly deal with received cookie
    547        * headers that have the same string property set more than once,
    548        * and then we use the last one.
    549        */
    550 
    551       if(!co->name) {
    552         /* The very first name/value pair is the actual cookie name */
    553         if(!sep)
    554           /* Bad name/value pair. */
    555           return CERR_NO_SEP;
    556 
    557         strstore(&co->name, curlx_str(&name), curlx_strlen(&name));
    558         strstore(&co->value, curlx_str(&val), curlx_strlen(&val));
    559         done = TRUE;
    560         if(!co->name || !co->value)
    561           return CERR_NO_NAME_VALUE;
    562 
    563         if(invalid_octets(co->value) || invalid_octets(co->name)) {
    564           infof(data, "invalid octets in name/value, cookie dropped");
    565           return CERR_INVALID_OCTET;
    566         }
    567       }
    568       else if(!curlx_strlen(&val)) {
    569         /*
    570          * this was a "<name>=" with no content, and we must allow
    571          * 'secure' and 'httponly' specified this weirdly
    572          */
    573         done = TRUE;
    574         /*
    575          * secure cookies are only allowed to be set when the connection is
    576          * using a secure protocol, or when the cookie is being set by
    577          * reading from file
    578          */
    579         if(curlx_str_casecompare(&name, "secure")) {
    580           if(secure || !ci->running) {
    581             co->secure = TRUE;
    582           }
    583           else {
    584             return CERR_BAD_SECURE;
    585           }
    586         }
    587         else if(curlx_str_casecompare(&name, "httponly"))
    588           co->httponly = TRUE;
    589         else if(sep)
    590           /* there was a '=' so we are not done parsing this field */
    591           done = FALSE;
    592       }
    593       if(done)
    594         ;
    595       else if(curlx_str_casecompare(&name, "path")) {
    596         strstore(&co->path, curlx_str(&val), curlx_strlen(&val));
    597         if(!co->path)
    598           return CERR_OUT_OF_MEMORY;
    599         free(co->spath); /* if this is set again */
    600         co->spath = sanitize_cookie_path(co->path);
    601         if(!co->spath)
    602           return CERR_OUT_OF_MEMORY;
    603       }
    604       else if(curlx_str_casecompare(&name, "domain") && curlx_strlen(&val)) {
    605         bool is_ip;
    606         const char *v = curlx_str(&val);
    607         /*
    608          * Now, we make sure that our host is within the given domain, or
    609          * the given domain is not valid and thus cannot be set.
    610          */
    611 
    612         if('.' == *v)
    613           curlx_str_nudge(&val, 1);
    614 
    615 #ifndef USE_LIBPSL
    616         /*
    617          * Without PSL we do not know when the incoming cookie is set on a
    618          * TLD or otherwise "protected" suffix. To reduce risk, we require a
    619          * dot OR the exact hostname being "localhost".
    620          */
    621         if(bad_domain(curlx_str(&val), curlx_strlen(&val)))
    622           domain = ":";
    623 #endif
    624 
    625         is_ip = Curl_host_is_ipnum(domain ? domain : curlx_str(&val));
    626 
    627         if(!domain
    628            || (is_ip && !strncmp(curlx_str(&val), domain,
    629                                  curlx_strlen(&val)) &&
    630                (curlx_strlen(&val) == strlen(domain)))
    631            || (!is_ip && cookie_tailmatch(curlx_str(&val),
    632                                           curlx_strlen(&val), domain))) {
    633           strstore(&co->domain, curlx_str(&val), curlx_strlen(&val));
    634           if(!co->domain)
    635             return CERR_OUT_OF_MEMORY;
    636 
    637           if(!is_ip)
    638             co->tailmatch = TRUE; /* we always do that if the domain name was
    639                                      given */
    640         }
    641         else {
    642           /*
    643            * We did not get a tailmatch and then the attempted set domain is
    644            * not a domain to which the current host belongs. Mark as bad.
    645            */
    646           infof(data, "skipped cookie with bad tailmatch domain: %s",
    647                 curlx_str(&val));
    648           return CERR_NO_TAILMATCH;
    649         }
    650       }
    651       else if(curlx_str_casecompare(&name, "version")) {
    652         /* just ignore */
    653       }
    654       else if(curlx_str_casecompare(&name, "max-age") && curlx_strlen(&val)) {
    655         /*
    656          * Defined in RFC2109:
    657          *
    658          * Optional. The Max-Age attribute defines the lifetime of the
    659          * cookie, in seconds. The delta-seconds value is a decimal non-
    660          * negative integer. After delta-seconds seconds elapse, the
    661          * client should discard the cookie. A value of zero means the
    662          * cookie should be discarded immediately.
    663          */
    664         int rc;
    665         const char *maxage = curlx_str(&val);
    666         if(*maxage == '\"')
    667           maxage++;
    668         rc = curlx_str_number(&maxage, &co->expires, CURL_OFF_T_MAX);
    669 
    670         switch(rc) {
    671         case STRE_OVERFLOW:
    672           /* overflow, used max value */
    673           co->expires = CURL_OFF_T_MAX;
    674           break;
    675         default:
    676           /* negative or otherwise bad, expire */
    677           co->expires = 1;
    678           break;
    679         case STRE_OK:
    680           if(!co->expires)
    681             /* already expired */
    682             co->expires = 1;
    683           else if(CURL_OFF_T_MAX - now < co->expires)
    684             /* would overflow */
    685             co->expires = CURL_OFF_T_MAX;
    686           else
    687             co->expires += now;
    688           break;
    689         }
    690         cap_expires(now, co);
    691       }
    692       else if(curlx_str_casecompare(&name, "expires") && curlx_strlen(&val)) {
    693         if(!co->expires && (curlx_strlen(&val) < MAX_DATE_LENGTH)) {
    694           /*
    695            * Let max-age have priority.
    696            *
    697            * If the date cannot get parsed for whatever reason, the cookie
    698            * will be treated as a session cookie
    699            */
    700           char dbuf[MAX_DATE_LENGTH + 1];
    701           memcpy(dbuf, curlx_str(&val), curlx_strlen(&val));
    702           dbuf[curlx_strlen(&val)] = 0;
    703           co->expires = Curl_getdate_capped(dbuf);
    704 
    705           /*
    706            * Session cookies have expires set to 0 so if we get that back
    707            * from the date parser let's add a second to make it a
    708            * non-session cookie
    709            */
    710           if(co->expires == 0)
    711             co->expires = 1;
    712           else if(co->expires < 0)
    713             co->expires = 0;
    714           cap_expires(now, co);
    715         }
    716       }
    717 
    718       /*
    719        * Else, this is the second (or more) name we do not know about!
    720        */
    721     }
    722 
    723     if(curlx_str_single(&ptr, ';'))
    724       break;
    725   } while(1);
    726 
    727   if(!co->domain && domain) {
    728     /* no domain was given in the header line, set the default */
    729     co->domain = strdup(domain);
    730     if(!co->domain)
    731       return CERR_OUT_OF_MEMORY;
    732   }
    733 
    734   if(!co->path && path) {
    735     /*
    736      * No path was given in the header line, set the default.
    737      */
    738     const char *endslash = strrchr(path, '/');
    739     if(endslash) {
    740       size_t pathlen = (endslash - path + 1); /* include end slash */
    741       co->path = Curl_memdup0(path, pathlen);
    742       if(co->path) {
    743         co->spath = sanitize_cookie_path(co->path);
    744         if(!co->spath)
    745           return CERR_OUT_OF_MEMORY;
    746       }
    747       else
    748         return CERR_OUT_OF_MEMORY;
    749     }
    750   }
    751 
    752   /*
    753    * If we did not get a cookie name, or a bad one, the this is an illegal
    754    * line so bail out.
    755    */
    756   if(!co->name)
    757     return CERR_BAD;
    758 
    759   data->req.setcookies++;
    760   return CERR_OK;
    761 }
    762 
    763 static int
    764 parse_netscape(struct Cookie *co,
    765                struct CookieInfo *ci,
    766                const char *lineptr,
    767                bool secure)  /* TRUE if connection is over secure
    768                                 origin */
    769 {
    770   /*
    771    * This line is NOT an HTTP header style line, we do offer support for
    772    * reading the odd netscape cookies-file format here
    773    */
    774   const char *ptr, *next;
    775   int fields;
    776   size_t len;
    777 
    778   /*
    779    * In 2008, Internet Explorer introduced HTTP-only cookies to prevent XSS
    780    * attacks. Cookies marked httpOnly are not accessible to JavaScript. In
    781    * Firefox's cookie files, they are prefixed #HttpOnly_ and the rest
    782    * remains as usual, so we skip 10 characters of the line.
    783    */
    784   if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
    785     lineptr += 10;
    786     co->httponly = TRUE;
    787   }
    788 
    789   if(lineptr[0]=='#')
    790     /* do not even try the comments */
    791     return CERR_COMMENT;
    792 
    793   /*
    794    * Now loop through the fields and init the struct we already have
    795    * allocated
    796    */
    797   fields = 0;
    798   for(next = lineptr; next; fields++) {
    799     ptr = next;
    800     len = strcspn(ptr, "\t\r\n");
    801     next = (ptr[len] == '\t' ? &ptr[len + 1] : NULL);
    802     switch(fields) {
    803     case 0:
    804       if(ptr[0]=='.') { /* skip preceding dots */
    805         ptr++;
    806         len--;
    807       }
    808       co->domain = Curl_memdup0(ptr, len);
    809       if(!co->domain)
    810         return CERR_OUT_OF_MEMORY;
    811       break;
    812     case 1:
    813       /*
    814        * flag: A TRUE/FALSE value indicating if all machines within a given
    815        * domain can access the variable. Set TRUE when the cookie says
    816        * .example.com and to false when the domain is complete www.example.com
    817        */
    818       co->tailmatch = !!curl_strnequal(ptr, "TRUE", len);
    819       break;
    820     case 2:
    821       /* The file format allows the path field to remain not filled in */
    822       if(strncmp("TRUE", ptr, len) && strncmp("FALSE", ptr, len)) {
    823         /* only if the path does not look like a boolean option! */
    824         co->path = Curl_memdup0(ptr, len);
    825         if(!co->path)
    826           return CERR_OUT_OF_MEMORY;
    827         else {
    828           co->spath = sanitize_cookie_path(co->path);
    829           if(!co->spath)
    830             return CERR_OUT_OF_MEMORY;
    831         }
    832         break;
    833       }
    834       /* this does not look like a path, make one up! */
    835       co->path = strdup("/");
    836       if(!co->path)
    837         return CERR_OUT_OF_MEMORY;
    838       co->spath = strdup("/");
    839       if(!co->spath)
    840         return CERR_OUT_OF_MEMORY;
    841       fields++; /* add a field and fall down to secure */
    842       FALLTHROUGH();
    843     case 3:
    844       co->secure = FALSE;
    845       if(curl_strnequal(ptr, "TRUE", len)) {
    846         if(secure || ci->running)
    847           co->secure = TRUE;
    848         else
    849           return CERR_BAD_SECURE;
    850       }
    851       break;
    852     case 4:
    853       if(curlx_str_number(&ptr, &co->expires, CURL_OFF_T_MAX))
    854         return CERR_RANGE;
    855       break;
    856     case 5:
    857       co->name = Curl_memdup0(ptr, len);
    858       if(!co->name)
    859         return CERR_OUT_OF_MEMORY;
    860       else {
    861         /* For Netscape file format cookies we check prefix on the name */
    862         if(curl_strnequal("__Secure-", co->name, 9))
    863           co->prefix_secure = TRUE;
    864         else if(curl_strnequal("__Host-", co->name, 7))
    865           co->prefix_host = TRUE;
    866       }
    867       break;
    868     case 6:
    869       co->value = Curl_memdup0(ptr, len);
    870       if(!co->value)
    871         return CERR_OUT_OF_MEMORY;
    872       break;
    873     }
    874   }
    875   if(6 == fields) {
    876     /* we got a cookie with blank contents, fix it */
    877     co->value = strdup("");
    878     if(!co->value)
    879       return CERR_OUT_OF_MEMORY;
    880     else
    881       fields++;
    882   }
    883 
    884   if(7 != fields)
    885     /* we did not find the sufficient number of fields */
    886     return CERR_FIELDS;
    887 
    888   return CERR_OK;
    889 }
    890 
    891 static int
    892 is_public_suffix(struct Curl_easy *data,
    893                  struct Cookie *co,
    894                  const char *domain)
    895 {
    896 #ifdef USE_LIBPSL
    897   /*
    898    * Check if the domain is a Public Suffix and if yes, ignore the cookie. We
    899    * must also check that the data handle is not NULL since the psl code will
    900    * dereference it.
    901    */
    902   DEBUGF(infof(data, "PSL check set-cookie '%s' for domain=%s in %s",
    903          co->name, co->domain, domain));
    904   if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
    905     bool acceptable = FALSE;
    906     char lcase[256];
    907     char lcookie[256];
    908     size_t dlen = strlen(domain);
    909     size_t clen = strlen(co->domain);
    910     if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
    911       const psl_ctx_t *psl = Curl_psl_use(data);
    912       if(psl) {
    913         /* the PSL check requires lowercase domain name and pattern */
    914         Curl_strntolower(lcase, domain, dlen + 1);
    915         Curl_strntolower(lcookie, co->domain, clen + 1);
    916         acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
    917         Curl_psl_release(data);
    918       }
    919       else
    920         infof(data, "libpsl problem, rejecting cookie for safety");
    921     }
    922 
    923     if(!acceptable) {
    924       infof(data, "cookie '%s' dropped, domain '%s' must not "
    925             "set cookies for '%s'", co->name, domain, co->domain);
    926       return CERR_PSL;
    927     }
    928   }
    929 #else
    930   (void)data;
    931   (void)co;
    932   (void)domain;
    933   DEBUGF(infof(data, "NO PSL to check set-cookie '%s' for domain=%s in %s",
    934          co->name, co->domain, domain));
    935 #endif
    936   return CERR_OK;
    937 }
    938 
    939 static int
    940 replace_existing(struct Curl_easy *data,
    941                  struct Cookie *co,
    942                  struct CookieInfo *ci,
    943                  bool secure,
    944                  bool *replacep)
    945 {
    946   bool replace_old = FALSE;
    947   struct Curl_llist_node *replace_n = NULL;
    948   struct Curl_llist_node *n;
    949   size_t myhash = cookiehash(co->domain);
    950   for(n = Curl_llist_head(&ci->cookielist[myhash]); n; n = Curl_node_next(n)) {
    951     struct Cookie *clist = Curl_node_elem(n);
    952     if(!strcmp(clist->name, co->name)) {
    953       /* the names are identical */
    954       bool matching_domains = FALSE;
    955 
    956       if(clist->domain && co->domain) {
    957         if(curl_strequal(clist->domain, co->domain))
    958           /* The domains are identical */
    959           matching_domains = TRUE;
    960       }
    961       else if(!clist->domain && !co->domain)
    962         matching_domains = TRUE;
    963 
    964       if(matching_domains && /* the domains were identical */
    965          clist->spath && co->spath && /* both have paths */
    966          clist->secure && !co->secure && !secure) {
    967         size_t cllen;
    968         const char *sep;
    969 
    970         /*
    971          * A non-secure cookie may not overlay an existing secure cookie.
    972          * For an existing cookie "a" with path "/login", refuse a new
    973          * cookie "a" with for example path "/login/en", while the path
    974          * "/loginhelper" is ok.
    975          */
    976 
    977         sep = strchr(clist->spath + 1, '/');
    978 
    979         if(sep)
    980           cllen = sep - clist->spath;
    981         else
    982           cllen = strlen(clist->spath);
    983 
    984         if(curl_strnequal(clist->spath, co->spath, cllen)) {
    985           infof(data, "cookie '%s' for domain '%s' dropped, would "
    986                 "overlay an existing cookie", co->name, co->domain);
    987           return CERR_BAD_SECURE;
    988         }
    989       }
    990     }
    991 
    992     if(!replace_n && !strcmp(clist->name, co->name)) {
    993       /* the names are identical */
    994 
    995       if(clist->domain && co->domain) {
    996         if(curl_strequal(clist->domain, co->domain) &&
    997           (clist->tailmatch == co->tailmatch))
    998           /* The domains are identical */
    999           replace_old = TRUE;
   1000       }
   1001       else if(!clist->domain && !co->domain)
   1002         replace_old = TRUE;
   1003 
   1004       if(replace_old) {
   1005         /* the domains were identical */
   1006 
   1007         if(clist->spath && co->spath &&
   1008            !curl_strequal(clist->spath, co->spath))
   1009           replace_old = FALSE;
   1010         else if(!clist->spath != !co->spath)
   1011           replace_old = FALSE;
   1012       }
   1013 
   1014       if(replace_old && !co->livecookie && clist->livecookie) {
   1015         /*
   1016          * Both cookies matched fine, except that the already present cookie
   1017          * is "live", which means it was set from a header, while the new one
   1018          * was read from a file and thus is not "live". "live" cookies are
   1019          * preferred so the new cookie is freed.
   1020          */
   1021         return CERR_LIVE_WINS;
   1022       }
   1023       if(replace_old)
   1024         replace_n = n;
   1025     }
   1026   }
   1027   if(replace_n) {
   1028     struct Cookie *repl = Curl_node_elem(replace_n);
   1029 
   1030     /* when replacing, creationtime is kept from old */
   1031     co->creationtime = repl->creationtime;
   1032 
   1033     /* unlink the old */
   1034     Curl_node_remove(replace_n);
   1035 
   1036     /* free the old cookie */
   1037     freecookie(repl);
   1038   }
   1039   *replacep = replace_old;
   1040   return CERR_OK;
   1041 }
   1042 
   1043 /*
   1044  * Curl_cookie_add
   1045  *
   1046  * Add a single cookie line to the cookie keeping object. Be aware that
   1047  * sometimes we get an IP-only hostname, and that might also be a numerical
   1048  * IPv6 address.
   1049  *
   1050  * Returns NULL on out of memory or invalid cookie. This is suboptimal,
   1051  * as they should be treated separately.
   1052  */
   1053 struct Cookie *
   1054 Curl_cookie_add(struct Curl_easy *data,
   1055                 struct CookieInfo *ci,
   1056                 bool httpheader, /* TRUE if HTTP header-style line */
   1057                 bool noexpire, /* if TRUE, skip remove_expired() */
   1058                 const char *lineptr,   /* first character of the line */
   1059                 const char *domain, /* default domain */
   1060                 const char *path,   /* full path used when this cookie is set,
   1061                                        used to get default path for the cookie
   1062                                        unless set */
   1063                 bool secure)  /* TRUE if connection is over secure origin */
   1064 {
   1065   struct Cookie *co;
   1066   size_t myhash;
   1067   int rc;
   1068   bool replaces = FALSE;
   1069 
   1070   DEBUGASSERT(data);
   1071   DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
   1072   if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
   1073     return NULL;
   1074 
   1075   /* First, alloc and init a new struct for it */
   1076   co = calloc(1, sizeof(struct Cookie));
   1077   if(!co)
   1078     return NULL; /* bail out if we are this low on memory */
   1079 
   1080   if(httpheader)
   1081     rc = parse_cookie_header(data, co, ci, lineptr, domain, path, secure);
   1082   else
   1083     rc = parse_netscape(co, ci, lineptr, secure);
   1084 
   1085   if(rc)
   1086     goto fail;
   1087 
   1088   if(co->prefix_secure && !co->secure)
   1089     /* The __Secure- prefix only requires that the cookie be set secure */
   1090     goto fail;
   1091 
   1092   if(co->prefix_host) {
   1093     /*
   1094      * The __Host- prefix requires the cookie to be secure, have a "/" path
   1095      * and not have a domain set.
   1096      */
   1097     if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
   1098       ;
   1099     else
   1100       goto fail;
   1101   }
   1102 
   1103   if(!ci->running &&    /* read from a file */
   1104      ci->newsession &&  /* clean session cookies */
   1105      !co->expires)      /* this is a session cookie since it does not expire */
   1106     goto fail;
   1107 
   1108   co->livecookie = ci->running;
   1109   co->creationtime = ++ci->lastct;
   1110 
   1111   /*
   1112    * Now we have parsed the incoming line, we must now check if this supersedes
   1113    * an already existing cookie, which it may if the previous have the same
   1114    * domain and path as this.
   1115    */
   1116 
   1117   /* remove expired cookies */
   1118   if(!noexpire)
   1119     remove_expired(ci);
   1120 
   1121   if(is_public_suffix(data, co, domain))
   1122     goto fail;
   1123 
   1124   if(replace_existing(data, co, ci, secure, &replaces))
   1125     goto fail;
   1126 
   1127   /* add this cookie to the list */
   1128   myhash = cookiehash(co->domain);
   1129   Curl_llist_append(&ci->cookielist[myhash], co, &co->node);
   1130 
   1131   if(ci->running)
   1132     /* Only show this when NOT reading the cookies from a file */
   1133     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
   1134           "expire %" FMT_OFF_T,
   1135           replaces ? "Replaced":"Added", co->name, co->value,
   1136           co->domain, co->path, co->expires);
   1137 
   1138   if(!replaces)
   1139     ci->numcookies++; /* one more cookie in the jar */
   1140 
   1141   /*
   1142    * Now that we have added a new cookie to the jar, update the expiration
   1143    * tracker in case it is the next one to expire.
   1144    */
   1145   if(co->expires && (co->expires < ci->next_expiration))
   1146     ci->next_expiration = co->expires;
   1147 
   1148   return co;
   1149 fail:
   1150   freecookie(co);
   1151   return NULL;
   1152 }
   1153 
   1154 
   1155 /*
   1156  * Curl_cookie_init()
   1157  *
   1158  * Inits a cookie struct to read data from a local file. This is always
   1159  * called before any cookies are set. File may be NULL in which case only the
   1160  * struct is initialized. Is file is "-" then STDIN is read.
   1161  *
   1162  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
   1163  *
   1164  * Note that 'data' might be called as NULL pointer. If data is NULL, 'file'
   1165  * will be ignored.
   1166  *
   1167  * Returns NULL on out of memory. Invalid cookies are ignored.
   1168  */
   1169 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
   1170                                     const char *file,
   1171                                     struct CookieInfo *ci,
   1172                                     bool newsession)
   1173 {
   1174   FILE *handle = NULL;
   1175 
   1176   if(!ci) {
   1177     int i;
   1178 
   1179     /* we did not get a struct, create one */
   1180     ci = calloc(1, sizeof(struct CookieInfo));
   1181     if(!ci)
   1182       return NULL; /* failed to get memory */
   1183 
   1184     /* This does not use the destructor callback since we want to add
   1185        and remove to lists while keeping the cookie struct intact */
   1186     for(i = 0; i < COOKIE_HASH_SIZE; i++)
   1187       Curl_llist_init(&ci->cookielist[i], NULL);
   1188     /*
   1189      * Initialize the next_expiration time to signal that we do not have enough
   1190      * information yet.
   1191      */
   1192     ci->next_expiration = CURL_OFF_T_MAX;
   1193   }
   1194   ci->newsession = newsession; /* new session? */
   1195 
   1196   if(data) {
   1197     FILE *fp = NULL;
   1198     if(file && *file) {
   1199       if(!strcmp(file, "-"))
   1200         fp = stdin;
   1201       else {
   1202         fp = fopen(file, "rb");
   1203         if(!fp)
   1204           infof(data, "WARNING: failed to open cookie file \"%s\"", file);
   1205         else
   1206           handle = fp;
   1207       }
   1208     }
   1209 
   1210     ci->running = FALSE; /* this is not running, this is init */
   1211     if(fp) {
   1212       struct dynbuf buf;
   1213       curlx_dyn_init(&buf, MAX_COOKIE_LINE);
   1214       while(Curl_get_line(&buf, fp)) {
   1215         const char *lineptr = curlx_dyn_ptr(&buf);
   1216         bool headerline = FALSE;
   1217         if(checkprefix("Set-Cookie:", lineptr)) {
   1218           /* This is a cookie line, get it! */
   1219           lineptr += 11;
   1220           headerline = TRUE;
   1221           curlx_str_passblanks(&lineptr);
   1222         }
   1223 
   1224         Curl_cookie_add(data, ci, headerline, TRUE, lineptr, NULL, NULL, TRUE);
   1225       }
   1226       curlx_dyn_free(&buf); /* free the line buffer */
   1227 
   1228       /*
   1229        * Remove expired cookies from the hash. We must make sure to run this
   1230        * after reading the file, and not on every cookie.
   1231        */
   1232       remove_expired(ci);
   1233 
   1234       if(handle)
   1235         fclose(handle);
   1236     }
   1237     data->state.cookie_engine = TRUE;
   1238   }
   1239   ci->running = TRUE;          /* now, we are running */
   1240 
   1241   return ci;
   1242 }
   1243 
   1244 /*
   1245  * cookie_sort
   1246  *
   1247  * Helper function to sort cookies such that the longest path gets before the
   1248  * shorter path. Path, domain and name lengths are considered in that order,
   1249  * with the creationtime as the tiebreaker. The creationtime is guaranteed to
   1250  * be unique per cookie, so we know we will get an ordering at that point.
   1251  */
   1252 static int cookie_sort(const void *p1, const void *p2)
   1253 {
   1254   const struct Cookie *c1 = *(const struct Cookie * const *)p1;
   1255   const struct Cookie *c2 = *(const struct Cookie * const *)p2;
   1256   size_t l1, l2;
   1257 
   1258   /* 1 - compare cookie path lengths */
   1259   l1 = c1->path ? strlen(c1->path) : 0;
   1260   l2 = c2->path ? strlen(c2->path) : 0;
   1261 
   1262   if(l1 != l2)
   1263     return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
   1264 
   1265   /* 2 - compare cookie domain lengths */
   1266   l1 = c1->domain ? strlen(c1->domain) : 0;
   1267   l2 = c2->domain ? strlen(c2->domain) : 0;
   1268 
   1269   if(l1 != l2)
   1270     return (l2 > l1) ? 1 : -1; /* avoid size_t <=> int conversions */
   1271 
   1272   /* 3 - compare cookie name lengths */
   1273   l1 = c1->name ? strlen(c1->name) : 0;
   1274   l2 = c2->name ? strlen(c2->name) : 0;
   1275 
   1276   if(l1 != l2)
   1277     return (l2 > l1) ? 1 : -1;
   1278 
   1279   /* 4 - compare cookie creation time */
   1280   return (c2->creationtime > c1->creationtime) ? 1 : -1;
   1281 }
   1282 
   1283 /*
   1284  * cookie_sort_ct
   1285  *
   1286  * Helper function to sort cookies according to creation time.
   1287  */
   1288 static int cookie_sort_ct(const void *p1, const void *p2)
   1289 {
   1290   const struct Cookie *c1 = *(const struct Cookie * const *)p1;
   1291   const struct Cookie *c2 = *(const struct Cookie * const *)p2;
   1292 
   1293   return (c2->creationtime > c1->creationtime) ? 1 : -1;
   1294 }
   1295 
   1296 /*
   1297  * Curl_cookie_getlist
   1298  *
   1299  * For a given host and path, return a linked list of cookies that the client
   1300  * should send to the server if used now. The secure boolean informs the cookie
   1301  * if a secure connection is achieved or not.
   1302  *
   1303  * It shall only return cookies that have not expired.
   1304  *
   1305  * Returns 0 when there is a list returned. Otherwise non-zero.
   1306  */
   1307 int Curl_cookie_getlist(struct Curl_easy *data,
   1308                         struct CookieInfo *ci,
   1309                         const char *host, const char *path,
   1310                         bool secure,
   1311                         struct Curl_llist *list)
   1312 {
   1313   size_t matches = 0;
   1314   bool is_ip;
   1315   const size_t myhash = cookiehash(host);
   1316   struct Curl_llist_node *n;
   1317 
   1318   Curl_llist_init(list, NULL);
   1319 
   1320   if(!ci || !Curl_llist_count(&ci->cookielist[myhash]))
   1321     return 1; /* no cookie struct or no cookies in the struct */
   1322 
   1323   /* at first, remove expired cookies */
   1324   remove_expired(ci);
   1325 
   1326   /* check if host is an IP(v4|v6) address */
   1327   is_ip = Curl_host_is_ipnum(host);
   1328 
   1329   for(n = Curl_llist_head(&ci->cookielist[myhash]);
   1330       n; n = Curl_node_next(n)) {
   1331     struct Cookie *co = Curl_node_elem(n);
   1332 
   1333     /* if the cookie requires we are secure we must only continue if we are! */
   1334     if(co->secure ? secure : TRUE) {
   1335 
   1336       /* now check if the domain is correct */
   1337       if(!co->domain ||
   1338          (co->tailmatch && !is_ip &&
   1339           cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
   1340          ((!co->tailmatch || is_ip) && curl_strequal(host, co->domain)) ) {
   1341         /*
   1342          * the right part of the host matches the domain stuff in the
   1343          * cookie data
   1344          */
   1345 
   1346         /*
   1347          * now check the left part of the path with the cookies path
   1348          * requirement
   1349          */
   1350         if(!co->spath || pathmatch(co->spath, path) ) {
   1351 
   1352           /*
   1353            * This is a match and we add it to the return-linked-list
   1354            */
   1355           Curl_llist_append(list, co, &co->getnode);
   1356           matches++;
   1357           if(matches >= MAX_COOKIE_SEND_AMOUNT) {
   1358             infof(data, "Included max number of cookies (%zu) in request!",
   1359                   matches);
   1360             break;
   1361           }
   1362         }
   1363       }
   1364     }
   1365   }
   1366 
   1367   if(matches) {
   1368     /*
   1369      * Now we need to make sure that if there is a name appearing more than
   1370      * once, the longest specified path version comes first. To make this
   1371      * the swiftest way, we just sort them all based on path length.
   1372      */
   1373     struct Cookie **array;
   1374     size_t i;
   1375 
   1376     /* alloc an array and store all cookie pointers */
   1377     array = malloc(sizeof(struct Cookie *) * matches);
   1378     if(!array)
   1379       goto fail;
   1380 
   1381     n = Curl_llist_head(list);
   1382 
   1383     for(i = 0; n; n = Curl_node_next(n))
   1384       array[i++] = Curl_node_elem(n);
   1385 
   1386     /* now sort the cookie pointers in path length order */
   1387     qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
   1388 
   1389     /* remake the linked list order according to the new order */
   1390     Curl_llist_destroy(list, NULL);
   1391 
   1392     for(i = 0; i < matches; i++)
   1393       Curl_llist_append(list, array[i], &array[i]->getnode);
   1394 
   1395     free(array); /* remove the temporary data again */
   1396   }
   1397 
   1398   return 0; /* success */
   1399 
   1400 fail:
   1401   /* failure, clear up the allocated chain and return NULL */
   1402   Curl_llist_destroy(list, NULL);
   1403   return 2; /* error */
   1404 }
   1405 
   1406 /*
   1407  * Curl_cookie_clearall
   1408  *
   1409  * Clear all existing cookies and reset the counter.
   1410  */
   1411 void Curl_cookie_clearall(struct CookieInfo *ci)
   1412 {
   1413   if(ci) {
   1414     unsigned int i;
   1415     for(i = 0; i < COOKIE_HASH_SIZE; i++) {
   1416       struct Curl_llist_node *n;
   1417       for(n = Curl_llist_head(&ci->cookielist[i]); n;) {
   1418         struct Cookie *c = Curl_node_elem(n);
   1419         struct Curl_llist_node *e = Curl_node_next(n);
   1420         Curl_node_remove(n);
   1421         freecookie(c);
   1422         n = e;
   1423       }
   1424     }
   1425     ci->numcookies = 0;
   1426   }
   1427 }
   1428 
   1429 /*
   1430  * Curl_cookie_clearsess
   1431  *
   1432  * Free all session cookies in the cookies list.
   1433  */
   1434 void Curl_cookie_clearsess(struct CookieInfo *ci)
   1435 {
   1436   unsigned int i;
   1437 
   1438   if(!ci)
   1439     return;
   1440 
   1441   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
   1442     struct Curl_llist_node *n = Curl_llist_head(&ci->cookielist[i]);
   1443     struct Curl_llist_node *e = NULL;
   1444 
   1445     for(; n; n = e) {
   1446       struct Cookie *curr = Curl_node_elem(n);
   1447       e = Curl_node_next(n); /* in case the node is removed, get it early */
   1448       if(!curr->expires) {
   1449         Curl_node_remove(n);
   1450         freecookie(curr);
   1451         ci->numcookies--;
   1452       }
   1453     }
   1454   }
   1455 }
   1456 
   1457 /*
   1458  * Curl_cookie_cleanup()
   1459  *
   1460  * Free a "cookie object" previous created with Curl_cookie_init().
   1461  */
   1462 void Curl_cookie_cleanup(struct CookieInfo *ci)
   1463 {
   1464   if(ci) {
   1465     Curl_cookie_clearall(ci);
   1466     free(ci); /* free the base struct as well */
   1467   }
   1468 }
   1469 
   1470 /*
   1471  * get_netscape_format()
   1472  *
   1473  * Formats a string for Netscape output file, w/o a newline at the end.
   1474  * Function returns a char * to a formatted line. The caller is responsible
   1475  * for freeing the returned pointer.
   1476  */
   1477 static char *get_netscape_format(const struct Cookie *co)
   1478 {
   1479   return aprintf(
   1480     "%s"     /* httponly preamble */
   1481     "%s%s\t" /* domain */
   1482     "%s\t"   /* tailmatch */
   1483     "%s\t"   /* path */
   1484     "%s\t"   /* secure */
   1485     "%" FMT_OFF_T "\t"   /* expires */
   1486     "%s\t"   /* name */
   1487     "%s",    /* value */
   1488     co->httponly ? "#HttpOnly_" : "",
   1489     /*
   1490      * Make sure all domains are prefixed with a dot if they allow
   1491      * tailmatching. This is Mozilla-style.
   1492      */
   1493     (co->tailmatch && co->domain && co->domain[0] != '.') ? "." : "",
   1494     co->domain ? co->domain : "unknown",
   1495     co->tailmatch ? "TRUE" : "FALSE",
   1496     co->path ? co->path : "/",
   1497     co->secure ? "TRUE" : "FALSE",
   1498     co->expires,
   1499     co->name,
   1500     co->value ? co->value : "");
   1501 }
   1502 
   1503 /*
   1504  * cookie_output()
   1505  *
   1506  * Writes all internally known cookies to the specified file. Specify
   1507  * "-" as filename to write to stdout.
   1508  *
   1509  * The function returns non-zero on write failure.
   1510  */
   1511 static CURLcode cookie_output(struct Curl_easy *data,
   1512                               struct CookieInfo *ci,
   1513                               const char *filename)
   1514 {
   1515   FILE *out = NULL;
   1516   bool use_stdout = FALSE;
   1517   char *tempstore = NULL;
   1518   CURLcode error = CURLE_OK;
   1519 
   1520   if(!ci)
   1521     /* no cookie engine alive */
   1522     return CURLE_OK;
   1523 
   1524   /* at first, remove expired cookies */
   1525   remove_expired(ci);
   1526 
   1527   if(!strcmp("-", filename)) {
   1528     /* use stdout */
   1529     out = stdout;
   1530     use_stdout = TRUE;
   1531   }
   1532   else {
   1533     error = Curl_fopen(data, filename, &out, &tempstore);
   1534     if(error)
   1535       goto error;
   1536   }
   1537 
   1538   fputs("# Netscape HTTP Cookie File\n"
   1539         "# https://curl.se/docs/http-cookies.html\n"
   1540         "# This file was generated by libcurl! Edit at your own risk.\n\n",
   1541         out);
   1542 
   1543   if(ci->numcookies) {
   1544     unsigned int i;
   1545     size_t nvalid = 0;
   1546     struct Cookie **array;
   1547     struct Curl_llist_node *n;
   1548 
   1549     array = calloc(1, sizeof(struct Cookie *) * ci->numcookies);
   1550     if(!array) {
   1551       error = CURLE_OUT_OF_MEMORY;
   1552       goto error;
   1553     }
   1554 
   1555     /* only sort the cookies with a domain property */
   1556     for(i = 0; i < COOKIE_HASH_SIZE; i++) {
   1557       for(n = Curl_llist_head(&ci->cookielist[i]); n;
   1558           n = Curl_node_next(n)) {
   1559         struct Cookie *co = Curl_node_elem(n);
   1560         if(!co->domain)
   1561           continue;
   1562         array[nvalid++] = co;
   1563       }
   1564     }
   1565 
   1566     qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
   1567 
   1568     for(i = 0; i < nvalid; i++) {
   1569       char *format_ptr = get_netscape_format(array[i]);
   1570       if(!format_ptr) {
   1571         free(array);
   1572         error = CURLE_OUT_OF_MEMORY;
   1573         goto error;
   1574       }
   1575       fprintf(out, "%s\n", format_ptr);
   1576       free(format_ptr);
   1577     }
   1578 
   1579     free(array);
   1580   }
   1581 
   1582   if(!use_stdout) {
   1583     fclose(out);
   1584     out = NULL;
   1585     if(tempstore && Curl_rename(tempstore, filename)) {
   1586       unlink(tempstore);
   1587       error = CURLE_WRITE_ERROR;
   1588       goto error;
   1589     }
   1590   }
   1591 
   1592   /*
   1593    * If we reach here we have successfully written a cookie file so there is
   1594    * no need to inspect the error, any error case should have jumped into the
   1595    * error block below.
   1596    */
   1597   free(tempstore);
   1598   return CURLE_OK;
   1599 
   1600 error:
   1601   if(out && !use_stdout)
   1602     fclose(out);
   1603   free(tempstore);
   1604   return error;
   1605 }
   1606 
   1607 static struct curl_slist *cookie_list(struct Curl_easy *data)
   1608 {
   1609   struct curl_slist *list = NULL;
   1610   struct curl_slist *beg;
   1611   unsigned int i;
   1612   struct Curl_llist_node *n;
   1613 
   1614   if(!data->cookies || (data->cookies->numcookies == 0))
   1615     return NULL;
   1616 
   1617   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
   1618     for(n = Curl_llist_head(&data->cookies->cookielist[i]); n;
   1619         n = Curl_node_next(n)) {
   1620       struct Cookie *c = Curl_node_elem(n);
   1621       char *line;
   1622       if(!c->domain)
   1623         continue;
   1624       line = get_netscape_format(c);
   1625       if(!line) {
   1626         curl_slist_free_all(list);
   1627         return NULL;
   1628       }
   1629       beg = Curl_slist_append_nodup(list, line);
   1630       if(!beg) {
   1631         free(line);
   1632         curl_slist_free_all(list);
   1633         return NULL;
   1634       }
   1635       list = beg;
   1636     }
   1637   }
   1638 
   1639   return list;
   1640 }
   1641 
   1642 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
   1643 {
   1644   struct curl_slist *list;
   1645   Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
   1646   list = cookie_list(data);
   1647   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
   1648   return list;
   1649 }
   1650 
   1651 void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
   1652 {
   1653   CURLcode res;
   1654 
   1655   if(data->set.str[STRING_COOKIEJAR]) {
   1656     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
   1657 
   1658     /* if we have a destination file for all the cookies to get dumped to */
   1659     res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
   1660     if(res)
   1661       infof(data, "WARNING: failed to save cookies in %s: %s",
   1662             data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
   1663   }
   1664   else {
   1665     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
   1666   }
   1667 
   1668   if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
   1669     Curl_cookie_cleanup(data->cookies);
   1670     data->cookies = NULL;
   1671   }
   1672   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
   1673 }
   1674 
   1675 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */