* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1998 - 2011, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
RECEIVING COOKIE INFORMATION
============================
-struct CookieInfo *cookie_init(char *file);
+struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
+ const char *file, struct CookieInfo *inc, bool newsession);
Inits a cookie struct to store data in a local file. This is always
called before any cookies are set.
-int cookies_set(struct CookieInfo *cookie, char *cookie_line);
+struct Cookie *Curl_cookie_add(struct SessionHandle *data,
+ struct CookieInfo *c, bool httpheader, char *lineptr,
+ const char *domain, const char *path);
- The 'cookie_line' parameter is a full "Set-cookie:" line as
+ The 'lineptr' parameter is a full "Set-cookie:" line as
received from a server.
The function need to replace previously stored lines that this new
SENDING COOKIE INFORMATION
==========================
-struct Cookies *cookie_getlist(struct CookieInfo *cookie,
- char *host, char *path, bool secure);
+struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
+ char *host, char *path, bool secure);
For a given host and path, return a linked list of cookies that
the client should send to the server if used now. The secure
****/
-#include "setup.h"
+#include "curl_setup.h"
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
#include "strequal.h"
#include "strtok.h"
#include "sendf.h"
+#include "slist.h"
#include "curl_memory.h"
#include "share.h"
#include "strtoofft.h"
#include "rawstr.h"
#include "curl_memrchr.h"
+#include "inet_pton.h"
/* The last #include file should be: */
#include "memdebug.h"
free(co->domain);
if(co->path)
free(co->path);
+ if(co->spath)
+ free(co->spath);
if(co->name)
free(co->name);
if(co->value)
free(co);
}
-static bool tailmatch(const char *little, const char *bigone)
+static bool tailmatch(const char *cooke_domain, const char *hostname)
+{
+ size_t cookie_domain_len = strlen(cooke_domain);
+ size_t hostname_len = strlen(hostname);
+
+ if(hostname_len < cookie_domain_len)
+ return FALSE;
+
+ if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len))
+ return FALSE;
+
+ /* A lead char of cookie_domain is not '.'.
+ RFC6265 4.1.2.3. The Domain Attribute says:
+ For example, if the value of the Domain attribute is
+ "example.com", the user agent will include the cookie in the Cookie
+ header when making HTTP requests to example.com, www.example.com, and
+ www.corp.example.com.
+ */
+ if(hostname_len == cookie_domain_len)
+ return TRUE;
+ if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * matching cookie path and url path
+ * RFC6265 5.1.4 Paths and Path-Match
+ */
+static bool pathmatch(const char* cookie_path, const char* request_uri)
{
- size_t littlelen = strlen(little);
- size_t biglen = strlen(bigone);
+ size_t cookie_path_len;
+ size_t uri_path_len;
+ char* uri_path = NULL;
+ char* pos;
+ bool ret = FALSE;
+
+ /* cookie_path must not have last '/' separator. ex: /sample */
+ cookie_path_len = strlen(cookie_path);
+ if(1 == cookie_path_len) {
+ /* cookie_path must be '/' */
+ return TRUE;
+ }
- if(littlelen > biglen)
+ uri_path = strdup(request_uri);
+ if(!uri_path)
return FALSE;
+ pos = strchr(uri_path, '?');
+ if(pos)
+ *pos = 0x0;
+
+ /* #-fragments are already cut off! */
+ if(0 == strlen(uri_path) || uri_path[0] != '/') {
+ free(uri_path);
+ uri_path = strdup("/");
+ if(!uri_path)
+ return FALSE;
+ }
+
+ /* here, RFC6265 5.1.4 says
+ 4. Output the characters of the uri-path from the first character up
+ to, but not including, the right-most %x2F ("/").
+ but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
+ without redirect.
+ Ignore this algorithm because /hoge is uri path for this case
+ (uri path is not /).
+ */
+
+ uri_path_len = strlen(uri_path);
+
+ if(uri_path_len < cookie_path_len) {
+ ret = FALSE;
+ goto pathmatched;
+ }
+
+ /* not using checkprefix() because matching should be case-sensitive */
+ if(strncmp(cookie_path, uri_path, cookie_path_len)) {
+ ret = FALSE;
+ goto pathmatched;
+ }
+
+ /* The cookie-path and the uri-path are identical. */
+ if(cookie_path_len == uri_path_len) {
+ ret = TRUE;
+ goto pathmatched;
+ }
+
+ /* here, cookie_path_len < url_path_len */
+ if(uri_path[cookie_path_len] == '/') {
+ ret = TRUE;
+ goto pathmatched;
+ }
+
+ ret = FALSE;
- return (bool)Curl_raw_equal(little, bigone+biglen-littlelen);
+pathmatched:
+ free(uri_path);
+ return ret;
+}
+
+/*
+ * cookie path sanitize
+ */
+static char *sanitize_cookie_path(const char *cookie_path)
+{
+ size_t len;
+ char *new_path = strdup(cookie_path);
+ if(!new_path)
+ return NULL;
+
+ /* some stupid site sends path attribute with '"'. */
+ len = strlen(new_path);
+ if(new_path[0] == '\"') {
+ memmove((void *)new_path, (const void *)(new_path + 1), len);
+ len--;
+ }
+ if(len && (new_path[len - 1] == '\"')) {
+ new_path[len - 1] = 0x0;
+ len--;
+ }
+
+ /* RFC6265 5.2.4 The Path Attribute */
+ if(new_path[0] != '/') {
+ /* Let cookie-path be the default-path. */
+ free(new_path);
+ new_path = strdup("/");
+ return new_path;
+ }
+
+ /* convert /hoge/ to /hoge */
+ if(len && new_path[len - 1] == '/') {
+ new_path[len - 1] = 0x0;
+ }
+
+ return new_path;
}
/*
* Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
+ *
+ * NOTE: OOM or cookie parsing failures are ignored.
*/
void Curl_cookie_loadfiles(struct SessionHandle *data)
{
if(list) {
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
while(list) {
- data->cookies = Curl_cookie_init(data,
- list->data,
- data->cookies,
- data->set.cookiesession);
+ struct CookieInfo *newcookies = Curl_cookie_init(data,
+ list->data,
+ data->cookies,
+ data->set.cookiesession);
+ if(!newcookies)
+ /* Failure may be due to OOM or a bad cookie; both are ignored
+ * but only the first should be
+ */
+ infof(data, "ignoring failed cookie_init for %s\n", list->data);
+ else
+ data->cookies = newcookies;
list = list->next;
}
- Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
curl_slist_free_all(data->change.cookielist); /* clean up list */
data->change.cookielist = NULL; /* don't do this again! */
+ Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
}
}
*str = strdup(newstr);
}
+/*
+ * remove_expired() removes expired cookies.
+ */
+static void remove_expired(struct CookieInfo *cookies)
+{
+ struct Cookie *co, *nx, *pv;
+ curl_off_t now = (curl_off_t)time(NULL);
+
+ co = cookies->cookies;
+ pv = NULL;
+ while(co) {
+ nx = co->next;
+ if((co->expirestr || co->maxage) && co->expires < now) {
+ if(co == cookies->cookies) {
+ cookies->cookies = co->next;
+ }
+ else {
+ pv->next = co->next;
+ }
+ cookies->numcookies--;
+ freecookie(co);
+ }
+ else {
+ pv = co;
+ }
+ co = nx;
+ }
+}
+
+/*
+ * Return true if the given string is an IP(v4|v6) address.
+ */
+static bool isip(const char *domain)
+{
+ struct in_addr addr;
+#ifdef ENABLE_IPV6
+ struct in6_addr addr6;
+#endif
+
+ if(Curl_inet_pton(AF_INET, domain, &addr)
+#ifdef ENABLE_IPV6
+ || Curl_inet_pton(AF_INET6, domain, &addr6)
+#endif
+ ) {
+ /* domain name given as IP address */
+ return TRUE;
+ }
+
+ return FALSE;
+}
/****************************************************************************
*
*
* Add a single cookie line to the cookie keeping object.
*
+ * Be aware that sometimes we get an IP-only host name, and that might also be
+ * a numerical IPv6 address.
+ *
+ * Returns NULL on out of memory or invalid cookie. This is suboptimal,
+ * as they should be treated separately.
***************************************************************************/
struct Cookie *
endofn++;
/* name ends with a '=' ? */
- sep = *endofn == '='?TRUE:FALSE;
+ sep = (*endofn == '=')?TRUE:FALSE;
/* Strip off trailing whitespace from the 'what' */
while(len && ISBLANK(what[len-1])) {
badcookie = TRUE; /* out of memory bad */
break;
}
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath) {
+ badcookie = TRUE; /* out of memory bad */
+ break;
+ }
}
else if(Curl_raw_equal("domain", name)) {
- /* note that this name may or may not have a preceding dot, but
- we don't care about that, we treat the names the same anyway */
-
- const char *domptr=whatptr;
- const char *nextptr;
- int dotcount=1;
+ bool is_ip;
+ const char *dotp;
- /* Count the dots, we need to make sure that there are enough
- of them. */
+ /* Now, we make sure that our host is within the given domain,
+ or the given domain is not valid and thus cannot be set. */
if('.' == whatptr[0])
- /* don't count the initial dot, assume it */
- domptr++;
-
- do {
- nextptr = strchr(domptr, '.');
- if(nextptr) {
- if(domptr != nextptr)
- dotcount++;
- domptr = nextptr+1;
+ whatptr++; /* ignore preceding dot */
+
+ is_ip = isip(domain ? domain : whatptr);
+
+ /* check for more dots */
+ dotp = strchr(whatptr, '.');
+ if(!dotp)
+ domain=":";
+
+ if(!domain
+ || (is_ip && !strcmp(whatptr, domain))
+ || (!is_ip && tailmatch(whatptr, domain))) {
+ strstore(&co->domain, whatptr);
+ if(!co->domain) {
+ badcookie = TRUE;
+ break;
}
- } while(nextptr);
-
- /* The original Netscape cookie spec defined that this domain name
- MUST have three dots (or two if one of the seven holy TLDs),
- but it seems that these kinds of cookies are in use "out there"
- so we cannot be that strict. I've therefore lowered the check
- to not allow less than two dots. */
-
- if(dotcount < 2) {
- /* Received and skipped a cookie with a domain using too few
- dots. */
- badcookie=TRUE; /* mark this as a bad cookie */
- infof(data, "skipped cookie with illegal dotcount domain: %s\n",
- whatptr);
- }
- else {
- /* Now, we make sure that our host is within the given domain,
- or the given domain is not valid and thus cannot be set. */
-
- if('.' == whatptr[0])
- whatptr++; /* ignore preceding dot */
-
- if(!domain || tailmatch(whatptr, domain)) {
- const char *tailptr=whatptr;
- if(tailptr[0] == '.')
- tailptr++;
- strstore(&co->domain, tailptr); /* don't prefix w/dots
- internally */
- if(!co->domain) {
- badcookie = TRUE;
- break;
- }
+ if(!is_ip)
co->tailmatch=TRUE; /* we always do that if the domain name was
given */
- }
- else {
- /* we did not get a tailmatch and then the attempted set domain
- is not a domain to which the current host belongs. Mark as
- bad. */
- badcookie=TRUE;
- infof(data, "skipped cookie with bad tailmatch domain: %s\n",
- whatptr);
- }
+ }
+ else {
+ /* we did not get a tailmatch and then the attempted set domain
+ is not a domain to which the current host belongs. Mark as
+ bad. */
+ badcookie=TRUE;
+ infof(data, "skipped cookie with bad tailmatch domain: %s\n",
+ whatptr);
}
}
else if(Curl_raw_equal("version", name)) {
badcookie = TRUE;
break;
}
- co->expires =
- strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
- + (long)now;
}
else if(Curl_raw_equal("expires", name)) {
strstore(&co->expirestr, whatptr);
badcookie = TRUE;
break;
}
- /* Note that if the date couldn't get parsed for whatever reason,
- the cookie will be treated as a session cookie */
- co->expires = curl_getdate(what, &now);
-
- /* Session cookies have expires set to 0 so if we get that back
- from the date parser let's add a second to make it a
- non-session cookie */
- if(co->expires == 0)
- co->expires = 1;
- else if(co->expires < 0)
- co->expires = 0;
}
else if(!co->name) {
co->name = strdup(name);
semiptr=strchr(ptr, '\0');
} while(semiptr);
+ if(co->maxage) {
+ co->expires =
+ curlx_strtoofft((*co->maxage=='\"')?
+ &co->maxage[1]:&co->maxage[0], NULL, 10);
+ if(CURL_OFF_T_MAX - now < co->expires)
+ /* avoid overflow */
+ co->expires = CURL_OFF_T_MAX;
+ else
+ co->expires += now;
+ }
+ else if(co->expirestr) {
+ /* Note that if the date couldn't get parsed for whatever reason,
+ the cookie will be treated as a session cookie */
+ co->expires = curl_getdate(co->expirestr, NULL);
+
+ /* Session cookies have expires set to 0 so if we get that back
+ from the date parser let's add a second to make it a
+ non-session cookie */
+ if(co->expires == 0)
+ co->expires = 1;
+ else if(co->expires < 0)
+ co->expires = 0;
+ }
+
if(!badcookie && !co->domain) {
if(domain) {
/* no domain was given in the header line, set the default */
if(co->path) {
memcpy(co->path, path, pathlen);
co->path[pathlen]=0; /* zero terminate */
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath)
+ badcookie = TRUE; /* out of memory bad */
}
else
badcookie = TRUE;
firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
- /* Here's a quick check to eliminate normal HTTP-headers from this */
- if(!firstptr || strchr(firstptr, ':')) {
- free(co);
- return NULL;
- }
-
/* Now loop through the fields and init the struct we already have
allocated */
for(ptr=firstptr, fields=0; ptr && !badcookie;
As far as I can see, it is set to true when the cookie says
.domain.com and to false when the domain is complete www.domain.com
*/
- co->tailmatch=(bool)Curl_raw_equal(ptr, "TRUE");
+ co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
break;
case 2:
/* It turns out, that sometimes the file format allows the path
co->path = strdup(ptr);
if(!co->path)
badcookie = TRUE;
+ else {
+ co->spath = sanitize_cookie_path(co->path);
+ if(!co->spath) {
+ badcookie = TRUE; /* out of memory bad */
+ }
+ }
break;
}
/* this doesn't look like a path, make one up! */
co->path = strdup("/");
if(!co->path)
badcookie = TRUE;
+ co->spath = strdup("/");
+ if(!co->spath)
+ badcookie = TRUE;
fields++; /* add a field and fall down to secure */
/* FALLTHROUGH */
case 3:
- co->secure = (bool)Curl_raw_equal(ptr, "TRUE");
+ co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
break;
case 4:
co->expires = curlx_strtoofft(ptr, NULL, 10);
superceeds an already existing cookie, which it may if the previous have
the same domain and path as this */
+ /* at first, remove expired cookies */
+ remove_expired(c);
+
clist = c->cookies;
replace_old = FALSE;
while(clist) {
if(replace_old) {
/* the domains were identical */
- if(clist->path && co->path) {
- if(Curl_raw_equal(clist->path, co->path)) {
+ if(clist->spath && co->spath) {
+ if(Curl_raw_equal(clist->spath, co->spath)) {
replace_old = TRUE;
}
else
replace_old = FALSE;
}
- else if(!clist->path && !co->path)
+ else if(!clist->spath && !co->spath)
replace_old = TRUE;
else
replace_old = FALSE;
free(clist->domain);
if(clist->path)
free(clist->path);
+ if(clist->spath)
+ free(clist->spath);
if(clist->expirestr)
free(clist->expirestr);
if(c->running)
/* Only show this when NOT reading the cookies from a file */
infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
- "expire %" FORMAT_OFF_T "\n",
+ "expire %" CURL_FORMAT_CURL_OFF_T "\n",
replace_old?"Replaced":"Added", co->name, co->value,
co->domain, co->path, co->expires);
lastc->next = co;
else
c->cookies = co;
+ c->numcookies++; /* one more cookie in the jar */
}
- c->numcookies++; /* one more cookie in the jar */
return co;
}
*
* If 'newsession' is TRUE, discard all "session cookies" on read from file.
*
+ * Returns NULL on out of memory. Invalid cookies are ignored.
****************************************************************************/
struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
const char *file,
bool newsession)
{
struct CookieInfo *c;
- FILE *fp;
+ FILE *fp = NULL;
bool fromfile=TRUE;
+ char *line = NULL;
if(NULL == inc) {
/* we didn't get a struct, create one */
if(!c)
return NULL; /* failed to get memory */
c->filename = strdup(file?file:"none"); /* copy the name just in case */
+ if(!c->filename)
+ goto fail; /* failed to get memory */
}
else {
/* we got an already existing one, use that */
char *lineptr;
bool headerline;
- char *line = malloc(MAX_COOKIE_LINE);
- if(line) {
- while(fgets(line, MAX_COOKIE_LINE, fp)) {
- if(checkprefix("Set-Cookie:", line)) {
- /* This is a cookie line, get it! */
- lineptr=&line[11];
- headerline=TRUE;
- }
- else {
- lineptr=line;
- headerline=FALSE;
- }
- while(*lineptr && ISBLANK(*lineptr))
- lineptr++;
-
- Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
+ line = malloc(MAX_COOKIE_LINE);
+ if(!line)
+ goto fail;
+ while(fgets(line, MAX_COOKIE_LINE, fp)) {
+ if(checkprefix("Set-Cookie:", line)) {
+ /* This is a cookie line, get it! */
+ lineptr=&line[11];
+ headerline=TRUE;
+ }
+ else {
+ lineptr=line;
+ headerline=FALSE;
}
- free(line); /* free the line buffer */
+ while(*lineptr && ISBLANK(*lineptr))
+ lineptr++;
+
+ Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
}
+ free(line); /* free the line buffer */
+
if(fromfile)
fclose(fp);
}
c->running = TRUE; /* now, we're running */
return c;
+
+fail:
+ Curl_safefree(line);
+ if(!inc)
+ /* Only clean up if we allocated it here, as the original could still be in
+ * use by a share handle */
+ Curl_cookie_cleanup(c);
+ if(fromfile && fp)
+ fclose(fp);
+ return NULL; /* out of memory */
}
/* sort this so that the longest path gets before the shorter path */
{
struct Cookie *c1 = *(struct Cookie **)p1;
struct Cookie *c2 = *(struct Cookie **)p2;
+ size_t l1, l2;
+
+ /* 1 - compare cookie path lengths */
+ l1 = c1->path ? strlen(c1->path) : 0;
+ l2 = c2->path ? strlen(c2->path) : 0;
- size_t l1 = c1->path?strlen(c1->path):0;
- size_t l2 = c2->path?strlen(c2->path):0;
+ if(l1 != l2)
+ return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
- return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
+ /* 2 - compare cookie domain lengths */
+ l1 = c1->domain ? strlen(c1->domain) : 0;
+ l2 = c2->domain ? strlen(c2->domain) : 0;
+
+ if(l1 != l2)
+ return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
+
+ /* 3 - compare cookie names */
+ if(c1->name && c2->name)
+ return strcmp(c1->name, c2->name);
+
+ /* sorry, can't be more deterministic */
+ return 0;
}
/*****************************************************************************
time_t now = time(NULL);
struct Cookie *mainco=NULL;
size_t matches = 0;
+ bool is_ip;
if(!c || !c->cookies)
return NULL; /* no cookie struct or no cookies in the struct */
+ /* at first, remove expired cookies */
+ remove_expired(c);
+
+ /* check if host is an IP(v4|v6) address */
+ is_ip = isip(host);
+
co = c->cookies;
while(co) {
/* now check if the domain is correct */
if(!co->domain ||
- (co->tailmatch && tailmatch(co->domain, host)) ||
- (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
+ (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
+ ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
/* the right part of the host matches the domain stuff in the
cookie data */
/* now check the left part of the path with the cookies path
requirement */
- if(!co->path ||
- /* not using checkprefix() because matching should be
- case-sensitive */
- !strncmp(co->path, path, strlen(co->path)) ) {
+ if(!co->spath || pathmatch(co->spath, path) ) {
/* and now, we know this is a match and we should create an
entry for the return-linked-list */
for(i=0; co; co = co->next)
array[i++] = co;
- /* now sort the cookie pointers in path lenth order */
+ /* now sort the cookie pointers in path length order */
qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
/* remake the linked list order according to the new order */
void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
{
struct Cookie *next;
- if(co) {
- while(co) {
- next = co->next;
- if(cookiestoo)
- freecookie(co);
- else
- free(co); /* we only free the struct since the "members" are all just
- pointed out in the main cookie list! */
- co = next;
- }
+ while(co) {
+ next = co->next;
+ if(cookiestoo)
+ freecookie(co);
+ else
+ free(co); /* we only free the struct since the "members" are all just
+ pointed out in the main cookie list! */
+ co = next;
}
}
*
* Curl_cookie_cleanup()
*
- * Free a "cookie object" previous created with cookie_init().
+ * Free a "cookie object" previous created with Curl_cookie_init().
*
****************************************************************************/
void Curl_cookie_cleanup(struct CookieInfo *c)
{
- struct Cookie *co;
- struct Cookie *next;
if(c) {
if(c->filename)
free(c->filename);
- co = c->cookies;
-
- while(co) {
- next = co->next;
- freecookie(co);
- co = next;
- }
+ Curl_cookie_freelist(c->cookies, TRUE);
free(c); /* free the base struct as well */
}
}
"%s\t" /* tailmatch */
"%s\t" /* path */
"%s\t" /* secure */
- "%" FORMAT_OFF_T "\t" /* expires */
+ "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */
"%s\t" /* name */
"%s", /* value */
co->httponly?"#HttpOnly_":"",
destination file */
return 0;
+ /* at first, remove expired cookies */
+ remove_expired(c);
+
if(strequal("-", dumphere)) {
/* use stdout */
out = stdout;
char *format_ptr;
fputs("# Netscape HTTP Cookie File\n"
- "# http://curl.haxx.se/rfc/cookie_spec.html\n"
+ "# http://curl.haxx.se/docs/http-cookies.html\n"
"# This file was generated by libcurl! Edit at your own risk.\n\n",
out);
co = c->cookies;
c = data->cookies->cookies;
- beg = list;
while(c) {
/* fill the list with _all_ the cookies we know */
line = get_netscape_format(c);
- if(line == NULL) {
- curl_slist_free_all(beg);
+ if(!line) {
+ curl_slist_free_all(list);
return NULL;
}
- list = curl_slist_append(list, line);
- free(line);
- if(list == NULL) {
- curl_slist_free_all(beg);
+ beg = Curl_slist_append_nodup(list, line);
+ if(!beg) {
+ free(line);
+ curl_slist_free_all(list);
return NULL;
}
- else if(beg == NULL) {
- beg = list;
- }
+ list = beg;
c = c->next;
}
data->set.str[STRING_COOKIEJAR]);
}
else {
- if(cleanup && data->change.cookielist)
+ if(cleanup && data->change.cookielist) {
/* since nothing is written, we can just free the list of cookie file
names */
curl_slist_free_all(data->change.cookielist); /* clean up list */
+ data->change.cookielist = NULL;
+ }
Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
}