113498266Sopenharmony_ci/***************************************************************************
213498266Sopenharmony_ci *                                  _   _ ____  _
313498266Sopenharmony_ci *  Project                     ___| | | |  _ \| |
413498266Sopenharmony_ci *                             / __| | | | |_) | |
513498266Sopenharmony_ci *                            | (__| |_| |  _ <| |___
613498266Sopenharmony_ci *                             \___|\___/|_| \_\_____|
713498266Sopenharmony_ci *
813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
913498266Sopenharmony_ci *
1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which
1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms
1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html.
1313498266Sopenharmony_ci *
1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell
1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is
1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file.
1713498266Sopenharmony_ci *
1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
1913498266Sopenharmony_ci * KIND, either express or implied.
2013498266Sopenharmony_ci *
2113498266Sopenharmony_ci * SPDX-License-Identifier: curl
2213498266Sopenharmony_ci *
2313498266Sopenharmony_ci ***************************************************************************/
2413498266Sopenharmony_ci
2513498266Sopenharmony_ci/***
2613498266Sopenharmony_ci
2713498266Sopenharmony_ci
2813498266Sopenharmony_ciRECEIVING COOKIE INFORMATION
2913498266Sopenharmony_ci============================
3013498266Sopenharmony_ci
3113498266Sopenharmony_cistruct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
3213498266Sopenharmony_ci                    const char *file, struct CookieInfo *inc, bool newsession);
3313498266Sopenharmony_ci
3413498266Sopenharmony_ci        Inits a cookie struct to store data in a local file. This is always
3513498266Sopenharmony_ci        called before any cookies are set.
3613498266Sopenharmony_ci
3713498266Sopenharmony_cistruct Cookie *Curl_cookie_add(struct Curl_easy *data,
3813498266Sopenharmony_ci                 struct CookieInfo *c, bool httpheader, bool noexpire,
3913498266Sopenharmony_ci                 char *lineptr, const char *domain, const char *path,
4013498266Sopenharmony_ci                 bool secure);
4113498266Sopenharmony_ci
4213498266Sopenharmony_ci        The 'lineptr' parameter is a full "Set-cookie:" line as
4313498266Sopenharmony_ci        received from a server.
4413498266Sopenharmony_ci
4513498266Sopenharmony_ci        The function need to replace previously stored lines that this new
4613498266Sopenharmony_ci        line supersedes.
4713498266Sopenharmony_ci
4813498266Sopenharmony_ci        It may remove lines that are expired.
4913498266Sopenharmony_ci
5013498266Sopenharmony_ci        It should return an indication of success/error.
5113498266Sopenharmony_ci
5213498266Sopenharmony_ci
5313498266Sopenharmony_ciSENDING COOKIE INFORMATION
5413498266Sopenharmony_ci==========================
5513498266Sopenharmony_ci
5613498266Sopenharmony_cistruct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
5713498266Sopenharmony_ci                                    char *host, char *path, bool secure);
5813498266Sopenharmony_ci
5913498266Sopenharmony_ci        For a given host and path, return a linked list of cookies that
6013498266Sopenharmony_ci        the client should send to the server if used now. The secure
6113498266Sopenharmony_ci        boolean informs the cookie if a secure connection is achieved or
6213498266Sopenharmony_ci        not.
6313498266Sopenharmony_ci
6413498266Sopenharmony_ci        It shall only return cookies that haven't expired.
6513498266Sopenharmony_ci
6613498266Sopenharmony_ci
6713498266Sopenharmony_ciExample set of cookies:
6813498266Sopenharmony_ci
6913498266Sopenharmony_ci    Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
7013498266Sopenharmony_ci    Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
7113498266Sopenharmony_ci    domain=.fidelity.com; path=/ftgw; secure
7213498266Sopenharmony_ci    Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
7313498266Sopenharmony_ci    domain=.fidelity.com; path=/; secure
7413498266Sopenharmony_ci    Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
7513498266Sopenharmony_ci    domain=.fidelity.com; path=/; secure
7613498266Sopenharmony_ci    Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
7713498266Sopenharmony_ci    domain=.fidelity.com; path=/; secure
7813498266Sopenharmony_ci    Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
7913498266Sopenharmony_ci    domain=.fidelity.com; path=/; secure
8013498266Sopenharmony_ci    Set-cookie:
8113498266Sopenharmony_ci    Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
8213498266Sopenharmony_ci    13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
8313498266Sopenharmony_ci****/
8413498266Sopenharmony_ci
8513498266Sopenharmony_ci
8613498266Sopenharmony_ci#include "curl_setup.h"
8713498266Sopenharmony_ci
8813498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
8913498266Sopenharmony_ci
9013498266Sopenharmony_ci#include "urldata.h"
9113498266Sopenharmony_ci#include "cookie.h"
9213498266Sopenharmony_ci#include "psl.h"
9313498266Sopenharmony_ci#include "strtok.h"
9413498266Sopenharmony_ci#include "sendf.h"
9513498266Sopenharmony_ci#include "slist.h"
9613498266Sopenharmony_ci#include "share.h"
9713498266Sopenharmony_ci#include "strtoofft.h"
9813498266Sopenharmony_ci#include "strcase.h"
9913498266Sopenharmony_ci#include "curl_get_line.h"
10013498266Sopenharmony_ci#include "curl_memrchr.h"
10113498266Sopenharmony_ci#include "parsedate.h"
10213498266Sopenharmony_ci#include "rename.h"
10313498266Sopenharmony_ci#include "fopen.h"
10413498266Sopenharmony_ci#include "strdup.h"
10513498266Sopenharmony_ci
10613498266Sopenharmony_ci/* The last 3 #include files should be in this order */
10713498266Sopenharmony_ci#include "curl_printf.h"
10813498266Sopenharmony_ci#include "curl_memory.h"
10913498266Sopenharmony_ci#include "memdebug.h"
11013498266Sopenharmony_ci
11113498266Sopenharmony_cistatic void strstore(char **str, const char *newstr, size_t len);
11213498266Sopenharmony_ci
11313498266Sopenharmony_cistatic void freecookie(struct Cookie *co)
11413498266Sopenharmony_ci{
11513498266Sopenharmony_ci  free(co->domain);
11613498266Sopenharmony_ci  free(co->path);
11713498266Sopenharmony_ci  free(co->spath);
11813498266Sopenharmony_ci  free(co->name);
11913498266Sopenharmony_ci  free(co->value);
12013498266Sopenharmony_ci  free(co);
12113498266Sopenharmony_ci}
12213498266Sopenharmony_ci
12313498266Sopenharmony_cistatic bool cookie_tailmatch(const char *cookie_domain,
12413498266Sopenharmony_ci                             size_t cookie_domain_len,
12513498266Sopenharmony_ci                             const char *hostname)
12613498266Sopenharmony_ci{
12713498266Sopenharmony_ci  size_t hostname_len = strlen(hostname);
12813498266Sopenharmony_ci
12913498266Sopenharmony_ci  if(hostname_len < cookie_domain_len)
13013498266Sopenharmony_ci    return FALSE;
13113498266Sopenharmony_ci
13213498266Sopenharmony_ci  if(!strncasecompare(cookie_domain,
13313498266Sopenharmony_ci                      hostname + hostname_len-cookie_domain_len,
13413498266Sopenharmony_ci                      cookie_domain_len))
13513498266Sopenharmony_ci    return FALSE;
13613498266Sopenharmony_ci
13713498266Sopenharmony_ci  /*
13813498266Sopenharmony_ci   * A lead char of cookie_domain is not '.'.
13913498266Sopenharmony_ci   * RFC6265 4.1.2.3. The Domain Attribute says:
14013498266Sopenharmony_ci   * For example, if the value of the Domain attribute is
14113498266Sopenharmony_ci   * "example.com", the user agent will include the cookie in the Cookie
14213498266Sopenharmony_ci   * header when making HTTP requests to example.com, www.example.com, and
14313498266Sopenharmony_ci   * www.corp.example.com.
14413498266Sopenharmony_ci   */
14513498266Sopenharmony_ci  if(hostname_len == cookie_domain_len)
14613498266Sopenharmony_ci    return TRUE;
14713498266Sopenharmony_ci  if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
14813498266Sopenharmony_ci    return TRUE;
14913498266Sopenharmony_ci  return FALSE;
15013498266Sopenharmony_ci}
15113498266Sopenharmony_ci
15213498266Sopenharmony_ci/*
15313498266Sopenharmony_ci * matching cookie path and url path
15413498266Sopenharmony_ci * RFC6265 5.1.4 Paths and Path-Match
15513498266Sopenharmony_ci */
15613498266Sopenharmony_cistatic bool pathmatch(const char *cookie_path, const char *request_uri)
15713498266Sopenharmony_ci{
15813498266Sopenharmony_ci  size_t cookie_path_len;
15913498266Sopenharmony_ci  size_t uri_path_len;
16013498266Sopenharmony_ci  char *uri_path = NULL;
16113498266Sopenharmony_ci  char *pos;
16213498266Sopenharmony_ci  bool ret = FALSE;
16313498266Sopenharmony_ci
16413498266Sopenharmony_ci  /* cookie_path must not have last '/' separator. ex: /sample */
16513498266Sopenharmony_ci  cookie_path_len = strlen(cookie_path);
16613498266Sopenharmony_ci  if(1 == cookie_path_len) {
16713498266Sopenharmony_ci    /* cookie_path must be '/' */
16813498266Sopenharmony_ci    return TRUE;
16913498266Sopenharmony_ci  }
17013498266Sopenharmony_ci
17113498266Sopenharmony_ci  uri_path = strdup(request_uri);
17213498266Sopenharmony_ci  if(!uri_path)
17313498266Sopenharmony_ci    return FALSE;
17413498266Sopenharmony_ci  pos = strchr(uri_path, '?');
17513498266Sopenharmony_ci  if(pos)
17613498266Sopenharmony_ci    *pos = 0x0;
17713498266Sopenharmony_ci
17813498266Sopenharmony_ci  /* #-fragments are already cut off! */
17913498266Sopenharmony_ci  if(0 == strlen(uri_path) || uri_path[0] != '/') {
18013498266Sopenharmony_ci    strstore(&uri_path, "/", 1);
18113498266Sopenharmony_ci    if(!uri_path)
18213498266Sopenharmony_ci      return FALSE;
18313498266Sopenharmony_ci  }
18413498266Sopenharmony_ci
18513498266Sopenharmony_ci  /*
18613498266Sopenharmony_ci   * here, RFC6265 5.1.4 says
18713498266Sopenharmony_ci   *  4. Output the characters of the uri-path from the first character up
18813498266Sopenharmony_ci   *     to, but not including, the right-most %x2F ("/").
18913498266Sopenharmony_ci   *  but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
19013498266Sopenharmony_ci   *  without redirect.
19113498266Sopenharmony_ci   *  Ignore this algorithm because /hoge is uri path for this case
19213498266Sopenharmony_ci   *  (uri path is not /).
19313498266Sopenharmony_ci   */
19413498266Sopenharmony_ci
19513498266Sopenharmony_ci  uri_path_len = strlen(uri_path);
19613498266Sopenharmony_ci
19713498266Sopenharmony_ci  if(uri_path_len < cookie_path_len) {
19813498266Sopenharmony_ci    ret = FALSE;
19913498266Sopenharmony_ci    goto pathmatched;
20013498266Sopenharmony_ci  }
20113498266Sopenharmony_ci
20213498266Sopenharmony_ci  /* not using checkprefix() because matching should be case-sensitive */
20313498266Sopenharmony_ci  if(strncmp(cookie_path, uri_path, cookie_path_len)) {
20413498266Sopenharmony_ci    ret = FALSE;
20513498266Sopenharmony_ci    goto pathmatched;
20613498266Sopenharmony_ci  }
20713498266Sopenharmony_ci
20813498266Sopenharmony_ci  /* The cookie-path and the uri-path are identical. */
20913498266Sopenharmony_ci  if(cookie_path_len == uri_path_len) {
21013498266Sopenharmony_ci    ret = TRUE;
21113498266Sopenharmony_ci    goto pathmatched;
21213498266Sopenharmony_ci  }
21313498266Sopenharmony_ci
21413498266Sopenharmony_ci  /* here, cookie_path_len < uri_path_len */
21513498266Sopenharmony_ci  if(uri_path[cookie_path_len] == '/') {
21613498266Sopenharmony_ci    ret = TRUE;
21713498266Sopenharmony_ci    goto pathmatched;
21813498266Sopenharmony_ci  }
21913498266Sopenharmony_ci
22013498266Sopenharmony_ci  ret = FALSE;
22113498266Sopenharmony_ci
22213498266Sopenharmony_cipathmatched:
22313498266Sopenharmony_ci  free(uri_path);
22413498266Sopenharmony_ci  return ret;
22513498266Sopenharmony_ci}
22613498266Sopenharmony_ci
22713498266Sopenharmony_ci/*
22813498266Sopenharmony_ci * Return the top-level domain, for optimal hashing.
22913498266Sopenharmony_ci */
23013498266Sopenharmony_cistatic const char *get_top_domain(const char * const domain, size_t *outlen)
23113498266Sopenharmony_ci{
23213498266Sopenharmony_ci  size_t len = 0;
23313498266Sopenharmony_ci  const char *first = NULL, *last;
23413498266Sopenharmony_ci
23513498266Sopenharmony_ci  if(domain) {
23613498266Sopenharmony_ci    len = strlen(domain);
23713498266Sopenharmony_ci    last = memrchr(domain, '.', len);
23813498266Sopenharmony_ci    if(last) {
23913498266Sopenharmony_ci      first = memrchr(domain, '.', (last - domain));
24013498266Sopenharmony_ci      if(first)
24113498266Sopenharmony_ci        len -= (++first - domain);
24213498266Sopenharmony_ci    }
24313498266Sopenharmony_ci  }
24413498266Sopenharmony_ci
24513498266Sopenharmony_ci  if(outlen)
24613498266Sopenharmony_ci    *outlen = len;
24713498266Sopenharmony_ci
24813498266Sopenharmony_ci  return first? first: domain;
24913498266Sopenharmony_ci}
25013498266Sopenharmony_ci
25113498266Sopenharmony_ci/* Avoid C1001, an "internal error" with MSVC14 */
25213498266Sopenharmony_ci#if defined(_MSC_VER) && (_MSC_VER == 1900)
25313498266Sopenharmony_ci#pragma optimize("", off)
25413498266Sopenharmony_ci#endif
25513498266Sopenharmony_ci
25613498266Sopenharmony_ci/*
25713498266Sopenharmony_ci * A case-insensitive hash for the cookie domains.
25813498266Sopenharmony_ci */
25913498266Sopenharmony_cistatic size_t cookie_hash_domain(const char *domain, const size_t len)
26013498266Sopenharmony_ci{
26113498266Sopenharmony_ci  const char *end = domain + len;
26213498266Sopenharmony_ci  size_t h = 5381;
26313498266Sopenharmony_ci
26413498266Sopenharmony_ci  while(domain < end) {
26513498266Sopenharmony_ci    h += h << 5;
26613498266Sopenharmony_ci    h ^= Curl_raw_toupper(*domain++);
26713498266Sopenharmony_ci  }
26813498266Sopenharmony_ci
26913498266Sopenharmony_ci  return (h % COOKIE_HASH_SIZE);
27013498266Sopenharmony_ci}
27113498266Sopenharmony_ci
27213498266Sopenharmony_ci#if defined(_MSC_VER) && (_MSC_VER == 1900)
27313498266Sopenharmony_ci#pragma optimize("", on)
27413498266Sopenharmony_ci#endif
27513498266Sopenharmony_ci
27613498266Sopenharmony_ci/*
27713498266Sopenharmony_ci * Hash this domain.
27813498266Sopenharmony_ci */
27913498266Sopenharmony_cistatic size_t cookiehash(const char * const domain)
28013498266Sopenharmony_ci{
28113498266Sopenharmony_ci  const char *top;
28213498266Sopenharmony_ci  size_t len;
28313498266Sopenharmony_ci
28413498266Sopenharmony_ci  if(!domain || Curl_host_is_ipnum(domain))
28513498266Sopenharmony_ci    return 0;
28613498266Sopenharmony_ci
28713498266Sopenharmony_ci  top = get_top_domain(domain, &len);
28813498266Sopenharmony_ci  return cookie_hash_domain(top, len);
28913498266Sopenharmony_ci}
29013498266Sopenharmony_ci
29113498266Sopenharmony_ci/*
29213498266Sopenharmony_ci * cookie path sanitize
29313498266Sopenharmony_ci */
29413498266Sopenharmony_cistatic char *sanitize_cookie_path(const char *cookie_path)
29513498266Sopenharmony_ci{
29613498266Sopenharmony_ci  size_t len;
29713498266Sopenharmony_ci  char *new_path = strdup(cookie_path);
29813498266Sopenharmony_ci  if(!new_path)
29913498266Sopenharmony_ci    return NULL;
30013498266Sopenharmony_ci
30113498266Sopenharmony_ci  /* some stupid site sends path attribute with '"'. */
30213498266Sopenharmony_ci  len = strlen(new_path);
30313498266Sopenharmony_ci  if(new_path[0] == '\"') {
30413498266Sopenharmony_ci    memmove(new_path, new_path + 1, len);
30513498266Sopenharmony_ci    len--;
30613498266Sopenharmony_ci  }
30713498266Sopenharmony_ci  if(len && (new_path[len - 1] == '\"')) {
30813498266Sopenharmony_ci    new_path[--len] = 0x0;
30913498266Sopenharmony_ci  }
31013498266Sopenharmony_ci
31113498266Sopenharmony_ci  /* RFC6265 5.2.4 The Path Attribute */
31213498266Sopenharmony_ci  if(new_path[0] != '/') {
31313498266Sopenharmony_ci    /* Let cookie-path be the default-path. */
31413498266Sopenharmony_ci    strstore(&new_path, "/", 1);
31513498266Sopenharmony_ci    return new_path;
31613498266Sopenharmony_ci  }
31713498266Sopenharmony_ci
31813498266Sopenharmony_ci  /* convert /hoge/ to /hoge */
31913498266Sopenharmony_ci  if(len && new_path[len - 1] == '/') {
32013498266Sopenharmony_ci    new_path[len - 1] = 0x0;
32113498266Sopenharmony_ci  }
32213498266Sopenharmony_ci
32313498266Sopenharmony_ci  return new_path;
32413498266Sopenharmony_ci}
32513498266Sopenharmony_ci
32613498266Sopenharmony_ci/*
32713498266Sopenharmony_ci * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
32813498266Sopenharmony_ci *
32913498266Sopenharmony_ci * NOTE: OOM or cookie parsing failures are ignored.
33013498266Sopenharmony_ci */
33113498266Sopenharmony_civoid Curl_cookie_loadfiles(struct Curl_easy *data)
33213498266Sopenharmony_ci{
33313498266Sopenharmony_ci  struct curl_slist *list = data->state.cookielist;
33413498266Sopenharmony_ci  if(list) {
33513498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
33613498266Sopenharmony_ci    while(list) {
33713498266Sopenharmony_ci      struct CookieInfo *newcookies =
33813498266Sopenharmony_ci        Curl_cookie_init(data, list->data, data->cookies,
33913498266Sopenharmony_ci                         data->set.cookiesession);
34013498266Sopenharmony_ci      if(!newcookies)
34113498266Sopenharmony_ci        /*
34213498266Sopenharmony_ci         * Failure may be due to OOM or a bad cookie; both are ignored
34313498266Sopenharmony_ci         * but only the first should be
34413498266Sopenharmony_ci         */
34513498266Sopenharmony_ci        infof(data, "ignoring failed cookie_init for %s", list->data);
34613498266Sopenharmony_ci      else
34713498266Sopenharmony_ci        data->cookies = newcookies;
34813498266Sopenharmony_ci      list = list->next;
34913498266Sopenharmony_ci    }
35013498266Sopenharmony_ci    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
35113498266Sopenharmony_ci  }
35213498266Sopenharmony_ci}
35313498266Sopenharmony_ci
35413498266Sopenharmony_ci/*
35513498266Sopenharmony_ci * strstore
35613498266Sopenharmony_ci *
35713498266Sopenharmony_ci * A thin wrapper around strdup which ensures that any memory allocated at
35813498266Sopenharmony_ci * *str will be freed before the string allocated by strdup is stored there.
35913498266Sopenharmony_ci * The intended usecase is repeated assignments to the same variable during
36013498266Sopenharmony_ci * parsing in a last-wins scenario. The caller is responsible for checking
36113498266Sopenharmony_ci * for OOM errors.
36213498266Sopenharmony_ci */
36313498266Sopenharmony_cistatic void strstore(char **str, const char *newstr, size_t len)
36413498266Sopenharmony_ci{
36513498266Sopenharmony_ci  DEBUGASSERT(newstr);
36613498266Sopenharmony_ci  DEBUGASSERT(str);
36713498266Sopenharmony_ci  free(*str);
36813498266Sopenharmony_ci  *str = Curl_memdup0(newstr, len);
36913498266Sopenharmony_ci}
37013498266Sopenharmony_ci
37113498266Sopenharmony_ci/*
37213498266Sopenharmony_ci * remove_expired
37313498266Sopenharmony_ci *
37413498266Sopenharmony_ci * Remove expired cookies from the hash by inspecting the expires timestamp on
37513498266Sopenharmony_ci * each cookie in the hash, freeing and deleting any where the timestamp is in
37613498266Sopenharmony_ci * the past.  If the cookiejar has recorded the next timestamp at which one or
37713498266Sopenharmony_ci * more cookies expire, then processing will exit early in case this timestamp
37813498266Sopenharmony_ci * is in the future.
37913498266Sopenharmony_ci */
38013498266Sopenharmony_cistatic void remove_expired(struct CookieInfo *cookies)
38113498266Sopenharmony_ci{
38213498266Sopenharmony_ci  struct Cookie *co, *nx;
38313498266Sopenharmony_ci  curl_off_t now = (curl_off_t)time(NULL);
38413498266Sopenharmony_ci  unsigned int i;
38513498266Sopenharmony_ci
38613498266Sopenharmony_ci  /*
38713498266Sopenharmony_ci   * If the earliest expiration timestamp in the jar is in the future we can
38813498266Sopenharmony_ci   * skip scanning the whole jar and instead exit early as there won't be any
38913498266Sopenharmony_ci   * cookies to evict.  If we need to evict however, reset the next_expiration
39013498266Sopenharmony_ci   * counter in order to track the next one. In case the recorded first
39113498266Sopenharmony_ci   * expiration is the max offset, then perform the safe fallback of checking
39213498266Sopenharmony_ci   * all cookies.
39313498266Sopenharmony_ci   */
39413498266Sopenharmony_ci  if(now < cookies->next_expiration &&
39513498266Sopenharmony_ci      cookies->next_expiration != CURL_OFF_T_MAX)
39613498266Sopenharmony_ci    return;
39713498266Sopenharmony_ci  else
39813498266Sopenharmony_ci    cookies->next_expiration = CURL_OFF_T_MAX;
39913498266Sopenharmony_ci
40013498266Sopenharmony_ci  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
40113498266Sopenharmony_ci    struct Cookie *pv = NULL;
40213498266Sopenharmony_ci    co = cookies->cookies[i];
40313498266Sopenharmony_ci    while(co) {
40413498266Sopenharmony_ci      nx = co->next;
40513498266Sopenharmony_ci      if(co->expires && co->expires < now) {
40613498266Sopenharmony_ci        if(!pv) {
40713498266Sopenharmony_ci          cookies->cookies[i] = co->next;
40813498266Sopenharmony_ci        }
40913498266Sopenharmony_ci        else {
41013498266Sopenharmony_ci          pv->next = co->next;
41113498266Sopenharmony_ci        }
41213498266Sopenharmony_ci        cookies->numcookies--;
41313498266Sopenharmony_ci        freecookie(co);
41413498266Sopenharmony_ci      }
41513498266Sopenharmony_ci      else {
41613498266Sopenharmony_ci        /*
41713498266Sopenharmony_ci         * If this cookie has an expiration timestamp earlier than what we've
41813498266Sopenharmony_ci         * seen so far then record it for the next round of expirations.
41913498266Sopenharmony_ci         */
42013498266Sopenharmony_ci        if(co->expires && co->expires < cookies->next_expiration)
42113498266Sopenharmony_ci          cookies->next_expiration = co->expires;
42213498266Sopenharmony_ci        pv = co;
42313498266Sopenharmony_ci      }
42413498266Sopenharmony_ci      co = nx;
42513498266Sopenharmony_ci    }
42613498266Sopenharmony_ci  }
42713498266Sopenharmony_ci}
42813498266Sopenharmony_ci
42913498266Sopenharmony_ci/* Make sure domain contains a dot or is localhost. */
43013498266Sopenharmony_cistatic bool bad_domain(const char *domain, size_t len)
43113498266Sopenharmony_ci{
43213498266Sopenharmony_ci  if((len == 9) && strncasecompare(domain, "localhost", 9))
43313498266Sopenharmony_ci    return FALSE;
43413498266Sopenharmony_ci  else {
43513498266Sopenharmony_ci    /* there must be a dot present, but that dot must not be a trailing dot */
43613498266Sopenharmony_ci    char *dot = memchr(domain, '.', len);
43713498266Sopenharmony_ci    if(dot) {
43813498266Sopenharmony_ci      size_t i = dot - domain;
43913498266Sopenharmony_ci      if((len - i) > 1)
44013498266Sopenharmony_ci        /* the dot is not the last byte */
44113498266Sopenharmony_ci        return FALSE;
44213498266Sopenharmony_ci    }
44313498266Sopenharmony_ci  }
44413498266Sopenharmony_ci  return TRUE;
44513498266Sopenharmony_ci}
44613498266Sopenharmony_ci
44713498266Sopenharmony_ci/*
44813498266Sopenharmony_ci  RFC 6265 section 4.1.1 says a server should accept this range:
44913498266Sopenharmony_ci
45013498266Sopenharmony_ci  cookie-octet    = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
45113498266Sopenharmony_ci
45213498266Sopenharmony_ci  But Firefox and Chrome as of June 2022 accept space, comma and double-quotes
45313498266Sopenharmony_ci  fine. The prime reason for filtering out control bytes is that some HTTP
45413498266Sopenharmony_ci  servers return 400 for requests that contain such.
45513498266Sopenharmony_ci*/
45613498266Sopenharmony_cistatic int invalid_octets(const char *p)
45713498266Sopenharmony_ci{
45813498266Sopenharmony_ci  /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */
45913498266Sopenharmony_ci  static const char badoctets[] = {
46013498266Sopenharmony_ci    "\x01\x02\x03\x04\x05\x06\x07\x08\x0a"
46113498266Sopenharmony_ci    "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14"
46213498266Sopenharmony_ci    "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f"
46313498266Sopenharmony_ci  };
46413498266Sopenharmony_ci  size_t len;
46513498266Sopenharmony_ci  /* scan for all the octets that are *not* in cookie-octet */
46613498266Sopenharmony_ci  len = strcspn(p, badoctets);
46713498266Sopenharmony_ci  return (p[len] != '\0');
46813498266Sopenharmony_ci}
46913498266Sopenharmony_ci
47013498266Sopenharmony_ci/*
47113498266Sopenharmony_ci * Curl_cookie_add
47213498266Sopenharmony_ci *
47313498266Sopenharmony_ci * Add a single cookie line to the cookie keeping object. Be aware that
47413498266Sopenharmony_ci * sometimes we get an IP-only host name, and that might also be a numerical
47513498266Sopenharmony_ci * IPv6 address.
47613498266Sopenharmony_ci *
47713498266Sopenharmony_ci * Returns NULL on out of memory or invalid cookie. This is suboptimal,
47813498266Sopenharmony_ci * as they should be treated separately.
47913498266Sopenharmony_ci */
48013498266Sopenharmony_cistruct Cookie *
48113498266Sopenharmony_ciCurl_cookie_add(struct Curl_easy *data,
48213498266Sopenharmony_ci                struct CookieInfo *c,
48313498266Sopenharmony_ci                bool httpheader, /* TRUE if HTTP header-style line */
48413498266Sopenharmony_ci                bool noexpire, /* if TRUE, skip remove_expired() */
48513498266Sopenharmony_ci                const char *lineptr,   /* first character of the line */
48613498266Sopenharmony_ci                const char *domain, /* default domain */
48713498266Sopenharmony_ci                const char *path,   /* full path used when this cookie is set,
48813498266Sopenharmony_ci                                       used to get default path for the cookie
48913498266Sopenharmony_ci                                       unless set */
49013498266Sopenharmony_ci                bool secure)  /* TRUE if connection is over secure origin */
49113498266Sopenharmony_ci{
49213498266Sopenharmony_ci  struct Cookie *clist;
49313498266Sopenharmony_ci  struct Cookie *co;
49413498266Sopenharmony_ci  struct Cookie *lastc = NULL;
49513498266Sopenharmony_ci  struct Cookie *replace_co = NULL;
49613498266Sopenharmony_ci  struct Cookie *replace_clist = NULL;
49713498266Sopenharmony_ci  time_t now = time(NULL);
49813498266Sopenharmony_ci  bool replace_old = FALSE;
49913498266Sopenharmony_ci  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
50013498266Sopenharmony_ci  size_t myhash;
50113498266Sopenharmony_ci
50213498266Sopenharmony_ci  DEBUGASSERT(data);
50313498266Sopenharmony_ci  DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */
50413498266Sopenharmony_ci  if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT)
50513498266Sopenharmony_ci    return NULL;
50613498266Sopenharmony_ci
50713498266Sopenharmony_ci  /* First, alloc and init a new struct for it */
50813498266Sopenharmony_ci  co = calloc(1, sizeof(struct Cookie));
50913498266Sopenharmony_ci  if(!co)
51013498266Sopenharmony_ci    return NULL; /* bail out if we're this low on memory */
51113498266Sopenharmony_ci
51213498266Sopenharmony_ci  if(httpheader) {
51313498266Sopenharmony_ci    /* This line was read off an HTTP-header */
51413498266Sopenharmony_ci    const char *ptr;
51513498266Sopenharmony_ci
51613498266Sopenharmony_ci    size_t linelength = strlen(lineptr);
51713498266Sopenharmony_ci    if(linelength > MAX_COOKIE_LINE) {
51813498266Sopenharmony_ci      /* discard overly long lines at once */
51913498266Sopenharmony_ci      free(co);
52013498266Sopenharmony_ci      return NULL;
52113498266Sopenharmony_ci    }
52213498266Sopenharmony_ci
52313498266Sopenharmony_ci    ptr = lineptr;
52413498266Sopenharmony_ci    do {
52513498266Sopenharmony_ci      size_t vlen;
52613498266Sopenharmony_ci      size_t nlen;
52713498266Sopenharmony_ci
52813498266Sopenharmony_ci      while(*ptr && ISBLANK(*ptr))
52913498266Sopenharmony_ci        ptr++;
53013498266Sopenharmony_ci
53113498266Sopenharmony_ci      /* we have a <name>=<value> pair or a stand-alone word here */
53213498266Sopenharmony_ci      nlen = strcspn(ptr, ";\t\r\n=");
53313498266Sopenharmony_ci      if(nlen) {
53413498266Sopenharmony_ci        bool done = FALSE;
53513498266Sopenharmony_ci        bool sep = FALSE;
53613498266Sopenharmony_ci        const char *namep = ptr;
53713498266Sopenharmony_ci        const char *valuep;
53813498266Sopenharmony_ci
53913498266Sopenharmony_ci        ptr += nlen;
54013498266Sopenharmony_ci
54113498266Sopenharmony_ci        /* trim trailing spaces and tabs after name */
54213498266Sopenharmony_ci        while(nlen && ISBLANK(namep[nlen - 1]))
54313498266Sopenharmony_ci          nlen--;
54413498266Sopenharmony_ci
54513498266Sopenharmony_ci        if(*ptr == '=') {
54613498266Sopenharmony_ci          vlen = strcspn(++ptr, ";\r\n");
54713498266Sopenharmony_ci          valuep = ptr;
54813498266Sopenharmony_ci          sep = TRUE;
54913498266Sopenharmony_ci          ptr = &valuep[vlen];
55013498266Sopenharmony_ci
55113498266Sopenharmony_ci          /* Strip off trailing whitespace from the value */
55213498266Sopenharmony_ci          while(vlen && ISBLANK(valuep[vlen-1]))
55313498266Sopenharmony_ci            vlen--;
55413498266Sopenharmony_ci
55513498266Sopenharmony_ci          /* Skip leading whitespace from the value */
55613498266Sopenharmony_ci          while(vlen && ISBLANK(*valuep)) {
55713498266Sopenharmony_ci            valuep++;
55813498266Sopenharmony_ci            vlen--;
55913498266Sopenharmony_ci          }
56013498266Sopenharmony_ci
56113498266Sopenharmony_ci          /* Reject cookies with a TAB inside the value */
56213498266Sopenharmony_ci          if(memchr(valuep, '\t', vlen)) {
56313498266Sopenharmony_ci            freecookie(co);
56413498266Sopenharmony_ci            infof(data, "cookie contains TAB, dropping");
56513498266Sopenharmony_ci            return NULL;
56613498266Sopenharmony_ci          }
56713498266Sopenharmony_ci        }
56813498266Sopenharmony_ci        else {
56913498266Sopenharmony_ci          valuep = NULL;
57013498266Sopenharmony_ci          vlen = 0;
57113498266Sopenharmony_ci        }
57213498266Sopenharmony_ci
57313498266Sopenharmony_ci        /*
57413498266Sopenharmony_ci         * Check for too long individual name or contents, or too long
57513498266Sopenharmony_ci         * combination of name + contents. Chrome and Firefox support 4095 or
57613498266Sopenharmony_ci         * 4096 bytes combo
57713498266Sopenharmony_ci         */
57813498266Sopenharmony_ci        if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) ||
57913498266Sopenharmony_ci           ((nlen + vlen) > MAX_NAME)) {
58013498266Sopenharmony_ci          freecookie(co);
58113498266Sopenharmony_ci          infof(data, "oversized cookie dropped, name/val %zu + %zu bytes",
58213498266Sopenharmony_ci                nlen, vlen);
58313498266Sopenharmony_ci          return NULL;
58413498266Sopenharmony_ci        }
58513498266Sopenharmony_ci
58613498266Sopenharmony_ci        /*
58713498266Sopenharmony_ci         * Check if we have a reserved prefix set before anything else, as we
58813498266Sopenharmony_ci         * otherwise have to test for the prefix in both the cookie name and
58913498266Sopenharmony_ci         * "the rest". Prefixes must start with '__' and end with a '-', so
59013498266Sopenharmony_ci         * only test for names where that can possibly be true.
59113498266Sopenharmony_ci         */
59213498266Sopenharmony_ci        if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') {
59313498266Sopenharmony_ci          if(strncasecompare("__Secure-", namep, 9))
59413498266Sopenharmony_ci            co->prefix |= COOKIE_PREFIX__SECURE;
59513498266Sopenharmony_ci          else if(strncasecompare("__Host-", namep, 7))
59613498266Sopenharmony_ci            co->prefix |= COOKIE_PREFIX__HOST;
59713498266Sopenharmony_ci        }
59813498266Sopenharmony_ci
59913498266Sopenharmony_ci        /*
60013498266Sopenharmony_ci         * Use strstore() below to properly deal with received cookie
60113498266Sopenharmony_ci         * headers that have the same string property set more than once,
60213498266Sopenharmony_ci         * and then we use the last one.
60313498266Sopenharmony_ci         */
60413498266Sopenharmony_ci
60513498266Sopenharmony_ci        if(!co->name) {
60613498266Sopenharmony_ci          /* The very first name/value pair is the actual cookie name */
60713498266Sopenharmony_ci          if(!sep) {
60813498266Sopenharmony_ci            /* Bad name/value pair. */
60913498266Sopenharmony_ci            badcookie = TRUE;
61013498266Sopenharmony_ci            break;
61113498266Sopenharmony_ci          }
61213498266Sopenharmony_ci          strstore(&co->name, namep, nlen);
61313498266Sopenharmony_ci          strstore(&co->value, valuep, vlen);
61413498266Sopenharmony_ci          done = TRUE;
61513498266Sopenharmony_ci          if(!co->name || !co->value) {
61613498266Sopenharmony_ci            badcookie = TRUE;
61713498266Sopenharmony_ci            break;
61813498266Sopenharmony_ci          }
61913498266Sopenharmony_ci          if(invalid_octets(co->value) || invalid_octets(co->name)) {
62013498266Sopenharmony_ci            infof(data, "invalid octets in name/value, cookie dropped");
62113498266Sopenharmony_ci            badcookie = TRUE;
62213498266Sopenharmony_ci            break;
62313498266Sopenharmony_ci          }
62413498266Sopenharmony_ci        }
62513498266Sopenharmony_ci        else if(!vlen) {
62613498266Sopenharmony_ci          /*
62713498266Sopenharmony_ci           * this was a "<name>=" with no content, and we must allow
62813498266Sopenharmony_ci           * 'secure' and 'httponly' specified this weirdly
62913498266Sopenharmony_ci           */
63013498266Sopenharmony_ci          done = TRUE;
63113498266Sopenharmony_ci          /*
63213498266Sopenharmony_ci           * secure cookies are only allowed to be set when the connection is
63313498266Sopenharmony_ci           * using a secure protocol, or when the cookie is being set by
63413498266Sopenharmony_ci           * reading from file
63513498266Sopenharmony_ci           */
63613498266Sopenharmony_ci          if((nlen == 6) && strncasecompare("secure", namep, 6)) {
63713498266Sopenharmony_ci            if(secure || !c->running) {
63813498266Sopenharmony_ci              co->secure = TRUE;
63913498266Sopenharmony_ci            }
64013498266Sopenharmony_ci            else {
64113498266Sopenharmony_ci              badcookie = TRUE;
64213498266Sopenharmony_ci              break;
64313498266Sopenharmony_ci            }
64413498266Sopenharmony_ci          }
64513498266Sopenharmony_ci          else if((nlen == 8) && strncasecompare("httponly", namep, 8))
64613498266Sopenharmony_ci            co->httponly = TRUE;
64713498266Sopenharmony_ci          else if(sep)
64813498266Sopenharmony_ci            /* there was a '=' so we're not done parsing this field */
64913498266Sopenharmony_ci            done = FALSE;
65013498266Sopenharmony_ci        }
65113498266Sopenharmony_ci        if(done)
65213498266Sopenharmony_ci          ;
65313498266Sopenharmony_ci        else if((nlen == 4) && strncasecompare("path", namep, 4)) {
65413498266Sopenharmony_ci          strstore(&co->path, valuep, vlen);
65513498266Sopenharmony_ci          if(!co->path) {
65613498266Sopenharmony_ci            badcookie = TRUE; /* out of memory bad */
65713498266Sopenharmony_ci            break;
65813498266Sopenharmony_ci          }
65913498266Sopenharmony_ci          free(co->spath); /* if this is set again */
66013498266Sopenharmony_ci          co->spath = sanitize_cookie_path(co->path);
66113498266Sopenharmony_ci          if(!co->spath) {
66213498266Sopenharmony_ci            badcookie = TRUE; /* out of memory bad */
66313498266Sopenharmony_ci            break;
66413498266Sopenharmony_ci          }
66513498266Sopenharmony_ci        }
66613498266Sopenharmony_ci        else if((nlen == 6) &&
66713498266Sopenharmony_ci                strncasecompare("domain", namep, 6) && vlen) {
66813498266Sopenharmony_ci          bool is_ip;
66913498266Sopenharmony_ci
67013498266Sopenharmony_ci          /*
67113498266Sopenharmony_ci           * Now, we make sure that our host is within the given domain, or
67213498266Sopenharmony_ci           * the given domain is not valid and thus cannot be set.
67313498266Sopenharmony_ci           */
67413498266Sopenharmony_ci
67513498266Sopenharmony_ci          if('.' == valuep[0]) {
67613498266Sopenharmony_ci            valuep++; /* ignore preceding dot */
67713498266Sopenharmony_ci            vlen--;
67813498266Sopenharmony_ci          }
67913498266Sopenharmony_ci
68013498266Sopenharmony_ci#ifndef USE_LIBPSL
68113498266Sopenharmony_ci          /*
68213498266Sopenharmony_ci           * Without PSL we don't know when the incoming cookie is set on a
68313498266Sopenharmony_ci           * TLD or otherwise "protected" suffix. To reduce risk, we require a
68413498266Sopenharmony_ci           * dot OR the exact host name being "localhost".
68513498266Sopenharmony_ci           */
68613498266Sopenharmony_ci          if(bad_domain(valuep, vlen))
68713498266Sopenharmony_ci            domain = ":";
68813498266Sopenharmony_ci#endif
68913498266Sopenharmony_ci
69013498266Sopenharmony_ci          is_ip = Curl_host_is_ipnum(domain ? domain : valuep);
69113498266Sopenharmony_ci
69213498266Sopenharmony_ci          if(!domain
69313498266Sopenharmony_ci             || (is_ip && !strncmp(valuep, domain, vlen) &&
69413498266Sopenharmony_ci                 (vlen == strlen(domain)))
69513498266Sopenharmony_ci             || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) {
69613498266Sopenharmony_ci            strstore(&co->domain, valuep, vlen);
69713498266Sopenharmony_ci            if(!co->domain) {
69813498266Sopenharmony_ci              badcookie = TRUE;
69913498266Sopenharmony_ci              break;
70013498266Sopenharmony_ci            }
70113498266Sopenharmony_ci            if(!is_ip)
70213498266Sopenharmony_ci              co->tailmatch = TRUE; /* we always do that if the domain name was
70313498266Sopenharmony_ci                                       given */
70413498266Sopenharmony_ci          }
70513498266Sopenharmony_ci          else {
70613498266Sopenharmony_ci            /*
70713498266Sopenharmony_ci             * We did not get a tailmatch and then the attempted set domain is
70813498266Sopenharmony_ci             * not a domain to which the current host belongs. Mark as bad.
70913498266Sopenharmony_ci             */
71013498266Sopenharmony_ci            badcookie = TRUE;
71113498266Sopenharmony_ci            infof(data, "skipped cookie with bad tailmatch domain: %s",
71213498266Sopenharmony_ci                  valuep);
71313498266Sopenharmony_ci          }
71413498266Sopenharmony_ci        }
71513498266Sopenharmony_ci        else if((nlen == 7) && strncasecompare("version", namep, 7)) {
71613498266Sopenharmony_ci          /* just ignore */
71713498266Sopenharmony_ci        }
71813498266Sopenharmony_ci        else if((nlen == 7) && strncasecompare("max-age", namep, 7)) {
71913498266Sopenharmony_ci          /*
72013498266Sopenharmony_ci           * Defined in RFC2109:
72113498266Sopenharmony_ci           *
72213498266Sopenharmony_ci           * Optional.  The Max-Age attribute defines the lifetime of the
72313498266Sopenharmony_ci           * cookie, in seconds.  The delta-seconds value is a decimal non-
72413498266Sopenharmony_ci           * negative integer.  After delta-seconds seconds elapse, the
72513498266Sopenharmony_ci           * client should discard the cookie.  A value of zero means the
72613498266Sopenharmony_ci           * cookie should be discarded immediately.
72713498266Sopenharmony_ci           */
72813498266Sopenharmony_ci          CURLofft offt;
72913498266Sopenharmony_ci          const char *maxage = valuep;
73013498266Sopenharmony_ci          offt = curlx_strtoofft((*maxage == '\"')?
73113498266Sopenharmony_ci                                 &maxage[1]:&maxage[0], NULL, 10,
73213498266Sopenharmony_ci                                 &co->expires);
73313498266Sopenharmony_ci          switch(offt) {
73413498266Sopenharmony_ci          case CURL_OFFT_FLOW:
73513498266Sopenharmony_ci            /* overflow, used max value */
73613498266Sopenharmony_ci            co->expires = CURL_OFF_T_MAX;
73713498266Sopenharmony_ci            break;
73813498266Sopenharmony_ci          case CURL_OFFT_INVAL:
73913498266Sopenharmony_ci            /* negative or otherwise bad, expire */
74013498266Sopenharmony_ci            co->expires = 1;
74113498266Sopenharmony_ci            break;
74213498266Sopenharmony_ci          case CURL_OFFT_OK:
74313498266Sopenharmony_ci            if(!co->expires)
74413498266Sopenharmony_ci              /* already expired */
74513498266Sopenharmony_ci              co->expires = 1;
74613498266Sopenharmony_ci            else if(CURL_OFF_T_MAX - now < co->expires)
74713498266Sopenharmony_ci              /* would overflow */
74813498266Sopenharmony_ci              co->expires = CURL_OFF_T_MAX;
74913498266Sopenharmony_ci            else
75013498266Sopenharmony_ci              co->expires += now;
75113498266Sopenharmony_ci            break;
75213498266Sopenharmony_ci          }
75313498266Sopenharmony_ci        }
75413498266Sopenharmony_ci        else if((nlen == 7) && strncasecompare("expires", namep, 7)) {
75513498266Sopenharmony_ci          char date[128];
75613498266Sopenharmony_ci          if(!co->expires && (vlen < sizeof(date))) {
75713498266Sopenharmony_ci            /* copy the date so that it can be null terminated */
75813498266Sopenharmony_ci            memcpy(date, valuep, vlen);
75913498266Sopenharmony_ci            date[vlen] = 0;
76013498266Sopenharmony_ci            /*
76113498266Sopenharmony_ci             * Let max-age have priority.
76213498266Sopenharmony_ci             *
76313498266Sopenharmony_ci             * If the date cannot get parsed for whatever reason, the cookie
76413498266Sopenharmony_ci             * will be treated as a session cookie
76513498266Sopenharmony_ci             */
76613498266Sopenharmony_ci            co->expires = Curl_getdate_capped(date);
76713498266Sopenharmony_ci
76813498266Sopenharmony_ci            /*
76913498266Sopenharmony_ci             * Session cookies have expires set to 0 so if we get that back
77013498266Sopenharmony_ci             * from the date parser let's add a second to make it a
77113498266Sopenharmony_ci             * non-session cookie
77213498266Sopenharmony_ci             */
77313498266Sopenharmony_ci            if(co->expires == 0)
77413498266Sopenharmony_ci              co->expires = 1;
77513498266Sopenharmony_ci            else if(co->expires < 0)
77613498266Sopenharmony_ci              co->expires = 0;
77713498266Sopenharmony_ci          }
77813498266Sopenharmony_ci        }
77913498266Sopenharmony_ci
78013498266Sopenharmony_ci        /*
78113498266Sopenharmony_ci         * Else, this is the second (or more) name we don't know about!
78213498266Sopenharmony_ci         */
78313498266Sopenharmony_ci      }
78413498266Sopenharmony_ci      else {
78513498266Sopenharmony_ci        /* this is an "illegal" <what>=<this> pair */
78613498266Sopenharmony_ci      }
78713498266Sopenharmony_ci
78813498266Sopenharmony_ci      while(*ptr && ISBLANK(*ptr))
78913498266Sopenharmony_ci        ptr++;
79013498266Sopenharmony_ci      if(*ptr == ';')
79113498266Sopenharmony_ci        ptr++;
79213498266Sopenharmony_ci      else
79313498266Sopenharmony_ci        break;
79413498266Sopenharmony_ci    } while(1);
79513498266Sopenharmony_ci
79613498266Sopenharmony_ci    if(!badcookie && !co->domain) {
79713498266Sopenharmony_ci      if(domain) {
79813498266Sopenharmony_ci        /* no domain was given in the header line, set the default */
79913498266Sopenharmony_ci        co->domain = strdup(domain);
80013498266Sopenharmony_ci        if(!co->domain)
80113498266Sopenharmony_ci          badcookie = TRUE;
80213498266Sopenharmony_ci      }
80313498266Sopenharmony_ci    }
80413498266Sopenharmony_ci
80513498266Sopenharmony_ci    if(!badcookie && !co->path && path) {
80613498266Sopenharmony_ci      /*
80713498266Sopenharmony_ci       * No path was given in the header line, set the default.  Note that the
80813498266Sopenharmony_ci       * passed-in path to this function MAY have a '?' and following part that
80913498266Sopenharmony_ci       * MUST NOT be stored as part of the path.
81013498266Sopenharmony_ci       */
81113498266Sopenharmony_ci      char *queryp = strchr(path, '?');
81213498266Sopenharmony_ci
81313498266Sopenharmony_ci      /*
81413498266Sopenharmony_ci       * queryp is where the interesting part of the path ends, so now we
81513498266Sopenharmony_ci       * want to the find the last
81613498266Sopenharmony_ci       */
81713498266Sopenharmony_ci      char *endslash;
81813498266Sopenharmony_ci      if(!queryp)
81913498266Sopenharmony_ci        endslash = strrchr(path, '/');
82013498266Sopenharmony_ci      else
82113498266Sopenharmony_ci        endslash = memrchr(path, '/', (queryp - path));
82213498266Sopenharmony_ci      if(endslash) {
82313498266Sopenharmony_ci        size_t pathlen = (endslash-path + 1); /* include end slash */
82413498266Sopenharmony_ci        co->path = Curl_memdup0(path, pathlen);
82513498266Sopenharmony_ci        if(co->path) {
82613498266Sopenharmony_ci          co->spath = sanitize_cookie_path(co->path);
82713498266Sopenharmony_ci          if(!co->spath)
82813498266Sopenharmony_ci            badcookie = TRUE; /* out of memory bad */
82913498266Sopenharmony_ci        }
83013498266Sopenharmony_ci        else
83113498266Sopenharmony_ci          badcookie = TRUE;
83213498266Sopenharmony_ci      }
83313498266Sopenharmony_ci    }
83413498266Sopenharmony_ci
83513498266Sopenharmony_ci    /*
83613498266Sopenharmony_ci     * If we didn't get a cookie name, or a bad one, the this is an illegal
83713498266Sopenharmony_ci     * line so bail out.
83813498266Sopenharmony_ci     */
83913498266Sopenharmony_ci    if(badcookie || !co->name) {
84013498266Sopenharmony_ci      freecookie(co);
84113498266Sopenharmony_ci      return NULL;
84213498266Sopenharmony_ci    }
84313498266Sopenharmony_ci    data->req.setcookies++;
84413498266Sopenharmony_ci  }
84513498266Sopenharmony_ci  else {
84613498266Sopenharmony_ci    /*
84713498266Sopenharmony_ci     * This line is NOT an HTTP header style line, we do offer support for
84813498266Sopenharmony_ci     * reading the odd netscape cookies-file format here
84913498266Sopenharmony_ci     */
85013498266Sopenharmony_ci    char *ptr;
85113498266Sopenharmony_ci    char *firstptr;
85213498266Sopenharmony_ci    char *tok_buf = NULL;
85313498266Sopenharmony_ci    int fields;
85413498266Sopenharmony_ci
85513498266Sopenharmony_ci    /*
85613498266Sopenharmony_ci     * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked
85713498266Sopenharmony_ci     * with httpOnly after the domain name are not accessible from javascripts,
85813498266Sopenharmony_ci     * but since curl does not operate at javascript level, we include them
85913498266Sopenharmony_ci     * anyway. In Firefox's cookie files, these lines are preceded with
86013498266Sopenharmony_ci     * #HttpOnly_ and then everything is as usual, so we skip 10 characters of
86113498266Sopenharmony_ci     * the line..
86213498266Sopenharmony_ci     */
86313498266Sopenharmony_ci    if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
86413498266Sopenharmony_ci      lineptr += 10;
86513498266Sopenharmony_ci      co->httponly = TRUE;
86613498266Sopenharmony_ci    }
86713498266Sopenharmony_ci
86813498266Sopenharmony_ci    if(lineptr[0]=='#') {
86913498266Sopenharmony_ci      /* don't even try the comments */
87013498266Sopenharmony_ci      free(co);
87113498266Sopenharmony_ci      return NULL;
87213498266Sopenharmony_ci    }
87313498266Sopenharmony_ci    /* strip off the possible end-of-line characters */
87413498266Sopenharmony_ci    ptr = strchr(lineptr, '\r');
87513498266Sopenharmony_ci    if(ptr)
87613498266Sopenharmony_ci      *ptr = 0; /* clear it */
87713498266Sopenharmony_ci    ptr = strchr(lineptr, '\n');
87813498266Sopenharmony_ci    if(ptr)
87913498266Sopenharmony_ci      *ptr = 0; /* clear it */
88013498266Sopenharmony_ci
88113498266Sopenharmony_ci    firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */
88213498266Sopenharmony_ci
88313498266Sopenharmony_ci    /*
88413498266Sopenharmony_ci     * Now loop through the fields and init the struct we already have
88513498266Sopenharmony_ci     * allocated
88613498266Sopenharmony_ci     */
88713498266Sopenharmony_ci    for(ptr = firstptr, fields = 0; ptr && !badcookie;
88813498266Sopenharmony_ci        ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
88913498266Sopenharmony_ci      switch(fields) {
89013498266Sopenharmony_ci      case 0:
89113498266Sopenharmony_ci        if(ptr[0]=='.') /* skip preceding dots */
89213498266Sopenharmony_ci          ptr++;
89313498266Sopenharmony_ci        co->domain = strdup(ptr);
89413498266Sopenharmony_ci        if(!co->domain)
89513498266Sopenharmony_ci          badcookie = TRUE;
89613498266Sopenharmony_ci        break;
89713498266Sopenharmony_ci      case 1:
89813498266Sopenharmony_ci        /*
89913498266Sopenharmony_ci         * flag: A TRUE/FALSE value indicating if all machines within a given
90013498266Sopenharmony_ci         * domain can access the variable. Set TRUE when the cookie says
90113498266Sopenharmony_ci         * .domain.com and to false when the domain is complete www.domain.com
90213498266Sopenharmony_ci         */
90313498266Sopenharmony_ci        co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
90413498266Sopenharmony_ci        break;
90513498266Sopenharmony_ci      case 2:
90613498266Sopenharmony_ci        /* The file format allows the path field to remain not filled in */
90713498266Sopenharmony_ci        if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
90813498266Sopenharmony_ci          /* only if the path doesn't look like a boolean option! */
90913498266Sopenharmony_ci          co->path = strdup(ptr);
91013498266Sopenharmony_ci          if(!co->path)
91113498266Sopenharmony_ci            badcookie = TRUE;
91213498266Sopenharmony_ci          else {
91313498266Sopenharmony_ci            co->spath = sanitize_cookie_path(co->path);
91413498266Sopenharmony_ci            if(!co->spath) {
91513498266Sopenharmony_ci              badcookie = TRUE; /* out of memory bad */
91613498266Sopenharmony_ci            }
91713498266Sopenharmony_ci          }
91813498266Sopenharmony_ci          break;
91913498266Sopenharmony_ci        }
92013498266Sopenharmony_ci        /* this doesn't look like a path, make one up! */
92113498266Sopenharmony_ci        co->path = strdup("/");
92213498266Sopenharmony_ci        if(!co->path)
92313498266Sopenharmony_ci          badcookie = TRUE;
92413498266Sopenharmony_ci        co->spath = strdup("/");
92513498266Sopenharmony_ci        if(!co->spath)
92613498266Sopenharmony_ci          badcookie = TRUE;
92713498266Sopenharmony_ci        fields++; /* add a field and fall down to secure */
92813498266Sopenharmony_ci        FALLTHROUGH();
92913498266Sopenharmony_ci      case 3:
93013498266Sopenharmony_ci        co->secure = FALSE;
93113498266Sopenharmony_ci        if(strcasecompare(ptr, "TRUE")) {
93213498266Sopenharmony_ci          if(secure || c->running)
93313498266Sopenharmony_ci            co->secure = TRUE;
93413498266Sopenharmony_ci          else
93513498266Sopenharmony_ci            badcookie = TRUE;
93613498266Sopenharmony_ci        }
93713498266Sopenharmony_ci        break;
93813498266Sopenharmony_ci      case 4:
93913498266Sopenharmony_ci        if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
94013498266Sopenharmony_ci          badcookie = TRUE;
94113498266Sopenharmony_ci        break;
94213498266Sopenharmony_ci      case 5:
94313498266Sopenharmony_ci        co->name = strdup(ptr);
94413498266Sopenharmony_ci        if(!co->name)
94513498266Sopenharmony_ci          badcookie = TRUE;
94613498266Sopenharmony_ci        else {
94713498266Sopenharmony_ci          /* For Netscape file format cookies we check prefix on the name */
94813498266Sopenharmony_ci          if(strncasecompare("__Secure-", co->name, 9))
94913498266Sopenharmony_ci            co->prefix |= COOKIE_PREFIX__SECURE;
95013498266Sopenharmony_ci          else if(strncasecompare("__Host-", co->name, 7))
95113498266Sopenharmony_ci            co->prefix |= COOKIE_PREFIX__HOST;
95213498266Sopenharmony_ci        }
95313498266Sopenharmony_ci        break;
95413498266Sopenharmony_ci      case 6:
95513498266Sopenharmony_ci        co->value = strdup(ptr);
95613498266Sopenharmony_ci        if(!co->value)
95713498266Sopenharmony_ci          badcookie = TRUE;
95813498266Sopenharmony_ci        break;
95913498266Sopenharmony_ci      }
96013498266Sopenharmony_ci    }
96113498266Sopenharmony_ci    if(6 == fields) {
96213498266Sopenharmony_ci      /* we got a cookie with blank contents, fix it */
96313498266Sopenharmony_ci      co->value = strdup("");
96413498266Sopenharmony_ci      if(!co->value)
96513498266Sopenharmony_ci        badcookie = TRUE;
96613498266Sopenharmony_ci      else
96713498266Sopenharmony_ci        fields++;
96813498266Sopenharmony_ci    }
96913498266Sopenharmony_ci
97013498266Sopenharmony_ci    if(!badcookie && (7 != fields))
97113498266Sopenharmony_ci      /* we did not find the sufficient number of fields */
97213498266Sopenharmony_ci      badcookie = TRUE;
97313498266Sopenharmony_ci
97413498266Sopenharmony_ci    if(badcookie) {
97513498266Sopenharmony_ci      freecookie(co);
97613498266Sopenharmony_ci      return NULL;
97713498266Sopenharmony_ci    }
97813498266Sopenharmony_ci
97913498266Sopenharmony_ci  }
98013498266Sopenharmony_ci
98113498266Sopenharmony_ci  if(co->prefix & COOKIE_PREFIX__SECURE) {
98213498266Sopenharmony_ci    /* The __Secure- prefix only requires that the cookie be set secure */
98313498266Sopenharmony_ci    if(!co->secure) {
98413498266Sopenharmony_ci      freecookie(co);
98513498266Sopenharmony_ci      return NULL;
98613498266Sopenharmony_ci    }
98713498266Sopenharmony_ci  }
98813498266Sopenharmony_ci  if(co->prefix & COOKIE_PREFIX__HOST) {
98913498266Sopenharmony_ci    /*
99013498266Sopenharmony_ci     * The __Host- prefix requires the cookie to be secure, have a "/" path
99113498266Sopenharmony_ci     * and not have a domain set.
99213498266Sopenharmony_ci     */
99313498266Sopenharmony_ci    if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
99413498266Sopenharmony_ci      ;
99513498266Sopenharmony_ci    else {
99613498266Sopenharmony_ci      freecookie(co);
99713498266Sopenharmony_ci      return NULL;
99813498266Sopenharmony_ci    }
99913498266Sopenharmony_ci  }
100013498266Sopenharmony_ci
100113498266Sopenharmony_ci  if(!c->running &&    /* read from a file */
100213498266Sopenharmony_ci     c->newsession &&  /* clean session cookies */
100313498266Sopenharmony_ci     !co->expires) {   /* this is a session cookie since it doesn't expire! */
100413498266Sopenharmony_ci    freecookie(co);
100513498266Sopenharmony_ci    return NULL;
100613498266Sopenharmony_ci  }
100713498266Sopenharmony_ci
100813498266Sopenharmony_ci  co->livecookie = c->running;
100913498266Sopenharmony_ci  co->creationtime = ++c->lastct;
101013498266Sopenharmony_ci
101113498266Sopenharmony_ci  /*
101213498266Sopenharmony_ci   * Now we have parsed the incoming line, we must now check if this supersedes
101313498266Sopenharmony_ci   * an already existing cookie, which it may if the previous have the same
101413498266Sopenharmony_ci   * domain and path as this.
101513498266Sopenharmony_ci   */
101613498266Sopenharmony_ci
101713498266Sopenharmony_ci  /* at first, remove expired cookies */
101813498266Sopenharmony_ci  if(!noexpire)
101913498266Sopenharmony_ci    remove_expired(c);
102013498266Sopenharmony_ci
102113498266Sopenharmony_ci#ifdef USE_LIBPSL
102213498266Sopenharmony_ci  /*
102313498266Sopenharmony_ci   * Check if the domain is a Public Suffix and if yes, ignore the cookie. We
102413498266Sopenharmony_ci   * must also check that the data handle isn't NULL since the psl code will
102513498266Sopenharmony_ci   * dereference it.
102613498266Sopenharmony_ci   */
102713498266Sopenharmony_ci  if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) {
102813498266Sopenharmony_ci    bool acceptable = FALSE;
102913498266Sopenharmony_ci    char lcase[256];
103013498266Sopenharmony_ci    char lcookie[256];
103113498266Sopenharmony_ci    size_t dlen = strlen(domain);
103213498266Sopenharmony_ci    size_t clen = strlen(co->domain);
103313498266Sopenharmony_ci    if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) {
103413498266Sopenharmony_ci      const psl_ctx_t *psl = Curl_psl_use(data);
103513498266Sopenharmony_ci      if(psl) {
103613498266Sopenharmony_ci        /* the PSL check requires lowercase domain name and pattern */
103713498266Sopenharmony_ci        Curl_strntolower(lcase, domain, dlen + 1);
103813498266Sopenharmony_ci        Curl_strntolower(lcookie, co->domain, clen + 1);
103913498266Sopenharmony_ci        acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie);
104013498266Sopenharmony_ci        Curl_psl_release(data);
104113498266Sopenharmony_ci      }
104213498266Sopenharmony_ci      else
104313498266Sopenharmony_ci        acceptable = !bad_domain(domain, strlen(domain));
104413498266Sopenharmony_ci    }
104513498266Sopenharmony_ci
104613498266Sopenharmony_ci    if(!acceptable) {
104713498266Sopenharmony_ci      infof(data, "cookie '%s' dropped, domain '%s' must not "
104813498266Sopenharmony_ci                  "set cookies for '%s'", co->name, domain, co->domain);
104913498266Sopenharmony_ci      freecookie(co);
105013498266Sopenharmony_ci      return NULL;
105113498266Sopenharmony_ci    }
105213498266Sopenharmony_ci  }
105313498266Sopenharmony_ci#endif
105413498266Sopenharmony_ci
105513498266Sopenharmony_ci  /* A non-secure cookie may not overlay an existing secure cookie. */
105613498266Sopenharmony_ci  myhash = cookiehash(co->domain);
105713498266Sopenharmony_ci  clist = c->cookies[myhash];
105813498266Sopenharmony_ci  while(clist) {
105913498266Sopenharmony_ci    if(strcasecompare(clist->name, co->name)) {
106013498266Sopenharmony_ci      /* the names are identical */
106113498266Sopenharmony_ci      bool matching_domains = FALSE;
106213498266Sopenharmony_ci
106313498266Sopenharmony_ci      if(clist->domain && co->domain) {
106413498266Sopenharmony_ci        if(strcasecompare(clist->domain, co->domain))
106513498266Sopenharmony_ci          /* The domains are identical */
106613498266Sopenharmony_ci          matching_domains = TRUE;
106713498266Sopenharmony_ci      }
106813498266Sopenharmony_ci      else if(!clist->domain && !co->domain)
106913498266Sopenharmony_ci        matching_domains = TRUE;
107013498266Sopenharmony_ci
107113498266Sopenharmony_ci      if(matching_domains && /* the domains were identical */
107213498266Sopenharmony_ci         clist->spath && co->spath && /* both have paths */
107313498266Sopenharmony_ci         clist->secure && !co->secure && !secure) {
107413498266Sopenharmony_ci        size_t cllen;
107513498266Sopenharmony_ci        const char *sep;
107613498266Sopenharmony_ci
107713498266Sopenharmony_ci        /*
107813498266Sopenharmony_ci         * A non-secure cookie may not overlay an existing secure cookie.
107913498266Sopenharmony_ci         * For an existing cookie "a" with path "/login", refuse a new
108013498266Sopenharmony_ci         * cookie "a" with for example path "/login/en", while the path
108113498266Sopenharmony_ci         * "/loginhelper" is ok.
108213498266Sopenharmony_ci         */
108313498266Sopenharmony_ci
108413498266Sopenharmony_ci        sep = strchr(clist->spath + 1, '/');
108513498266Sopenharmony_ci
108613498266Sopenharmony_ci        if(sep)
108713498266Sopenharmony_ci          cllen = sep - clist->spath;
108813498266Sopenharmony_ci        else
108913498266Sopenharmony_ci          cllen = strlen(clist->spath);
109013498266Sopenharmony_ci
109113498266Sopenharmony_ci        if(strncasecompare(clist->spath, co->spath, cllen)) {
109213498266Sopenharmony_ci          infof(data, "cookie '%s' for domain '%s' dropped, would "
109313498266Sopenharmony_ci                "overlay an existing cookie", co->name, co->domain);
109413498266Sopenharmony_ci          freecookie(co);
109513498266Sopenharmony_ci          return NULL;
109613498266Sopenharmony_ci        }
109713498266Sopenharmony_ci      }
109813498266Sopenharmony_ci    }
109913498266Sopenharmony_ci
110013498266Sopenharmony_ci    if(!replace_co && strcasecompare(clist->name, co->name)) {
110113498266Sopenharmony_ci      /* the names are identical */
110213498266Sopenharmony_ci
110313498266Sopenharmony_ci      if(clist->domain && co->domain) {
110413498266Sopenharmony_ci        if(strcasecompare(clist->domain, co->domain) &&
110513498266Sopenharmony_ci          (clist->tailmatch == co->tailmatch))
110613498266Sopenharmony_ci          /* The domains are identical */
110713498266Sopenharmony_ci          replace_old = TRUE;
110813498266Sopenharmony_ci      }
110913498266Sopenharmony_ci      else if(!clist->domain && !co->domain)
111013498266Sopenharmony_ci        replace_old = TRUE;
111113498266Sopenharmony_ci
111213498266Sopenharmony_ci      if(replace_old) {
111313498266Sopenharmony_ci        /* the domains were identical */
111413498266Sopenharmony_ci
111513498266Sopenharmony_ci        if(clist->spath && co->spath &&
111613498266Sopenharmony_ci           !strcasecompare(clist->spath, co->spath))
111713498266Sopenharmony_ci          replace_old = FALSE;
111813498266Sopenharmony_ci        else if(!clist->spath != !co->spath)
111913498266Sopenharmony_ci          replace_old = FALSE;
112013498266Sopenharmony_ci      }
112113498266Sopenharmony_ci
112213498266Sopenharmony_ci      if(replace_old && !co->livecookie && clist->livecookie) {
112313498266Sopenharmony_ci        /*
112413498266Sopenharmony_ci         * Both cookies matched fine, except that the already present cookie is
112513498266Sopenharmony_ci         * "live", which means it was set from a header, while the new one was
112613498266Sopenharmony_ci         * read from a file and thus isn't "live". "live" cookies are preferred
112713498266Sopenharmony_ci         * so the new cookie is freed.
112813498266Sopenharmony_ci         */
112913498266Sopenharmony_ci        freecookie(co);
113013498266Sopenharmony_ci        return NULL;
113113498266Sopenharmony_ci      }
113213498266Sopenharmony_ci      if(replace_old) {
113313498266Sopenharmony_ci        replace_co = co;
113413498266Sopenharmony_ci        replace_clist = clist;
113513498266Sopenharmony_ci      }
113613498266Sopenharmony_ci    }
113713498266Sopenharmony_ci    lastc = clist;
113813498266Sopenharmony_ci    clist = clist->next;
113913498266Sopenharmony_ci  }
114013498266Sopenharmony_ci  if(replace_co) {
114113498266Sopenharmony_ci    co = replace_co;
114213498266Sopenharmony_ci    clist = replace_clist;
114313498266Sopenharmony_ci    co->next = clist->next; /* get the next-pointer first */
114413498266Sopenharmony_ci
114513498266Sopenharmony_ci    /* when replacing, creationtime is kept from old */
114613498266Sopenharmony_ci    co->creationtime = clist->creationtime;
114713498266Sopenharmony_ci
114813498266Sopenharmony_ci    /* then free all the old pointers */
114913498266Sopenharmony_ci    free(clist->name);
115013498266Sopenharmony_ci    free(clist->value);
115113498266Sopenharmony_ci    free(clist->domain);
115213498266Sopenharmony_ci    free(clist->path);
115313498266Sopenharmony_ci    free(clist->spath);
115413498266Sopenharmony_ci
115513498266Sopenharmony_ci    *clist = *co;  /* then store all the new data */
115613498266Sopenharmony_ci
115713498266Sopenharmony_ci    free(co);   /* free the newly allocated memory */
115813498266Sopenharmony_ci    co = clist;
115913498266Sopenharmony_ci  }
116013498266Sopenharmony_ci
116113498266Sopenharmony_ci  if(c->running)
116213498266Sopenharmony_ci    /* Only show this when NOT reading the cookies from a file */
116313498266Sopenharmony_ci    infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
116413498266Sopenharmony_ci          "expire %" CURL_FORMAT_CURL_OFF_T,
116513498266Sopenharmony_ci          replace_old?"Replaced":"Added", co->name, co->value,
116613498266Sopenharmony_ci          co->domain, co->path, co->expires);
116713498266Sopenharmony_ci
116813498266Sopenharmony_ci  if(!replace_old) {
116913498266Sopenharmony_ci    /* then make the last item point on this new one */
117013498266Sopenharmony_ci    if(lastc)
117113498266Sopenharmony_ci      lastc->next = co;
117213498266Sopenharmony_ci    else
117313498266Sopenharmony_ci      c->cookies[myhash] = co;
117413498266Sopenharmony_ci    c->numcookies++; /* one more cookie in the jar */
117513498266Sopenharmony_ci  }
117613498266Sopenharmony_ci
117713498266Sopenharmony_ci  /*
117813498266Sopenharmony_ci   * Now that we've added a new cookie to the jar, update the expiration
117913498266Sopenharmony_ci   * tracker in case it is the next one to expire.
118013498266Sopenharmony_ci   */
118113498266Sopenharmony_ci  if(co->expires && (co->expires < c->next_expiration))
118213498266Sopenharmony_ci    c->next_expiration = co->expires;
118313498266Sopenharmony_ci
118413498266Sopenharmony_ci  return co;
118513498266Sopenharmony_ci}
118613498266Sopenharmony_ci
118713498266Sopenharmony_ci
118813498266Sopenharmony_ci/*
118913498266Sopenharmony_ci * Curl_cookie_init()
119013498266Sopenharmony_ci *
119113498266Sopenharmony_ci * Inits a cookie struct to read data from a local file. This is always
119213498266Sopenharmony_ci * called before any cookies are set. File may be NULL in which case only the
119313498266Sopenharmony_ci * struct is initialized. Is file is "-" then STDIN is read.
119413498266Sopenharmony_ci *
119513498266Sopenharmony_ci * If 'newsession' is TRUE, discard all "session cookies" on read from file.
119613498266Sopenharmony_ci *
119713498266Sopenharmony_ci * Note that 'data' might be called as NULL pointer. If data is NULL, 'file'
119813498266Sopenharmony_ci * will be ignored.
119913498266Sopenharmony_ci *
120013498266Sopenharmony_ci * Returns NULL on out of memory. Invalid cookies are ignored.
120113498266Sopenharmony_ci */
120213498266Sopenharmony_cistruct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
120313498266Sopenharmony_ci                                    const char *file,
120413498266Sopenharmony_ci                                    struct CookieInfo *inc,
120513498266Sopenharmony_ci                                    bool newsession)
120613498266Sopenharmony_ci{
120713498266Sopenharmony_ci  struct CookieInfo *c;
120813498266Sopenharmony_ci  char *line = NULL;
120913498266Sopenharmony_ci  FILE *handle = NULL;
121013498266Sopenharmony_ci
121113498266Sopenharmony_ci  if(!inc) {
121213498266Sopenharmony_ci    /* we didn't get a struct, create one */
121313498266Sopenharmony_ci    c = calloc(1, sizeof(struct CookieInfo));
121413498266Sopenharmony_ci    if(!c)
121513498266Sopenharmony_ci      return NULL; /* failed to get memory */
121613498266Sopenharmony_ci    /*
121713498266Sopenharmony_ci     * Initialize the next_expiration time to signal that we don't have enough
121813498266Sopenharmony_ci     * information yet.
121913498266Sopenharmony_ci     */
122013498266Sopenharmony_ci    c->next_expiration = CURL_OFF_T_MAX;
122113498266Sopenharmony_ci  }
122213498266Sopenharmony_ci  else {
122313498266Sopenharmony_ci    /* we got an already existing one, use that */
122413498266Sopenharmony_ci    c = inc;
122513498266Sopenharmony_ci  }
122613498266Sopenharmony_ci  c->newsession = newsession; /* new session? */
122713498266Sopenharmony_ci
122813498266Sopenharmony_ci  if(data) {
122913498266Sopenharmony_ci    FILE *fp = NULL;
123013498266Sopenharmony_ci    if(file && *file) {
123113498266Sopenharmony_ci      if(!strcmp(file, "-"))
123213498266Sopenharmony_ci        fp = stdin;
123313498266Sopenharmony_ci      else {
123413498266Sopenharmony_ci        fp = fopen(file, "rb");
123513498266Sopenharmony_ci        if(!fp)
123613498266Sopenharmony_ci          infof(data, "WARNING: failed to open cookie file \"%s\"", file);
123713498266Sopenharmony_ci        else
123813498266Sopenharmony_ci          handle = fp;
123913498266Sopenharmony_ci      }
124013498266Sopenharmony_ci    }
124113498266Sopenharmony_ci
124213498266Sopenharmony_ci    c->running = FALSE; /* this is not running, this is init */
124313498266Sopenharmony_ci    if(fp) {
124413498266Sopenharmony_ci
124513498266Sopenharmony_ci      line = malloc(MAX_COOKIE_LINE);
124613498266Sopenharmony_ci      if(!line)
124713498266Sopenharmony_ci        goto fail;
124813498266Sopenharmony_ci      while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
124913498266Sopenharmony_ci        char *lineptr = line;
125013498266Sopenharmony_ci        bool headerline = FALSE;
125113498266Sopenharmony_ci        if(checkprefix("Set-Cookie:", line)) {
125213498266Sopenharmony_ci          /* This is a cookie line, get it! */
125313498266Sopenharmony_ci          lineptr = &line[11];
125413498266Sopenharmony_ci          headerline = TRUE;
125513498266Sopenharmony_ci          while(*lineptr && ISBLANK(*lineptr))
125613498266Sopenharmony_ci            lineptr++;
125713498266Sopenharmony_ci        }
125813498266Sopenharmony_ci
125913498266Sopenharmony_ci        Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
126013498266Sopenharmony_ci      }
126113498266Sopenharmony_ci      free(line); /* free the line buffer */
126213498266Sopenharmony_ci
126313498266Sopenharmony_ci      /*
126413498266Sopenharmony_ci       * Remove expired cookies from the hash. We must make sure to run this
126513498266Sopenharmony_ci       * after reading the file, and not on every cookie.
126613498266Sopenharmony_ci       */
126713498266Sopenharmony_ci      remove_expired(c);
126813498266Sopenharmony_ci
126913498266Sopenharmony_ci      if(handle)
127013498266Sopenharmony_ci        fclose(handle);
127113498266Sopenharmony_ci    }
127213498266Sopenharmony_ci    data->state.cookie_engine = TRUE;
127313498266Sopenharmony_ci  }
127413498266Sopenharmony_ci  c->running = TRUE;          /* now, we're running */
127513498266Sopenharmony_ci
127613498266Sopenharmony_ci  return c;
127713498266Sopenharmony_ci
127813498266Sopenharmony_cifail:
127913498266Sopenharmony_ci  free(line);
128013498266Sopenharmony_ci  /*
128113498266Sopenharmony_ci   * Only clean up if we allocated it here, as the original could still be in
128213498266Sopenharmony_ci   * use by a share handle.
128313498266Sopenharmony_ci   */
128413498266Sopenharmony_ci  if(!inc)
128513498266Sopenharmony_ci    Curl_cookie_cleanup(c);
128613498266Sopenharmony_ci  if(handle)
128713498266Sopenharmony_ci    fclose(handle);
128813498266Sopenharmony_ci  return NULL; /* out of memory */
128913498266Sopenharmony_ci}
129013498266Sopenharmony_ci
129113498266Sopenharmony_ci/*
129213498266Sopenharmony_ci * cookie_sort
129313498266Sopenharmony_ci *
129413498266Sopenharmony_ci * Helper function to sort cookies such that the longest path gets before the
129513498266Sopenharmony_ci * shorter path. Path, domain and name lengths are considered in that order,
129613498266Sopenharmony_ci * with the creationtime as the tiebreaker. The creationtime is guaranteed to
129713498266Sopenharmony_ci * be unique per cookie, so we know we will get an ordering at that point.
129813498266Sopenharmony_ci */
129913498266Sopenharmony_cistatic int cookie_sort(const void *p1, const void *p2)
130013498266Sopenharmony_ci{
130113498266Sopenharmony_ci  struct Cookie *c1 = *(struct Cookie **)p1;
130213498266Sopenharmony_ci  struct Cookie *c2 = *(struct Cookie **)p2;
130313498266Sopenharmony_ci  size_t l1, l2;
130413498266Sopenharmony_ci
130513498266Sopenharmony_ci  /* 1 - compare cookie path lengths */
130613498266Sopenharmony_ci  l1 = c1->path ? strlen(c1->path) : 0;
130713498266Sopenharmony_ci  l2 = c2->path ? strlen(c2->path) : 0;
130813498266Sopenharmony_ci
130913498266Sopenharmony_ci  if(l1 != l2)
131013498266Sopenharmony_ci    return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
131113498266Sopenharmony_ci
131213498266Sopenharmony_ci  /* 2 - compare cookie domain lengths */
131313498266Sopenharmony_ci  l1 = c1->domain ? strlen(c1->domain) : 0;
131413498266Sopenharmony_ci  l2 = c2->domain ? strlen(c2->domain) : 0;
131513498266Sopenharmony_ci
131613498266Sopenharmony_ci  if(l1 != l2)
131713498266Sopenharmony_ci    return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
131813498266Sopenharmony_ci
131913498266Sopenharmony_ci  /* 3 - compare cookie name lengths */
132013498266Sopenharmony_ci  l1 = c1->name ? strlen(c1->name) : 0;
132113498266Sopenharmony_ci  l2 = c2->name ? strlen(c2->name) : 0;
132213498266Sopenharmony_ci
132313498266Sopenharmony_ci  if(l1 != l2)
132413498266Sopenharmony_ci    return (l2 > l1) ? 1 : -1;
132513498266Sopenharmony_ci
132613498266Sopenharmony_ci  /* 4 - compare cookie creation time */
132713498266Sopenharmony_ci  return (c2->creationtime > c1->creationtime) ? 1 : -1;
132813498266Sopenharmony_ci}
132913498266Sopenharmony_ci
133013498266Sopenharmony_ci/*
133113498266Sopenharmony_ci * cookie_sort_ct
133213498266Sopenharmony_ci *
133313498266Sopenharmony_ci * Helper function to sort cookies according to creation time.
133413498266Sopenharmony_ci */
133513498266Sopenharmony_cistatic int cookie_sort_ct(const void *p1, const void *p2)
133613498266Sopenharmony_ci{
133713498266Sopenharmony_ci  struct Cookie *c1 = *(struct Cookie **)p1;
133813498266Sopenharmony_ci  struct Cookie *c2 = *(struct Cookie **)p2;
133913498266Sopenharmony_ci
134013498266Sopenharmony_ci  return (c2->creationtime > c1->creationtime) ? 1 : -1;
134113498266Sopenharmony_ci}
134213498266Sopenharmony_ci
134313498266Sopenharmony_ci#define CLONE(field)                     \
134413498266Sopenharmony_ci  do {                                   \
134513498266Sopenharmony_ci    if(src->field) {                     \
134613498266Sopenharmony_ci      d->field = strdup(src->field);     \
134713498266Sopenharmony_ci      if(!d->field)                      \
134813498266Sopenharmony_ci        goto fail;                       \
134913498266Sopenharmony_ci    }                                    \
135013498266Sopenharmony_ci  } while(0)
135113498266Sopenharmony_ci
135213498266Sopenharmony_cistatic struct Cookie *dup_cookie(struct Cookie *src)
135313498266Sopenharmony_ci{
135413498266Sopenharmony_ci  struct Cookie *d = calloc(1, sizeof(struct Cookie));
135513498266Sopenharmony_ci  if(d) {
135613498266Sopenharmony_ci    CLONE(domain);
135713498266Sopenharmony_ci    CLONE(path);
135813498266Sopenharmony_ci    CLONE(spath);
135913498266Sopenharmony_ci    CLONE(name);
136013498266Sopenharmony_ci    CLONE(value);
136113498266Sopenharmony_ci    d->expires = src->expires;
136213498266Sopenharmony_ci    d->tailmatch = src->tailmatch;
136313498266Sopenharmony_ci    d->secure = src->secure;
136413498266Sopenharmony_ci    d->livecookie = src->livecookie;
136513498266Sopenharmony_ci    d->httponly = src->httponly;
136613498266Sopenharmony_ci    d->creationtime = src->creationtime;
136713498266Sopenharmony_ci  }
136813498266Sopenharmony_ci  return d;
136913498266Sopenharmony_ci
137013498266Sopenharmony_cifail:
137113498266Sopenharmony_ci  freecookie(d);
137213498266Sopenharmony_ci  return NULL;
137313498266Sopenharmony_ci}
137413498266Sopenharmony_ci
137513498266Sopenharmony_ci/*
137613498266Sopenharmony_ci * Curl_cookie_getlist
137713498266Sopenharmony_ci *
137813498266Sopenharmony_ci * For a given host and path, return a linked list of cookies that the client
137913498266Sopenharmony_ci * should send to the server if used now. The secure boolean informs the cookie
138013498266Sopenharmony_ci * if a secure connection is achieved or not.
138113498266Sopenharmony_ci *
138213498266Sopenharmony_ci * It shall only return cookies that haven't expired.
138313498266Sopenharmony_ci */
138413498266Sopenharmony_cistruct Cookie *Curl_cookie_getlist(struct Curl_easy *data,
138513498266Sopenharmony_ci                                   struct CookieInfo *c,
138613498266Sopenharmony_ci                                   const char *host, const char *path,
138713498266Sopenharmony_ci                                   bool secure)
138813498266Sopenharmony_ci{
138913498266Sopenharmony_ci  struct Cookie *newco;
139013498266Sopenharmony_ci  struct Cookie *co;
139113498266Sopenharmony_ci  struct Cookie *mainco = NULL;
139213498266Sopenharmony_ci  size_t matches = 0;
139313498266Sopenharmony_ci  bool is_ip;
139413498266Sopenharmony_ci  const size_t myhash = cookiehash(host);
139513498266Sopenharmony_ci
139613498266Sopenharmony_ci  if(!c || !c->cookies[myhash])
139713498266Sopenharmony_ci    return NULL; /* no cookie struct or no cookies in the struct */
139813498266Sopenharmony_ci
139913498266Sopenharmony_ci  /* at first, remove expired cookies */
140013498266Sopenharmony_ci  remove_expired(c);
140113498266Sopenharmony_ci
140213498266Sopenharmony_ci  /* check if host is an IP(v4|v6) address */
140313498266Sopenharmony_ci  is_ip = Curl_host_is_ipnum(host);
140413498266Sopenharmony_ci
140513498266Sopenharmony_ci  co = c->cookies[myhash];
140613498266Sopenharmony_ci
140713498266Sopenharmony_ci  while(co) {
140813498266Sopenharmony_ci    /* if the cookie requires we're secure we must only continue if we are! */
140913498266Sopenharmony_ci    if(co->secure?secure:TRUE) {
141013498266Sopenharmony_ci
141113498266Sopenharmony_ci      /* now check if the domain is correct */
141213498266Sopenharmony_ci      if(!co->domain ||
141313498266Sopenharmony_ci         (co->tailmatch && !is_ip &&
141413498266Sopenharmony_ci          cookie_tailmatch(co->domain, strlen(co->domain), host)) ||
141513498266Sopenharmony_ci         ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
141613498266Sopenharmony_ci        /*
141713498266Sopenharmony_ci         * the right part of the host matches the domain stuff in the
141813498266Sopenharmony_ci         * cookie data
141913498266Sopenharmony_ci         */
142013498266Sopenharmony_ci
142113498266Sopenharmony_ci        /*
142213498266Sopenharmony_ci         * now check the left part of the path with the cookies path
142313498266Sopenharmony_ci         * requirement
142413498266Sopenharmony_ci         */
142513498266Sopenharmony_ci        if(!co->spath || pathmatch(co->spath, path) ) {
142613498266Sopenharmony_ci
142713498266Sopenharmony_ci          /*
142813498266Sopenharmony_ci           * and now, we know this is a match and we should create an
142913498266Sopenharmony_ci           * entry for the return-linked-list
143013498266Sopenharmony_ci           */
143113498266Sopenharmony_ci
143213498266Sopenharmony_ci          newco = dup_cookie(co);
143313498266Sopenharmony_ci          if(newco) {
143413498266Sopenharmony_ci            /* then modify our next */
143513498266Sopenharmony_ci            newco->next = mainco;
143613498266Sopenharmony_ci
143713498266Sopenharmony_ci            /* point the main to us */
143813498266Sopenharmony_ci            mainco = newco;
143913498266Sopenharmony_ci
144013498266Sopenharmony_ci            matches++;
144113498266Sopenharmony_ci            if(matches >= MAX_COOKIE_SEND_AMOUNT) {
144213498266Sopenharmony_ci              infof(data, "Included max number of cookies (%zu) in request!",
144313498266Sopenharmony_ci                    matches);
144413498266Sopenharmony_ci              break;
144513498266Sopenharmony_ci            }
144613498266Sopenharmony_ci          }
144713498266Sopenharmony_ci          else
144813498266Sopenharmony_ci            goto fail;
144913498266Sopenharmony_ci        }
145013498266Sopenharmony_ci      }
145113498266Sopenharmony_ci    }
145213498266Sopenharmony_ci    co = co->next;
145313498266Sopenharmony_ci  }
145413498266Sopenharmony_ci
145513498266Sopenharmony_ci  if(matches) {
145613498266Sopenharmony_ci    /*
145713498266Sopenharmony_ci     * Now we need to make sure that if there is a name appearing more than
145813498266Sopenharmony_ci     * once, the longest specified path version comes first. To make this
145913498266Sopenharmony_ci     * the swiftest way, we just sort them all based on path length.
146013498266Sopenharmony_ci     */
146113498266Sopenharmony_ci    struct Cookie **array;
146213498266Sopenharmony_ci    size_t i;
146313498266Sopenharmony_ci
146413498266Sopenharmony_ci    /* alloc an array and store all cookie pointers */
146513498266Sopenharmony_ci    array = malloc(sizeof(struct Cookie *) * matches);
146613498266Sopenharmony_ci    if(!array)
146713498266Sopenharmony_ci      goto fail;
146813498266Sopenharmony_ci
146913498266Sopenharmony_ci    co = mainco;
147013498266Sopenharmony_ci
147113498266Sopenharmony_ci    for(i = 0; co; co = co->next)
147213498266Sopenharmony_ci      array[i++] = co;
147313498266Sopenharmony_ci
147413498266Sopenharmony_ci    /* now sort the cookie pointers in path length order */
147513498266Sopenharmony_ci    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
147613498266Sopenharmony_ci
147713498266Sopenharmony_ci    /* remake the linked list order according to the new order */
147813498266Sopenharmony_ci
147913498266Sopenharmony_ci    mainco = array[0]; /* start here */
148013498266Sopenharmony_ci    for(i = 0; i<matches-1; i++)
148113498266Sopenharmony_ci      array[i]->next = array[i + 1];
148213498266Sopenharmony_ci    array[matches-1]->next = NULL; /* terminate the list */
148313498266Sopenharmony_ci
148413498266Sopenharmony_ci    free(array); /* remove the temporary data again */
148513498266Sopenharmony_ci  }
148613498266Sopenharmony_ci
148713498266Sopenharmony_ci  return mainco; /* return the new list */
148813498266Sopenharmony_ci
148913498266Sopenharmony_cifail:
149013498266Sopenharmony_ci  /* failure, clear up the allocated chain and return NULL */
149113498266Sopenharmony_ci  Curl_cookie_freelist(mainco);
149213498266Sopenharmony_ci  return NULL;
149313498266Sopenharmony_ci}
149413498266Sopenharmony_ci
149513498266Sopenharmony_ci/*
149613498266Sopenharmony_ci * Curl_cookie_clearall
149713498266Sopenharmony_ci *
149813498266Sopenharmony_ci * Clear all existing cookies and reset the counter.
149913498266Sopenharmony_ci */
150013498266Sopenharmony_civoid Curl_cookie_clearall(struct CookieInfo *cookies)
150113498266Sopenharmony_ci{
150213498266Sopenharmony_ci  if(cookies) {
150313498266Sopenharmony_ci    unsigned int i;
150413498266Sopenharmony_ci    for(i = 0; i < COOKIE_HASH_SIZE; i++) {
150513498266Sopenharmony_ci      Curl_cookie_freelist(cookies->cookies[i]);
150613498266Sopenharmony_ci      cookies->cookies[i] = NULL;
150713498266Sopenharmony_ci    }
150813498266Sopenharmony_ci    cookies->numcookies = 0;
150913498266Sopenharmony_ci  }
151013498266Sopenharmony_ci}
151113498266Sopenharmony_ci
151213498266Sopenharmony_ci/*
151313498266Sopenharmony_ci * Curl_cookie_freelist
151413498266Sopenharmony_ci *
151513498266Sopenharmony_ci * Free a list of cookies previously returned by Curl_cookie_getlist();
151613498266Sopenharmony_ci */
151713498266Sopenharmony_civoid Curl_cookie_freelist(struct Cookie *co)
151813498266Sopenharmony_ci{
151913498266Sopenharmony_ci  struct Cookie *next;
152013498266Sopenharmony_ci  while(co) {
152113498266Sopenharmony_ci    next = co->next;
152213498266Sopenharmony_ci    freecookie(co);
152313498266Sopenharmony_ci    co = next;
152413498266Sopenharmony_ci  }
152513498266Sopenharmony_ci}
152613498266Sopenharmony_ci
152713498266Sopenharmony_ci/*
152813498266Sopenharmony_ci * Curl_cookie_clearsess
152913498266Sopenharmony_ci *
153013498266Sopenharmony_ci * Free all session cookies in the cookies list.
153113498266Sopenharmony_ci */
153213498266Sopenharmony_civoid Curl_cookie_clearsess(struct CookieInfo *cookies)
153313498266Sopenharmony_ci{
153413498266Sopenharmony_ci  struct Cookie *first, *curr, *next, *prev = NULL;
153513498266Sopenharmony_ci  unsigned int i;
153613498266Sopenharmony_ci
153713498266Sopenharmony_ci  if(!cookies)
153813498266Sopenharmony_ci    return;
153913498266Sopenharmony_ci
154013498266Sopenharmony_ci  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
154113498266Sopenharmony_ci    if(!cookies->cookies[i])
154213498266Sopenharmony_ci      continue;
154313498266Sopenharmony_ci
154413498266Sopenharmony_ci    first = curr = prev = cookies->cookies[i];
154513498266Sopenharmony_ci
154613498266Sopenharmony_ci    for(; curr; curr = next) {
154713498266Sopenharmony_ci      next = curr->next;
154813498266Sopenharmony_ci      if(!curr->expires) {
154913498266Sopenharmony_ci        if(first == curr)
155013498266Sopenharmony_ci          first = next;
155113498266Sopenharmony_ci
155213498266Sopenharmony_ci        if(prev == curr)
155313498266Sopenharmony_ci          prev = next;
155413498266Sopenharmony_ci        else
155513498266Sopenharmony_ci          prev->next = next;
155613498266Sopenharmony_ci
155713498266Sopenharmony_ci        freecookie(curr);
155813498266Sopenharmony_ci        cookies->numcookies--;
155913498266Sopenharmony_ci      }
156013498266Sopenharmony_ci      else
156113498266Sopenharmony_ci        prev = curr;
156213498266Sopenharmony_ci    }
156313498266Sopenharmony_ci
156413498266Sopenharmony_ci    cookies->cookies[i] = first;
156513498266Sopenharmony_ci  }
156613498266Sopenharmony_ci}
156713498266Sopenharmony_ci
156813498266Sopenharmony_ci/*
156913498266Sopenharmony_ci * Curl_cookie_cleanup()
157013498266Sopenharmony_ci *
157113498266Sopenharmony_ci * Free a "cookie object" previous created with Curl_cookie_init().
157213498266Sopenharmony_ci */
157313498266Sopenharmony_civoid Curl_cookie_cleanup(struct CookieInfo *c)
157413498266Sopenharmony_ci{
157513498266Sopenharmony_ci  if(c) {
157613498266Sopenharmony_ci    unsigned int i;
157713498266Sopenharmony_ci    for(i = 0; i < COOKIE_HASH_SIZE; i++)
157813498266Sopenharmony_ci      Curl_cookie_freelist(c->cookies[i]);
157913498266Sopenharmony_ci    free(c); /* free the base struct as well */
158013498266Sopenharmony_ci  }
158113498266Sopenharmony_ci}
158213498266Sopenharmony_ci
158313498266Sopenharmony_ci/*
158413498266Sopenharmony_ci * get_netscape_format()
158513498266Sopenharmony_ci *
158613498266Sopenharmony_ci * Formats a string for Netscape output file, w/o a newline at the end.
158713498266Sopenharmony_ci * Function returns a char * to a formatted line. The caller is responsible
158813498266Sopenharmony_ci * for freeing the returned pointer.
158913498266Sopenharmony_ci */
159013498266Sopenharmony_cistatic char *get_netscape_format(const struct Cookie *co)
159113498266Sopenharmony_ci{
159213498266Sopenharmony_ci  return aprintf(
159313498266Sopenharmony_ci    "%s"     /* httponly preamble */
159413498266Sopenharmony_ci    "%s%s\t" /* domain */
159513498266Sopenharmony_ci    "%s\t"   /* tailmatch */
159613498266Sopenharmony_ci    "%s\t"   /* path */
159713498266Sopenharmony_ci    "%s\t"   /* secure */
159813498266Sopenharmony_ci    "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
159913498266Sopenharmony_ci    "%s\t"   /* name */
160013498266Sopenharmony_ci    "%s",    /* value */
160113498266Sopenharmony_ci    co->httponly?"#HttpOnly_":"",
160213498266Sopenharmony_ci    /*
160313498266Sopenharmony_ci     * Make sure all domains are prefixed with a dot if they allow
160413498266Sopenharmony_ci     * tailmatching. This is Mozilla-style.
160513498266Sopenharmony_ci     */
160613498266Sopenharmony_ci    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
160713498266Sopenharmony_ci    co->domain?co->domain:"unknown",
160813498266Sopenharmony_ci    co->tailmatch?"TRUE":"FALSE",
160913498266Sopenharmony_ci    co->path?co->path:"/",
161013498266Sopenharmony_ci    co->secure?"TRUE":"FALSE",
161113498266Sopenharmony_ci    co->expires,
161213498266Sopenharmony_ci    co->name,
161313498266Sopenharmony_ci    co->value?co->value:"");
161413498266Sopenharmony_ci}
161513498266Sopenharmony_ci
161613498266Sopenharmony_ci/*
161713498266Sopenharmony_ci * cookie_output()
161813498266Sopenharmony_ci *
161913498266Sopenharmony_ci * Writes all internally known cookies to the specified file. Specify
162013498266Sopenharmony_ci * "-" as file name to write to stdout.
162113498266Sopenharmony_ci *
162213498266Sopenharmony_ci * The function returns non-zero on write failure.
162313498266Sopenharmony_ci */
162413498266Sopenharmony_cistatic CURLcode cookie_output(struct Curl_easy *data,
162513498266Sopenharmony_ci                              struct CookieInfo *c, const char *filename)
162613498266Sopenharmony_ci{
162713498266Sopenharmony_ci  struct Cookie *co;
162813498266Sopenharmony_ci  FILE *out = NULL;
162913498266Sopenharmony_ci  bool use_stdout = FALSE;
163013498266Sopenharmony_ci  char *tempstore = NULL;
163113498266Sopenharmony_ci  CURLcode error = CURLE_OK;
163213498266Sopenharmony_ci
163313498266Sopenharmony_ci  if(!c)
163413498266Sopenharmony_ci    /* no cookie engine alive */
163513498266Sopenharmony_ci    return CURLE_OK;
163613498266Sopenharmony_ci
163713498266Sopenharmony_ci  /* at first, remove expired cookies */
163813498266Sopenharmony_ci  remove_expired(c);
163913498266Sopenharmony_ci
164013498266Sopenharmony_ci  if(!strcmp("-", filename)) {
164113498266Sopenharmony_ci    /* use stdout */
164213498266Sopenharmony_ci    out = stdout;
164313498266Sopenharmony_ci    use_stdout = TRUE;
164413498266Sopenharmony_ci  }
164513498266Sopenharmony_ci  else {
164613498266Sopenharmony_ci    error = Curl_fopen(data, filename, &out, &tempstore);
164713498266Sopenharmony_ci    if(error)
164813498266Sopenharmony_ci      goto error;
164913498266Sopenharmony_ci  }
165013498266Sopenharmony_ci
165113498266Sopenharmony_ci  fputs("# Netscape HTTP Cookie File\n"
165213498266Sopenharmony_ci        "# https://curl.se/docs/http-cookies.html\n"
165313498266Sopenharmony_ci        "# This file was generated by libcurl! Edit at your own risk.\n\n",
165413498266Sopenharmony_ci        out);
165513498266Sopenharmony_ci
165613498266Sopenharmony_ci  if(c->numcookies) {
165713498266Sopenharmony_ci    unsigned int i;
165813498266Sopenharmony_ci    size_t nvalid = 0;
165913498266Sopenharmony_ci    struct Cookie **array;
166013498266Sopenharmony_ci
166113498266Sopenharmony_ci    array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
166213498266Sopenharmony_ci    if(!array) {
166313498266Sopenharmony_ci      error = CURLE_OUT_OF_MEMORY;
166413498266Sopenharmony_ci      goto error;
166513498266Sopenharmony_ci    }
166613498266Sopenharmony_ci
166713498266Sopenharmony_ci    /* only sort the cookies with a domain property */
166813498266Sopenharmony_ci    for(i = 0; i < COOKIE_HASH_SIZE; i++) {
166913498266Sopenharmony_ci      for(co = c->cookies[i]; co; co = co->next) {
167013498266Sopenharmony_ci        if(!co->domain)
167113498266Sopenharmony_ci          continue;
167213498266Sopenharmony_ci        array[nvalid++] = co;
167313498266Sopenharmony_ci      }
167413498266Sopenharmony_ci    }
167513498266Sopenharmony_ci
167613498266Sopenharmony_ci    qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
167713498266Sopenharmony_ci
167813498266Sopenharmony_ci    for(i = 0; i < nvalid; i++) {
167913498266Sopenharmony_ci      char *format_ptr = get_netscape_format(array[i]);
168013498266Sopenharmony_ci      if(!format_ptr) {
168113498266Sopenharmony_ci        free(array);
168213498266Sopenharmony_ci        error = CURLE_OUT_OF_MEMORY;
168313498266Sopenharmony_ci        goto error;
168413498266Sopenharmony_ci      }
168513498266Sopenharmony_ci      fprintf(out, "%s\n", format_ptr);
168613498266Sopenharmony_ci      free(format_ptr);
168713498266Sopenharmony_ci    }
168813498266Sopenharmony_ci
168913498266Sopenharmony_ci    free(array);
169013498266Sopenharmony_ci  }
169113498266Sopenharmony_ci
169213498266Sopenharmony_ci  if(!use_stdout) {
169313498266Sopenharmony_ci    fclose(out);
169413498266Sopenharmony_ci    out = NULL;
169513498266Sopenharmony_ci    if(tempstore && Curl_rename(tempstore, filename)) {
169613498266Sopenharmony_ci      unlink(tempstore);
169713498266Sopenharmony_ci      error = CURLE_WRITE_ERROR;
169813498266Sopenharmony_ci      goto error;
169913498266Sopenharmony_ci    }
170013498266Sopenharmony_ci  }
170113498266Sopenharmony_ci
170213498266Sopenharmony_ci  /*
170313498266Sopenharmony_ci   * If we reach here we have successfully written a cookie file so there is
170413498266Sopenharmony_ci   * no need to inspect the error, any error case should have jumped into the
170513498266Sopenharmony_ci   * error block below.
170613498266Sopenharmony_ci   */
170713498266Sopenharmony_ci  free(tempstore);
170813498266Sopenharmony_ci  return CURLE_OK;
170913498266Sopenharmony_ci
171013498266Sopenharmony_cierror:
171113498266Sopenharmony_ci  if(out && !use_stdout)
171213498266Sopenharmony_ci    fclose(out);
171313498266Sopenharmony_ci  free(tempstore);
171413498266Sopenharmony_ci  return error;
171513498266Sopenharmony_ci}
171613498266Sopenharmony_ci
171713498266Sopenharmony_cistatic struct curl_slist *cookie_list(struct Curl_easy *data)
171813498266Sopenharmony_ci{
171913498266Sopenharmony_ci  struct curl_slist *list = NULL;
172013498266Sopenharmony_ci  struct curl_slist *beg;
172113498266Sopenharmony_ci  struct Cookie *c;
172213498266Sopenharmony_ci  char *line;
172313498266Sopenharmony_ci  unsigned int i;
172413498266Sopenharmony_ci
172513498266Sopenharmony_ci  if(!data->cookies || (data->cookies->numcookies == 0))
172613498266Sopenharmony_ci    return NULL;
172713498266Sopenharmony_ci
172813498266Sopenharmony_ci  for(i = 0; i < COOKIE_HASH_SIZE; i++) {
172913498266Sopenharmony_ci    for(c = data->cookies->cookies[i]; c; c = c->next) {
173013498266Sopenharmony_ci      if(!c->domain)
173113498266Sopenharmony_ci        continue;
173213498266Sopenharmony_ci      line = get_netscape_format(c);
173313498266Sopenharmony_ci      if(!line) {
173413498266Sopenharmony_ci        curl_slist_free_all(list);
173513498266Sopenharmony_ci        return NULL;
173613498266Sopenharmony_ci      }
173713498266Sopenharmony_ci      beg = Curl_slist_append_nodup(list, line);
173813498266Sopenharmony_ci      if(!beg) {
173913498266Sopenharmony_ci        free(line);
174013498266Sopenharmony_ci        curl_slist_free_all(list);
174113498266Sopenharmony_ci        return NULL;
174213498266Sopenharmony_ci      }
174313498266Sopenharmony_ci      list = beg;
174413498266Sopenharmony_ci    }
174513498266Sopenharmony_ci  }
174613498266Sopenharmony_ci
174713498266Sopenharmony_ci  return list;
174813498266Sopenharmony_ci}
174913498266Sopenharmony_ci
175013498266Sopenharmony_cistruct curl_slist *Curl_cookie_list(struct Curl_easy *data)
175113498266Sopenharmony_ci{
175213498266Sopenharmony_ci  struct curl_slist *list;
175313498266Sopenharmony_ci  Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
175413498266Sopenharmony_ci  list = cookie_list(data);
175513498266Sopenharmony_ci  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
175613498266Sopenharmony_ci  return list;
175713498266Sopenharmony_ci}
175813498266Sopenharmony_ci
175913498266Sopenharmony_civoid Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
176013498266Sopenharmony_ci{
176113498266Sopenharmony_ci  CURLcode res;
176213498266Sopenharmony_ci
176313498266Sopenharmony_ci  if(data->set.str[STRING_COOKIEJAR]) {
176413498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
176513498266Sopenharmony_ci
176613498266Sopenharmony_ci    /* if we have a destination file for all the cookies to get dumped to */
176713498266Sopenharmony_ci    res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]);
176813498266Sopenharmony_ci    if(res)
176913498266Sopenharmony_ci      infof(data, "WARNING: failed to save cookies in %s: %s",
177013498266Sopenharmony_ci            data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res));
177113498266Sopenharmony_ci  }
177213498266Sopenharmony_ci  else {
177313498266Sopenharmony_ci    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
177413498266Sopenharmony_ci  }
177513498266Sopenharmony_ci
177613498266Sopenharmony_ci  if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
177713498266Sopenharmony_ci    Curl_cookie_cleanup(data->cookies);
177813498266Sopenharmony_ci    data->cookies = NULL;
177913498266Sopenharmony_ci  }
178013498266Sopenharmony_ci  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
178113498266Sopenharmony_ci}
178213498266Sopenharmony_ci
178313498266Sopenharmony_ci#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
1784