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