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


rc_tagdetect_client
Author(s): Monika Florek-Jasinska , Raphael Schaller
autogenerated on Sat Feb 13 2021 03:42:08