1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 ***************************************************************************/
26 RECEIVING COOKIE INFORMATION
27 ============================
29 struct CookieInfo *cookie_init(char *file);
31 Inits a cookie struct to store data in a local file. This is always
32 called before any cookies are set.
34 int cookies_set(struct CookieInfo *cookie, char *cookie_line);
36 The 'cookie_line' parameter is a full "Set-cookie:" line as
37 received from a server.
39 The function need to replace previously stored lines that this new
42 It may remove lines that are expired.
44 It should return an indication of success/error.
47 SENDING COOKIE INFORMATION
48 ==========================
50 struct Cookies *cookie_getlist(struct CookieInfo *cookie,
51 char *host, char *path, bool secure);
53 For a given host and path, return a linked list of cookies that
54 the client should send to the server if used now. The secure
55 boolean informs the cookie if a secure connection is achieved or
58 It shall only return cookies that haven't expired.
61 Example set of cookies:
63 Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
64 Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
65 domain=.fidelity.com; path=/ftgw; secure
66 Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
67 domain=.fidelity.com; path=/; secure
68 Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
69 domain=.fidelity.com; path=/; secure
70 Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
71 domain=.fidelity.com; path=/; secure
72 Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
73 domain=.fidelity.com; path=/; secure
75 Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
76 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
82 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
87 #define _MPRINTF_REPLACE
88 #include <curl/mprintf.h>
95 #include "curl_memory.h"
97 #include "strtoofft.h"
99 #include "curl_memrchr.h"
101 /* The last #include file should be: */
102 #include "memdebug.h"
105 static void freecookie(struct Cookie *co)
125 static bool tailmatch(const char *little, const char *bigone)
127 size_t littlelen = strlen(little);
128 size_t biglen = strlen(bigone);
130 if(littlelen > biglen)
133 return (bool)Curl_raw_equal(little, bigone+biglen-littlelen);
137 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
139 void Curl_cookie_loadfiles(struct SessionHandle *data)
141 struct curl_slist *list = data->change.cookielist;
143 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
145 data->cookies = Curl_cookie_init(data,
148 data->set.cookiesession);
151 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
152 curl_slist_free_all(data->change.cookielist); /* clean up list */
153 data->change.cookielist = NULL; /* don't do this again! */
158 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
159 * that will be freed before the allocated string is stored there.
161 * It is meant to easily replace strdup()
163 static void strstore(char **str, const char *newstr)
167 *str = strdup(newstr);
171 /****************************************************************************
175 * Add a single cookie line to the cookie keeping object.
177 ***************************************************************************/
180 Curl_cookie_add(struct SessionHandle *data,
181 /* The 'data' pointer here may be NULL at times, and thus
182 must only be used very carefully for things that can deal
183 with data being NULL. Such as infof() and similar */
185 struct CookieInfo *c,
186 bool httpheader, /* TRUE if HTTP header-style line */
187 char *lineptr, /* first character of the line */
188 const char *domain, /* default domain */
189 const char *path) /* full path used when this cookie is set,
190 used to get default path for the cookie
193 struct Cookie *clist;
196 struct Cookie *lastc=NULL;
197 time_t now = time(NULL);
198 bool replace_old = FALSE;
199 bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
201 #ifdef CURL_DISABLE_VERBOSE_STRINGS
205 /* First, alloc and init a new struct for it */
206 co = calloc(1, sizeof(struct Cookie));
208 return NULL; /* bail out if we're this low on memory */
211 /* This line was read off a HTTP-header */
217 what = malloc(MAX_COOKIE_LINE);
223 semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
225 while(*lineptr && ISBLANK(*lineptr))
230 /* we have a <what>=<this> pair or a 'secure' word here */
231 sep = strchr(ptr, '=');
232 if(sep && (!semiptr || (semiptr>sep)) ) {
234 * There is a = sign and if there was a semicolon too, which make sure
235 * that the semicolon comes _after_ the equal sign.
238 name[0]=what[0]=0; /* init the buffers */
239 if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
240 MAX_COOKIE_LINE_TXT "[^;\r\n]",
242 /* this is a <name>=<what> pair. We use strstore() below to properly
243 deal with received cookie headers that have the same string
244 property set more than once, and then we use the last one. */
248 /* Strip off trailing whitespace from the 'what' */
249 size_t len=strlen(what);
250 while(len && ISBLANK(what[len-1])) {
255 /* Skip leading whitespace from the 'what' */
257 while(*whatptr && ISBLANK(*whatptr)) {
261 if(Curl_raw_equal("path", name)) {
262 strstore(&co->path, whatptr);
264 badcookie = TRUE; /* out of memory bad */
268 else if(Curl_raw_equal("domain", name)) {
269 /* note that this name may or may not have a preceding dot, but
270 we don't care about that, we treat the names the same anyway */
272 const char *domptr=whatptr;
276 /* Count the dots, we need to make sure that there are enough
279 if('.' == whatptr[0])
280 /* don't count the initial dot, assume it */
284 nextptr = strchr(domptr, '.');
286 if(domptr != nextptr)
292 /* The original Netscape cookie spec defined that this domain name
293 MUST have three dots (or two if one of the seven holy TLDs),
294 but it seems that these kinds of cookies are in use "out there"
295 so we cannot be that strict. I've therefore lowered the check
296 to not allow less than two dots. */
299 /* Received and skipped a cookie with a domain using too few
301 badcookie=TRUE; /* mark this as a bad cookie */
302 infof(data, "skipped cookie with illegal dotcount domain: %s\n",
306 /* Now, we make sure that our host is within the given domain,
307 or the given domain is not valid and thus cannot be set. */
309 if('.' == whatptr[0])
310 whatptr++; /* ignore preceding dot */
312 if(!domain || tailmatch(whatptr, domain)) {
313 const char *tailptr=whatptr;
314 if(tailptr[0] == '.')
316 strstore(&co->domain, tailptr); /* don't prefix w/dots
322 co->tailmatch=TRUE; /* we always do that if the domain name was
326 /* we did not get a tailmatch and then the attempted set domain
327 is not a domain to which the current host belongs. Mark as
330 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
335 else if(Curl_raw_equal("version", name)) {
336 strstore(&co->version, whatptr);
342 else if(Curl_raw_equal("max-age", name)) {
343 /* Defined in RFC2109:
345 Optional. The Max-Age attribute defines the lifetime of the
346 cookie, in seconds. The delta-seconds value is a decimal non-
347 negative integer. After delta-seconds seconds elapse, the
348 client should discard the cookie. A value of zero means the
349 cookie should be discarded immediately.
352 strstore(&co->maxage, whatptr);
358 strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
361 else if(Curl_raw_equal("expires", name)) {
362 strstore(&co->expirestr, whatptr);
367 /* Note that if the date couldn't get parsed for whatever reason,
368 the cookie will be treated as a session cookie */
369 co->expires = curl_getdate(what, &now);
371 /* Session cookies have expires set to 0 so if we get that back
372 from the date parser let's add a second to make it a
373 non-session cookie */
376 else if(co->expires < 0)
380 co->name = strdup(name);
381 co->value = strdup(whatptr);
382 if(!co->name || !co->value) {
388 else this is the second (or more) name we don't know
392 /* this is an "illegal" <what>=<this> pair */
396 if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
398 if(Curl_raw_equal("secure", what)) {
401 else if(Curl_raw_equal("httponly", what)) {
405 unsupported keyword without assign! */
409 if(!semiptr || !*semiptr) {
410 /* we already know there are no more cookies */
416 while(*ptr && ISBLANK(*ptr))
418 semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
421 /* There are no more semicolons, but there's a final name=value pair
423 semiptr=strchr(ptr, '\0');
426 if(!badcookie && !co->domain) {
428 /* no domain was given in the header line, set the default */
429 co->domain=strdup(domain);
435 if(!badcookie && !co->path && path) {
436 /* No path was given in the header line, set the default.
437 Note that the passed-in path to this function MAY have a '?' and
438 following part that MUST not be stored as part of the path. */
439 char *queryp = strchr(path, '?');
441 /* queryp is where the interesting part of the path ends, so now we
442 want to the find the last */
445 endslash = strrchr(path, '/');
447 endslash = memrchr(path, '/', (size_t)(queryp - path));
449 size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
450 co->path=malloc(pathlen+1); /* one extra for the zero byte */
452 memcpy(co->path, path, pathlen);
453 co->path[pathlen]=0; /* zero terminate */
462 if(badcookie || !co->name) {
463 /* we didn't get a cookie name or a bad one,
464 this is an illegal line, bail out */
471 /* This line is NOT a HTTP header style line, we do offer support for
472 reading the odd netscape cookies-file format here */
478 /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
479 marked with httpOnly after the domain name are not accessible
480 from javascripts, but since curl does not operate at javascript
481 level, we include them anyway. In Firefox's cookie files, these
482 lines are preceded with #HttpOnly_ and then everything is
483 as usual, so we skip 10 characters of the line..
485 if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
490 if(lineptr[0]=='#') {
491 /* don't even try the comments */
495 /* strip off the possible end-of-line characters */
496 ptr=strchr(lineptr, '\r');
498 *ptr=0; /* clear it */
499 ptr=strchr(lineptr, '\n');
501 *ptr=0; /* clear it */
503 firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
505 /* Here's a quick check to eliminate normal HTTP-headers from this */
506 if(!firstptr || strchr(firstptr, ':')) {
511 /* Now loop through the fields and init the struct we already have
513 for(ptr=firstptr, fields=0; ptr && !badcookie;
514 ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
517 if(ptr[0]=='.') /* skip preceding dots */
519 co->domain = strdup(ptr);
524 /* This field got its explanation on the 23rd of May 2001 by
527 flag: A TRUE/FALSE value indicating if all machines within a given
528 domain can access the variable. This value is set automatically by
529 the browser, depending on the value you set for the domain.
531 As far as I can see, it is set to true when the cookie says
532 .domain.com and to false when the domain is complete www.domain.com
534 co->tailmatch=(bool)Curl_raw_equal(ptr, "TRUE");
537 /* It turns out, that sometimes the file format allows the path
538 field to remain not filled in, we try to detect this and work
539 around it! Andrés GarcÃa made us aware of this... */
540 if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
541 /* only if the path doesn't look like a boolean option! */
542 co->path = strdup(ptr);
547 /* this doesn't look like a path, make one up! */
548 co->path = strdup("/");
551 fields++; /* add a field and fall down to secure */
554 co->secure = (bool)Curl_raw_equal(ptr, "TRUE");
557 co->expires = curlx_strtoofft(ptr, NULL, 10);
560 co->name = strdup(ptr);
565 co->value = strdup(ptr);
572 /* we got a cookie with blank contents, fix it */
573 co->value = strdup("");
580 if(!badcookie && (7 != fields))
581 /* we did not find the sufficient number of fields */
591 if(!c->running && /* read from a file */
592 c->newsession && /* clean session cookies */
593 !co->expires) { /* this is a session cookie since it doesn't expire! */
598 co->livecookie = c->running;
600 /* now, we have parsed the incoming line, we must now check if this
601 superceeds an already existing cookie, which it may if the previous have
602 the same domain and path as this */
607 if(Curl_raw_equal(clist->name, co->name)) {
608 /* the names are identical */
610 if(clist->domain && co->domain) {
611 if(Curl_raw_equal(clist->domain, co->domain))
612 /* The domains are identical */
615 else if(!clist->domain && !co->domain)
619 /* the domains were identical */
621 if(clist->path && co->path) {
622 if(Curl_raw_equal(clist->path, co->path)) {
628 else if(!clist->path && !co->path)
635 if(replace_old && !co->livecookie && clist->livecookie) {
636 /* Both cookies matched fine, except that the already present
637 cookie is "live", which means it was set from a header, while
638 the new one isn't "live" and thus only read from a file. We let
639 live cookies stay alive */
641 /* Free the newcomer and get out of here! */
647 co->next = clist->next; /* get the next-pointer first */
649 /* then free all the old pointers */
658 free(clist->expirestr);
661 free(clist->version);
665 *clist = *co; /* then store all the new data */
667 free(co); /* free the newly alloced memory */
668 co = clist; /* point to the previous struct instead */
670 /* We have replaced a cookie, now skip the rest of the list but
671 make sure the 'lastc' pointer is properly set */
684 /* Only show this when NOT reading the cookies from a file */
685 infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
686 "expire %" FORMAT_OFF_T "\n",
687 replace_old?"Replaced":"Added", co->name, co->value,
688 co->domain, co->path, co->expires);
691 /* then make the last item point on this new one */
698 c->numcookies++; /* one more cookie in the jar */
702 /*****************************************************************************
706 * Inits a cookie struct to read data from a local file. This is always
707 * called before any cookies are set. File may be NULL.
709 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
711 ****************************************************************************/
712 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
714 struct CookieInfo *inc,
717 struct CookieInfo *c;
722 /* we didn't get a struct, create one */
723 c = calloc(1, sizeof(struct CookieInfo));
725 return NULL; /* failed to get memory */
726 c->filename = strdup(file?file:"none"); /* copy the name just in case */
729 /* we got an already existing one, use that */
732 c->running = FALSE; /* this is not running, this is init */
734 if(file && strequal(file, "-")) {
738 else if(file && !*file) {
739 /* points to a "" string */
743 fp = file?fopen(file, "r"):NULL;
745 c->newsession = newsession; /* new session? */
751 char *line = malloc(MAX_COOKIE_LINE);
753 while(fgets(line, MAX_COOKIE_LINE, fp)) {
754 if(checkprefix("Set-Cookie:", line)) {
755 /* This is a cookie line, get it! */
763 while(*lineptr && ISBLANK(*lineptr))
766 Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
768 free(line); /* free the line buffer */
774 c->running = TRUE; /* now, we're running */
779 /* sort this so that the longest path gets before the shorter path */
780 static int cookie_sort(const void *p1, const void *p2)
782 struct Cookie *c1 = *(struct Cookie **)p1;
783 struct Cookie *c2 = *(struct Cookie **)p2;
785 size_t l1 = c1->path?strlen(c1->path):0;
786 size_t l2 = c2->path?strlen(c2->path):0;
788 return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
791 /*****************************************************************************
793 * Curl_cookie_getlist()
795 * For a given host and path, return a linked list of cookies that the
796 * client should send to the server if used now. The secure boolean informs
797 * the cookie if a secure connection is achieved or not.
799 * It shall only return cookies that haven't expired.
801 ****************************************************************************/
803 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
804 const char *host, const char *path,
807 struct Cookie *newco;
809 time_t now = time(NULL);
810 struct Cookie *mainco=NULL;
813 if(!c || !c->cookies)
814 return NULL; /* no cookie struct or no cookies in the struct */
819 /* only process this cookie if it is not expired or had no expire
820 date AND that if the cookie requires we're secure we must only
821 continue if we are! */
822 if((!co->expires || (co->expires > now)) &&
823 (co->secure?secure:TRUE)) {
825 /* now check if the domain is correct */
827 (co->tailmatch && tailmatch(co->domain, host)) ||
828 (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
829 /* the right part of the host matches the domain stuff in the
832 /* now check the left part of the path with the cookies path
835 /* not using checkprefix() because matching should be
837 !strncmp(co->path, path, strlen(co->path)) ) {
839 /* and now, we know this is a match and we should create an
840 entry for the return-linked-list */
842 newco = malloc(sizeof(struct Cookie));
844 /* first, copy the whole source cookie: */
845 memcpy(newco, co, sizeof(struct Cookie));
847 /* then modify our next */
848 newco->next = mainco;
850 /* point the main to us */
857 /* failure, clear up the allocated chain and return NULL */
873 /* Now we need to make sure that if there is a name appearing more than
874 once, the longest specified path version comes first. To make this
875 the swiftest way, we just sort them all based on path length. */
876 struct Cookie **array;
879 /* alloc an array and store all cookie pointers */
880 array = malloc(sizeof(struct Cookie *) * matches);
886 for(i=0; co; co = co->next)
889 /* now sort the cookie pointers in path lenth order */
890 qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
892 /* remake the linked list order according to the new order */
894 mainco = array[0]; /* start here */
895 for(i=0; i<matches-1; i++)
896 array[i]->next = array[i+1];
897 array[matches-1]->next = NULL; /* terminate the list */
899 free(array); /* remove the temporary data again */
902 return mainco; /* return the new list */
905 /*****************************************************************************
907 * Curl_cookie_clearall()
909 * Clear all existing cookies and reset the counter.
911 ****************************************************************************/
912 void Curl_cookie_clearall(struct CookieInfo *cookies)
915 Curl_cookie_freelist(cookies->cookies, TRUE);
916 cookies->cookies = NULL;
917 cookies->numcookies = 0;
921 /*****************************************************************************
923 * Curl_cookie_freelist()
925 * Free a list of cookies previously returned by Curl_cookie_getlist();
927 * The 'cookiestoo' argument tells this function whether to just free the
928 * list or actually also free all cookies within the list as well.
930 ****************************************************************************/
932 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
941 free(co); /* we only free the struct since the "members" are all just
942 pointed out in the main cookie list! */
949 /*****************************************************************************
951 * Curl_cookie_clearsess()
953 * Free all session cookies in the cookies list.
955 ****************************************************************************/
956 void Curl_cookie_clearsess(struct CookieInfo *cookies)
958 struct Cookie *first, *curr, *next, *prev = NULL;
960 if(!cookies || !cookies->cookies)
963 first = curr = prev = cookies->cookies;
965 for(; curr; curr = next) {
977 cookies->numcookies--;
983 cookies->cookies = first;
987 /*****************************************************************************
989 * Curl_cookie_cleanup()
991 * Free a "cookie object" previous created with cookie_init().
993 ****************************************************************************/
994 void Curl_cookie_cleanup(struct CookieInfo *c)
1008 free(c); /* free the base struct as well */
1012 /* get_netscape_format()
1014 * Formats a string for Netscape output file, w/o a newline at the end.
1016 * Function returns a char * to a formatted line. Has to be free()d
1018 static char *get_netscape_format(const struct Cookie *co)
1021 "%s" /* httponly preamble */
1022 "%s%s\t" /* domain */
1023 "%s\t" /* tailmatch */
1026 "%" FORMAT_OFF_T "\t" /* expires */
1029 co->httponly?"#HttpOnly_":"",
1030 /* Make sure all domains are prefixed with a dot if they allow
1031 tailmatching. This is Mozilla-style. */
1032 (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1033 co->domain?co->domain:"unknown",
1034 co->tailmatch?"TRUE":"FALSE",
1035 co->path?co->path:"/",
1036 co->secure?"TRUE":"FALSE",
1039 co->value?co->value:"");
1043 * Curl_cookie_output()
1045 * Writes all internally known cookies to the specified file. Specify
1046 * "-" as file name to write to stdout.
1048 * The function returns non-zero on write failure.
1050 int Curl_cookie_output(struct CookieInfo *c, const char *dumphere)
1054 bool use_stdout=FALSE;
1056 if((NULL == c) || (0 == c->numcookies))
1057 /* If there are no known cookies, we don't write or even create any
1061 if(strequal("-", dumphere)) {
1067 out = fopen(dumphere, "w");
1069 return 1; /* failure */
1075 fputs("# Netscape HTTP Cookie File\n"
1076 "# http://curl.haxx.se/rfc/cookie_spec.html\n"
1077 "# This file was generated by libcurl! Edit at your own risk.\n\n",
1082 format_ptr = get_netscape_format(co);
1083 if(format_ptr == NULL) {
1084 fprintf(out, "#\n# Fatal libcurl error\n");
1089 fprintf(out, "%s\n", format_ptr);
1101 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1103 struct curl_slist *list = NULL;
1104 struct curl_slist *beg;
1108 if((data->cookies == NULL) ||
1109 (data->cookies->numcookies == 0))
1112 c = data->cookies->cookies;
1116 /* fill the list with _all_ the cookies we know */
1117 line = get_netscape_format(c);
1119 curl_slist_free_all(beg);
1122 list = curl_slist_append(list, line);
1125 curl_slist_free_all(beg);
1128 else if(beg == NULL) {
1137 void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1139 if(data->set.str[STRING_COOKIEJAR]) {
1140 if(data->change.cookielist) {
1141 /* If there is a list of cookie files to read, do it first so that
1142 we have all the told files read before we write the new jar.
1143 Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1144 Curl_cookie_loadfiles(data);
1147 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1149 /* if we have a destination file for all the cookies to get dumped to */
1150 if(Curl_cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1151 infof(data, "WARNING: failed to save cookies in %s\n",
1152 data->set.str[STRING_COOKIEJAR]);
1155 if(cleanup && data->change.cookielist)
1156 /* since nothing is written, we can just free the list of cookie file
1158 curl_slist_free_all(data->change.cookielist); /* clean up list */
1159 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1162 if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1163 Curl_cookie_cleanup(data->cookies);
1165 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1168 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */