fix out of memory handling issue
[platform/upstream/curl.git] / lib / cookie.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2007, 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 http://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  * $Id$
22  ***************************************************************************/
23
24 /***
25
26
27 RECEIVING COOKIE INFORMATION
28 ============================
29
30 struct CookieInfo *cookie_init(char *file);
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 int cookies_set(struct CookieInfo *cookie, char *cookie_line);
36
37         The 'cookie_line' parameter is a full "Set-cookie:" line as
38         received from a server.
39
40         The function need to replace previously stored lines that this new
41         line superceeds.
42
43         It may remove lines that are expired.
44
45         It should return an indication of success/error.
46
47
48 SENDING COOKIE INFORMATION
49 ==========================
50
51 struct Cookies *cookie_getlist(struct CookieInfo *cookie,
52                                char *host, char *path, bool secure);
53
54         For a given host and path, return a linked list of cookies that
55         the client should send to the server if used now. The secure
56         boolean informs the cookie if a secure connection is achieved or
57         not.
58
59         It shall only return cookies that haven't expired.
60
61
62 Example set of cookies:
63
64     Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
65     Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
66     domain=.fidelity.com; path=/ftgw; secure
67     Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
68     domain=.fidelity.com; path=/; secure
69     Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
70     domain=.fidelity.com; path=/; secure
71     Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
72     domain=.fidelity.com; path=/; secure
73     Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
74     domain=.fidelity.com; path=/; secure
75     Set-cookie:
76     Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
77     13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
78 ****/
79
80
81 #include "setup.h"
82
83 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
84
85 #include <stdlib.h>
86 #include <string.h>
87
88 #define _MPRINTF_REPLACE /* without this on windows OS we get undefined reference to snprintf */
89 #include <curl/mprintf.h>
90
91 #include "urldata.h"
92 #include "cookie.h"
93 #include "strequal.h"
94 #include "strtok.h"
95 #include "sendf.h"
96 #include "memory.h"
97 #include "share.h"
98 #include "strtoofft.h"
99
100 /* The last #include file should be: */
101 #ifdef CURLDEBUG
102 #include "memdebug.h"
103 #endif
104
105
106 static void freecookie(struct Cookie *co)
107 {
108   if(co->expirestr)
109     free(co->expirestr);
110   if(co->domain)
111     free(co->domain);
112   if(co->path)
113     free(co->path);
114   if(co->name)
115     free(co->name);
116   if(co->value)
117     free(co->value);
118   if(co->maxage)
119     free(co->maxage);
120   if(co->version)
121     free(co->version);
122
123   free(co);
124 }
125
126 static bool tailmatch(const char *little, const char *bigone)
127 {
128   size_t littlelen = strlen(little);
129   size_t biglen = strlen(bigone);
130
131   if(littlelen > biglen)
132     return FALSE;
133
134   return (bool)strequal(little, bigone+biglen-littlelen);
135 }
136
137 /*
138  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
139  */
140 void Curl_cookie_loadfiles(struct SessionHandle *data)
141 {
142   struct curl_slist *list = data->change.cookielist;
143   if(list) {
144     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
145     while(list) {
146       data->cookies = Curl_cookie_init(data,
147                                        list->data,
148                                        data->cookies,
149                                        data->set.cookiesession);
150       list = list->next;
151     }
152     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
153     curl_slist_free_all(data->change.cookielist); /* clean up list */
154     data->change.cookielist = NULL; /* don't do this again! */
155   }
156 }
157
158 /****************************************************************************
159  *
160  * Curl_cookie_add()
161  *
162  * Add a single cookie line to the cookie keeping object.
163  *
164  ***************************************************************************/
165
166 struct Cookie *
167 Curl_cookie_add(struct SessionHandle *data,
168                 /* The 'data' pointer here may be NULL at times, and thus
169                    must only be used very carefully for things that can deal
170                    with data being NULL. Such as infof() and similar */
171
172                 struct CookieInfo *c,
173                 bool httpheader, /* TRUE if HTTP header-style line */
174                 char *lineptr,   /* first character of the line */
175                 char *domain,    /* default domain */
176                 char *path)      /* full path used when this cookie is set,
177                                     used to get default path for the cookie
178                                     unless set */
179 {
180   struct Cookie *clist;
181   char *what;
182   char name[MAX_NAME];
183   char *ptr;
184   char *semiptr;
185   struct Cookie *co;
186   struct Cookie *lastc=NULL;
187   time_t now = time(NULL);
188   bool replace_old = FALSE;
189   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
190
191 #ifdef CURL_DISABLE_VERBOSE_STRINGS
192   (void)data;
193 #endif
194
195   /* First, alloc and init a new struct for it */
196   co = (struct Cookie *)calloc(sizeof(struct Cookie), 1);
197   if(!co)
198     return NULL; /* bail out if we're this low on memory */
199
200   if(httpheader) {
201     /* This line was read off a HTTP-header */
202     char *sep;
203
204     what = malloc(MAX_COOKIE_LINE);
205     if(!what) {
206       free(co);
207       return NULL;
208     }
209
210     semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
211
212     while(*lineptr && ISBLANK(*lineptr))
213       lineptr++;
214
215     ptr = lineptr;
216     do {
217       /* we have a <what>=<this> pair or a 'secure' word here */
218       sep = strchr(ptr, '=');
219       if(sep && (!semiptr || (semiptr>sep)) ) {
220         /*
221          * There is a = sign and if there was a semicolon too, which make sure
222          * that the semicolon comes _after_ the equal sign.
223          */
224
225         name[0]=what[0]=0; /* init the buffers */
226         if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
227                        MAX_COOKIE_LINE_TXT "[^;\r\n]",
228                        name, what)) {
229           /* this is a <name>=<what> pair */
230
231           char *whatptr;
232
233           /* Strip off trailing whitespace from the 'what' */
234           size_t len=strlen(what);
235           while(len && ISBLANK(what[len-1])) {
236             what[len-1]=0;
237             len--;
238           }
239
240           /* Skip leading whitespace from the 'what' */
241           whatptr=what;
242           while(*whatptr && ISBLANK(*whatptr)) {
243             whatptr++;
244           }
245
246           if(strequal("path", name)) {
247             co->path=strdup(whatptr);
248             if(!co->path) {
249               badcookie = TRUE; /* out of memory bad */
250               break;
251             }
252           }
253           else if(strequal("domain", name)) {
254             /* note that this name may or may not have a preceeding dot, but
255                we don't care about that, we treat the names the same anyway */
256
257             const char *domptr=whatptr;
258             int dotcount=1;
259
260             /* Count the dots, we need to make sure that there are enough
261                of them. */
262
263             if('.' == whatptr[0])
264               /* don't count the initial dot, assume it */
265               domptr++;
266
267             do {
268               domptr = strchr(domptr, '.');
269               if(domptr) {
270                 domptr++;
271                 dotcount++;
272               }
273             } while(domptr);
274
275             /* The original Netscape cookie spec defined that this domain name
276                MUST have three dots (or two if one of the seven holy TLDs),
277                but it seems that these kinds of cookies are in use "out there"
278                so we cannot be that strict. I've therefore lowered the check
279                to not allow less than two dots. */
280
281             if(dotcount < 2) {
282               /* Received and skipped a cookie with a domain using too few
283                  dots. */
284               badcookie=TRUE; /* mark this as a bad cookie */
285               infof(data, "skipped cookie with illegal dotcount domain: %s\n",
286                     whatptr);
287             }
288             else {
289               /* Now, we make sure that our host is within the given domain,
290                  or the given domain is not valid and thus cannot be set. */
291
292               if('.' == whatptr[0])
293                 whatptr++; /* ignore preceeding dot */
294
295               if(!domain || tailmatch(whatptr, domain)) {
296                 const char *tailptr=whatptr;
297                 if(tailptr[0] == '.')
298                   tailptr++;
299                 co->domain=strdup(tailptr); /* don't prefix w/dots
300                                                internally */
301                 if(!co->domain) {
302                   badcookie = TRUE;
303                   break;
304                 }
305                 co->tailmatch=TRUE; /* we always do that if the domain name was
306                                        given */
307               }
308               else {
309                 /* we did not get a tailmatch and then the attempted set domain
310                    is not a domain to which the current host belongs. Mark as
311                    bad. */
312                 badcookie=TRUE;
313                 infof(data, "skipped cookie with bad tailmatch domain: %s\n",
314                       whatptr);
315               }
316             }
317           }
318           else if(strequal("version", name)) {
319             co->version=strdup(whatptr);
320             if(!co->version) {
321               badcookie = TRUE;
322               break;
323             }
324           }
325           else if(strequal("max-age", name)) {
326             /* Defined in RFC2109:
327
328                Optional.  The Max-Age attribute defines the lifetime of the
329                cookie, in seconds.  The delta-seconds value is a decimal non-
330                negative integer.  After delta-seconds seconds elapse, the
331                client should discard the cookie.  A value of zero means the
332                cookie should be discarded immediately.
333
334              */
335             co->maxage = strdup(whatptr);
336             if(!co->maxage) {
337               badcookie = TRUE;
338               break;
339             }
340             co->expires =
341               atoi((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0]) + (long)now;
342           }
343           else if(strequal("expires", name)) {
344             co->expirestr=strdup(whatptr);
345             if(!co->expirestr) {
346               badcookie = TRUE;
347               break;
348             }
349             co->expires = curl_getdate(what, &now);
350           }
351           else if(!co->name) {
352             co->name = strdup(name);
353             co->value = strdup(whatptr);
354             if(!co->name || !co->value) {
355               badcookie = TRUE;
356               break;
357             }
358           }
359           /*
360             else this is the second (or more) name we don't know
361             about! */
362         }
363         else {
364           /* this is an "illegal" <what>=<this> pair */
365         }
366       }
367       else {
368         if(sscanf(ptr, "%" MAX_COOKIE_LINE_TXT "[^;\r\n]",
369                   what)) {
370           if(strequal("secure", what))
371             co->secure = TRUE;
372           /* else,
373              unsupported keyword without assign! */
374
375         }
376       }
377       if(!semiptr || !*semiptr) {
378         /* we already know there are no more cookies */
379         semiptr = NULL;
380         continue;
381       }
382
383       ptr=semiptr+1;
384       while(ptr && *ptr && ISBLANK(*ptr))
385         ptr++;
386       semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
387
388       if(!semiptr && *ptr)
389         /* There are no more semicolons, but there's a final name=value pair
390            coming up */
391         semiptr=strchr(ptr, '\0');
392     } while(semiptr);
393
394     if(!badcookie && !co->domain) {
395       if(domain) {
396         /* no domain was given in the header line, set the default */
397         co->domain=strdup(domain);
398         if(!co->domain)
399           badcookie = TRUE;
400       }
401     }
402
403     if(!badcookie && !co->path && path) {
404       /* no path was given in the header line, set the default  */
405       char *endslash = strrchr(path, '/');
406       if(endslash) {
407         size_t pathlen = endslash-path+1; /* include the ending slash */
408         co->path=malloc(pathlen+1); /* one extra for the zero byte */
409         if(co->path) {
410           memcpy(co->path, path, pathlen);
411           co->path[pathlen]=0; /* zero terminate */
412         }
413         else
414           badcookie = TRUE;
415       }
416     }
417
418     free(what);
419
420     if(badcookie || !co->name) {
421       /* we didn't get a cookie name or a bad one,
422          this is an illegal line, bail out */
423       freecookie(co);
424       return NULL;
425     }
426
427   }
428   else {
429     /* This line is NOT a HTTP header style line, we do offer support for
430        reading the odd netscape cookies-file format here */
431     char *firstptr;
432     char *tok_buf;
433     int fields;
434
435     if(lineptr[0]=='#') {
436       /* don't even try the comments */
437       free(co);
438       return NULL;
439     }
440     /* strip off the possible end-of-line characters */
441     ptr=strchr(lineptr, '\r');
442     if(ptr)
443       *ptr=0; /* clear it */
444     ptr=strchr(lineptr, '\n');
445     if(ptr)
446       *ptr=0; /* clear it */
447
448     firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
449
450     /* Here's a quick check to eliminate normal HTTP-headers from this */
451     if(!firstptr || strchr(firstptr, ':')) {
452       free(co);
453       return NULL;
454     }
455
456     /* Now loop through the fields and init the struct we already have
457        allocated */
458     for(ptr=firstptr, fields=0; ptr && !badcookie;
459         ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
460       switch(fields) {
461       case 0:
462         if(ptr[0]=='.') /* skip preceeding dots */
463           ptr++;
464         co->domain = strdup(ptr);
465         if(!co->domain)
466           badcookie = TRUE;
467         break;
468       case 1:
469         /* This field got its explanation on the 23rd of May 2001 by
470            Andrés García:
471
472            flag: A TRUE/FALSE value indicating if all machines within a given
473            domain can access the variable. This value is set automatically by
474            the browser, depending on the value you set for the domain.
475
476            As far as I can see, it is set to true when the cookie says
477            .domain.com and to false when the domain is complete www.domain.com
478         */
479         co->tailmatch=(bool)strequal(ptr, "TRUE"); /* store information */
480         break;
481       case 2:
482         /* It turns out, that sometimes the file format allows the path
483            field to remain not filled in, we try to detect this and work
484            around it! Andrés García made us aware of this... */
485         if (strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
486           /* only if the path doesn't look like a boolean option! */
487           co->path = strdup(ptr);
488           if(!co->path)
489             badcookie = TRUE;
490           break;
491         }
492         /* this doesn't look like a path, make one up! */
493         co->path = strdup("/");
494         if(!co->path)
495           badcookie = TRUE;
496         fields++; /* add a field and fall down to secure */
497         /* FALLTHROUGH */
498       case 3:
499         co->secure = (bool)strequal(ptr, "TRUE");
500         break;
501       case 4:
502         co->expires = curlx_strtoofft(ptr, NULL, 10);
503         break;
504       case 5:
505         co->name = strdup(ptr);
506         if(!co->name)
507           badcookie = TRUE;
508         break;
509       case 6:
510         co->value = strdup(ptr);
511         if(!co->value)
512           badcookie = TRUE;
513         break;
514       }
515     }
516     if(6 == fields) {
517       /* we got a cookie with blank contents, fix it */
518       co->value = strdup("");
519       if(!co->value)
520         badcookie = TRUE;
521       else
522         fields++;
523     }
524
525     if(!badcookie && (7 != fields))
526       /* we did not find the sufficient number of fields */
527       badcookie = TRUE;
528
529     if(badcookie) {
530       freecookie(co);
531       return NULL;
532     }
533
534   }
535
536   if(!c->running &&    /* read from a file */
537      c->newsession &&  /* clean session cookies */
538      !co->expires) {   /* this is a session cookie since it doesn't expire! */
539     freecookie(co);
540     return NULL;
541   }
542
543   co->livecookie = c->running;
544
545   /* now, we have parsed the incoming line, we must now check if this
546      superceeds an already existing cookie, which it may if the previous have
547      the same domain and path as this */
548
549   clist = c->cookies;
550   replace_old = FALSE;
551   while(clist) {
552     if(strequal(clist->name, co->name)) {
553       /* the names are identical */
554
555       if(clist->domain && co->domain) {
556         if(strequal(clist->domain, co->domain))
557           /* The domains are identical */
558           replace_old=TRUE;
559       }
560       else if(!clist->domain && !co->domain)
561         replace_old = TRUE;
562
563       if(replace_old) {
564         /* the domains were identical */
565
566         if(clist->path && co->path) {
567           if(strequal(clist->path, co->path)) {
568             replace_old = TRUE;
569           }
570           else
571             replace_old = FALSE;
572         }
573         else if(!clist->path && !co->path)
574           replace_old = TRUE;
575         else
576           replace_old = FALSE;
577
578       }
579
580       if(replace_old && !co->livecookie && clist->livecookie) {
581         /* Both cookies matched fine, except that the already present
582            cookie is "live", which means it was set from a header, while
583            the new one isn't "live" and thus only read from a file. We let
584            live cookies stay alive */
585
586         /* Free the newcomer and get out of here! */
587         freecookie(co);
588         return NULL;
589       }
590
591       if(replace_old) {
592         co->next = clist->next; /* get the next-pointer first */
593
594         /* then free all the old pointers */
595         if(clist->name)
596           free(clist->name);
597         if(clist->value)
598           free(clist->value);
599         if(clist->domain)
600           free(clist->domain);
601         if(clist->path)
602           free(clist->path);
603         if(clist->expirestr)
604           free(clist->expirestr);
605
606         if(clist->version)
607           free(clist->version);
608         if(clist->maxage)
609           free(clist->maxage);
610
611         *clist = *co;  /* then store all the new data */
612
613         free(co);   /* free the newly alloced memory */
614         co = clist; /* point to the previous struct instead */
615
616         /* We have replaced a cookie, now skip the rest of the list but
617            make sure the 'lastc' pointer is properly set */
618         do {
619           lastc = clist;
620           clist = clist->next;
621         } while(clist);
622         break;
623       }
624     }
625     lastc = clist;
626     clist = clist->next;
627   }
628
629   if(c->running)
630     /* Only show this when NOT reading the cookies from a file */
631     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, expire %d\n",
632           replace_old?"Replaced":"Added", co->name, co->value,
633           co->domain, co->path, co->expires);
634
635   if(!replace_old) {
636     /* then make the last item point on this new one */
637     if(lastc)
638       lastc->next = co;
639     else
640       c->cookies = co;
641   }
642
643   c->numcookies++; /* one more cookie in the jar */
644   return co;
645 }
646
647 /*****************************************************************************
648  *
649  * Curl_cookie_init()
650  *
651  * Inits a cookie struct to read data from a local file. This is always
652  * called before any cookies are set. File may be NULL.
653  *
654  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
655  *
656  ****************************************************************************/
657 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
658                                     char *file,
659                                     struct CookieInfo *inc,
660                                     bool newsession)
661 {
662   struct CookieInfo *c;
663   FILE *fp;
664   bool fromfile=TRUE;
665
666   if(NULL == inc) {
667     /* we didn't get a struct, create one */
668     c = (struct CookieInfo *)calloc(1, sizeof(struct CookieInfo));
669     if(!c)
670       return NULL; /* failed to get memory */
671     c->filename = strdup(file?file:"none"); /* copy the name just in case */
672   }
673   else {
674     /* we got an already existing one, use that */
675     c = inc;
676   }
677   c->running = FALSE; /* this is not running, this is init */
678
679   if(file && strequal(file, "-")) {
680     fp = stdin;
681     fromfile=FALSE;
682   }
683   else if(file && !*file) {
684     /* points to a "" string */
685     fp = NULL;
686   }
687   else
688     fp = file?fopen(file, "r"):NULL;
689
690   c->newsession = newsession; /* new session? */
691
692   if(fp) {
693     char *lineptr;
694     bool headerline;
695
696     char *line = (char *)malloc(MAX_COOKIE_LINE);
697     if(line) {
698       while(fgets(line, MAX_COOKIE_LINE, fp)) {
699         if(checkprefix("Set-Cookie:", line)) {
700           /* This is a cookie line, get it! */
701           lineptr=&line[11];
702           headerline=TRUE;
703         }
704         else {
705           lineptr=line;
706           headerline=FALSE;
707         }
708         while(*lineptr && ISBLANK(*lineptr))
709           lineptr++;
710
711         Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
712       }
713       free(line); /* free the line buffer */
714     }
715     if(fromfile)
716       fclose(fp);
717   }
718
719   c->running = TRUE;          /* now, we're running */
720
721   return c;
722 }
723
724 /*****************************************************************************
725  *
726  * Curl_cookie_getlist()
727  *
728  * For a given host and path, return a linked list of cookies that the
729  * client should send to the server if used now. The secure boolean informs
730  * the cookie if a secure connection is achieved or not.
731  *
732  * It shall only return cookies that haven't expired.
733  *
734  ****************************************************************************/
735
736 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
737                                    char *host, char *path, bool secure)
738 {
739   struct Cookie *newco;
740   struct Cookie *co;
741   time_t now = time(NULL);
742   struct Cookie *mainco=NULL;
743
744   if(!c || !c->cookies)
745     return NULL; /* no cookie struct or no cookies in the struct */
746
747   co = c->cookies;
748
749   while(co) {
750     /* only process this cookie if it is not expired or had no expire
751        date AND that if the cookie requires we're secure we must only
752        continue if we are! */
753     if( (co->expires<=0 || (co->expires> now)) &&
754         (co->secure?secure:TRUE) ) {
755
756       /* now check if the domain is correct */
757       if(!co->domain ||
758          (co->tailmatch && tailmatch(co->domain, host)) ||
759          (!co->tailmatch && strequal(host, co->domain)) ) {
760         /* the right part of the host matches the domain stuff in the
761            cookie data */
762
763         /* now check the left part of the path with the cookies path
764            requirement */
765         if(!co->path ||
766            /* not using checkprefix() because matching should be
767               case-sensitive */
768            !strncmp(co->path, path, strlen(co->path)) ) {
769
770           /* and now, we know this is a match and we should create an
771              entry for the return-linked-list */
772
773           newco = (struct Cookie *)malloc(sizeof(struct Cookie));
774           if(newco) {
775             /* first, copy the whole source cookie: */
776             memcpy(newco, co, sizeof(struct Cookie));
777
778             /* then modify our next */
779             newco->next = mainco;
780
781             /* point the main to us */
782             mainco = newco;
783           }
784           else {
785             /* failure, clear up the allocated chain and return NULL */
786             while(mainco) {
787               co = mainco->next;
788               free(mainco);
789               mainco = co;
790             }
791
792             return NULL;
793           }
794         }
795       }
796     }
797     co = co->next;
798   }
799
800   return mainco; /* return the new list */
801 }
802
803 /*****************************************************************************
804  *
805  * Curl_cookie_clearall()
806  *
807  * Clear all existing cookies and reset the counter.
808  *
809  ****************************************************************************/
810 void Curl_cookie_clearall(struct CookieInfo *cookies)
811 {
812   if(cookies) {
813     Curl_cookie_freelist(cookies->cookies);
814     cookies->cookies = NULL;
815     cookies->numcookies = 0;
816   }
817 }
818
819 /*****************************************************************************
820  *
821  * Curl_cookie_freelist()
822  *
823  * Free a list of cookies previously returned by Curl_cookie_getlist();
824  *
825  ****************************************************************************/
826
827 void Curl_cookie_freelist(struct Cookie *co)
828 {
829   struct Cookie *next;
830   if(co) {
831     while(co) {
832       next = co->next;
833       free(co); /* we only free the struct since the "members" are all
834                       just copied! */
835       co = next;
836     }
837   }
838 }
839
840
841 /*****************************************************************************
842  *
843  * Curl_cookie_clearsess()
844  *
845  * Free all session cookies in the cookies list.
846  *
847  ****************************************************************************/
848 void Curl_cookie_clearsess(struct CookieInfo *cookies)
849 {
850   struct Cookie *first, *curr, *next, *prev = NULL;
851
852   if(!cookies->cookies)
853     return;
854
855   first = curr = prev = cookies->cookies;
856
857   for(; curr; curr = next) {
858     next = curr->next;
859     if(!curr->expires) {
860       if(first == curr)
861         first = next;
862
863       if(prev == curr)
864         prev = next;
865       else
866         prev->next = next;
867
868       free(curr);
869       cookies->numcookies--;
870     }
871     else
872       prev = curr;
873   }
874
875   cookies->cookies = first;
876 }
877
878
879 /*****************************************************************************
880  *
881  * Curl_cookie_cleanup()
882  *
883  * Free a "cookie object" previous created with cookie_init().
884  *
885  ****************************************************************************/
886 void Curl_cookie_cleanup(struct CookieInfo *c)
887 {
888   struct Cookie *co;
889   struct Cookie *next;
890   if(c) {
891     if(c->filename)
892       free(c->filename);
893     co = c->cookies;
894
895     while(co) {
896       next = co->next;
897       freecookie(co);
898       co = next;
899     }
900     free(c); /* free the base struct as well */
901   }
902 }
903
904 /* get_netscape_format()
905  *
906  * Formats a string for Netscape output file, w/o a newline at the end.
907  *
908  * Function returns a char * to a formatted line. Has to be free()d
909 */
910 static char *get_netscape_format(const struct Cookie *co)
911 {
912   return aprintf(
913     "%s%s\t" /* domain */
914     "%s\t"   /* tailmatch */
915     "%s\t"   /* path */
916     "%s\t"   /* secure */
917     "%" FORMAT_OFF_T "\t"   /* expires */
918     "%s\t"   /* name */
919     "%s",    /* value */
920     /* Make sure all domains are prefixed with a dot if they allow
921        tailmatching. This is Mozilla-style. */
922     (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
923     co->domain?co->domain:"unknown",
924     co->tailmatch?"TRUE":"FALSE",
925     co->path?co->path:"/",
926     co->secure?"TRUE":"FALSE",
927     co->expires,
928     co->name,
929     co->value?co->value:"");
930 }
931
932 /*
933  * Curl_cookie_output()
934  *
935  * Writes all internally known cookies to the specified file. Specify
936  * "-" as file name to write to stdout.
937  *
938  * The function returns non-zero on write failure.
939  */
940 int Curl_cookie_output(struct CookieInfo *c, char *dumphere)
941 {
942   struct Cookie *co;
943   FILE *out;
944   bool use_stdout=FALSE;
945
946   if((NULL == c) || (0 == c->numcookies))
947     /* If there are no known cookies, we don't write or even create any
948        destination file */
949     return 0;
950
951   if(strequal("-", dumphere)) {
952     /* use stdout */
953     out = stdout;
954     use_stdout=TRUE;
955   }
956   else {
957     out = fopen(dumphere, "w");
958     if(!out)
959       return 1; /* failure */
960   }
961
962   if(c) {
963     char *format_ptr;
964
965     fputs("# Netscape HTTP Cookie File\n"
966           "# http://curlm.haxx.se/rfc/cookie_spec.html\n"
967           "# This file was generated by libcurl! Edit at your own risk.\n\n",
968           out);
969     co = c->cookies;
970
971     while(co) {
972       format_ptr = get_netscape_format(co);
973       if (format_ptr == NULL) {
974         fprintf(out, "#\n# Fatal libcurl error\n");
975         fclose(out);
976         return 1;
977       }
978       fprintf(out, "%s\n", format_ptr);
979       free(format_ptr);
980       co=co->next;
981     }
982   }
983
984   if(!use_stdout)
985     fclose(out);
986
987   return 0;
988 }
989
990 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
991 {
992   struct curl_slist *list = NULL;
993   struct curl_slist *beg;
994   struct Cookie *c;
995   char *line;
996
997   if ((data->cookies == NULL) ||
998       (data->cookies->numcookies == 0))
999     return NULL;
1000
1001   c = data->cookies->cookies;
1002
1003   beg = list;
1004   while (c) {
1005     /* fill the list with _all_ the cookies we know */
1006     line = get_netscape_format(c);
1007     if (line == NULL) {
1008       curl_slist_free_all(beg);
1009       return NULL;
1010     }
1011     list = curl_slist_append(list, line);
1012     free(line);
1013     if (list == NULL) {
1014       curl_slist_free_all(beg);
1015       return NULL;
1016     }
1017     else if (beg == NULL) {
1018       beg = list;
1019     }
1020     c = c->next;
1021   }
1022
1023   return list;
1024 }
1025
1026 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */