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