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