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