For congruency sake with the naming of other CURL_XXXXXX_CURL_OFF_T macros,
[platform/upstream/curl.git] / lib / cookie.c
1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2008, 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                 const char *domain, /* default domain */
176                 const 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 name[MAX_NAME];
182   struct Cookie *co;
183   struct Cookie *lastc=NULL;
184   time_t now = time(NULL);
185   bool replace_old = FALSE;
186   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
187
188 #ifdef CURL_DISABLE_VERBOSE_STRINGS
189   (void)data;
190 #endif
191
192   /* First, alloc and init a new struct for it */
193   co = (struct Cookie *)calloc(sizeof(struct Cookie), 1);
194   if(!co)
195     return NULL; /* bail out if we're this low on memory */
196
197   if(httpheader) {
198     /* This line was read off a HTTP-header */
199     const char *ptr;
200     const char *sep;
201     const char *semiptr;
202     char *what;
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           const 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           }
373           else if (strequal("httponly", what)) {
374             co->httponly = TRUE;
375           }
376           /* else,
377              unsupported keyword without assign! */
378
379         }
380       }
381       if(!semiptr || !*semiptr) {
382         /* we already know there are no more cookies */
383         semiptr = NULL;
384         continue;
385       }
386
387       ptr=semiptr+1;
388       while(ptr && *ptr && ISBLANK(*ptr))
389         ptr++;
390       semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
391
392       if(!semiptr && *ptr)
393         /* There are no more semicolons, but there's a final name=value pair
394            coming up */
395         semiptr=strchr(ptr, '\0');
396     } while(semiptr);
397
398     if(!badcookie && !co->domain) {
399       if(domain) {
400         /* no domain was given in the header line, set the default */
401         co->domain=strdup(domain);
402         if(!co->domain)
403           badcookie = TRUE;
404       }
405     }
406
407     if(!badcookie && !co->path && path) {
408       /* no path was given in the header line, set the default  */
409       char *endslash = strrchr(path, '/');
410       if(endslash) {
411         size_t pathlen = endslash-path+1; /* include the ending slash */
412         co->path=malloc(pathlen+1); /* one extra for the zero byte */
413         if(co->path) {
414           memcpy(co->path, path, pathlen);
415           co->path[pathlen]=0; /* zero terminate */
416         }
417         else
418           badcookie = TRUE;
419       }
420     }
421
422     free(what);
423
424     if(badcookie || !co->name) {
425       /* we didn't get a cookie name or a bad one,
426          this is an illegal line, bail out */
427       freecookie(co);
428       return NULL;
429     }
430
431   }
432   else {
433     /* This line is NOT a HTTP header style line, we do offer support for
434        reading the odd netscape cookies-file format here */
435     char *ptr;
436     char *firstptr;
437     char *tok_buf;
438     int fields;
439
440     /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies 
441        marked with httpOnly after the domain name are not accessible  
442        from javascripts, but since curl does not operate at javascript  
443        level, we include them anyway. In Firefox's cookie files, these 
444        lines are preceeded with #HttpOnly_ and then everything is
445        as usual, so we skip 10 characters of the line..
446     */
447     if (strncmp(lineptr, "#HttpOnly_", 10) == 0) {
448       lineptr += 10;
449       co->httponly = TRUE;
450     }
451
452
453     if(lineptr[0]=='#') {
454       /* don't even try the comments */
455       free(co);
456       return NULL;
457     }
458     /* strip off the possible end-of-line characters */
459     ptr=strchr(lineptr, '\r');
460     if(ptr)
461       *ptr=0; /* clear it */
462     ptr=strchr(lineptr, '\n');
463     if(ptr)
464       *ptr=0; /* clear it */
465
466     firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
467
468     /* Here's a quick check to eliminate normal HTTP-headers from this */
469     if(!firstptr || strchr(firstptr, ':')) {
470       free(co);
471       return NULL;
472     }
473
474     /* Now loop through the fields and init the struct we already have
475        allocated */
476     for(ptr=firstptr, fields=0; ptr && !badcookie;
477         ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
478       switch(fields) {
479       case 0:
480         if(ptr[0]=='.') /* skip preceeding dots */
481           ptr++;
482         co->domain = strdup(ptr);
483         if(!co->domain)
484           badcookie = TRUE;
485         break;
486       case 1:
487         /* This field got its explanation on the 23rd of May 2001 by
488            Andrés García:
489
490            flag: A TRUE/FALSE value indicating if all machines within a given
491            domain can access the variable. This value is set automatically by
492            the browser, depending on the value you set for the domain.
493
494            As far as I can see, it is set to true when the cookie says
495            .domain.com and to false when the domain is complete www.domain.com
496         */
497         co->tailmatch=(bool)strequal(ptr, "TRUE"); /* store information */
498         break;
499       case 2:
500         /* It turns out, that sometimes the file format allows the path
501            field to remain not filled in, we try to detect this and work
502            around it! Andrés García made us aware of this... */
503         if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
504           /* only if the path doesn't look like a boolean option! */
505           co->path = strdup(ptr);
506           if(!co->path)
507             badcookie = TRUE;
508           break;
509         }
510         /* this doesn't look like a path, make one up! */
511         co->path = strdup("/");
512         if(!co->path)
513           badcookie = TRUE;
514         fields++; /* add a field and fall down to secure */
515         /* FALLTHROUGH */
516       case 3:
517         co->secure = (bool)strequal(ptr, "TRUE");
518         break;
519       case 4:
520         co->expires = curlx_strtoofft(ptr, NULL, 10);
521         break;
522       case 5:
523         co->name = strdup(ptr);
524         if(!co->name)
525           badcookie = TRUE;
526         break;
527       case 6:
528         co->value = strdup(ptr);
529         if(!co->value)
530           badcookie = TRUE;
531         break;
532       }
533     }
534     if(6 == fields) {
535       /* we got a cookie with blank contents, fix it */
536       co->value = strdup("");
537       if(!co->value)
538         badcookie = TRUE;
539       else
540         fields++;
541     }
542
543     if(!badcookie && (7 != fields))
544       /* we did not find the sufficient number of fields */
545       badcookie = TRUE;
546
547     if(badcookie) {
548       freecookie(co);
549       return NULL;
550     }
551
552   }
553
554   if(!c->running &&    /* read from a file */
555      c->newsession &&  /* clean session cookies */
556      !co->expires) {   /* this is a session cookie since it doesn't expire! */
557     freecookie(co);
558     return NULL;
559   }
560
561   co->livecookie = c->running;
562
563   /* now, we have parsed the incoming line, we must now check if this
564      superceeds an already existing cookie, which it may if the previous have
565      the same domain and path as this */
566
567   clist = c->cookies;
568   replace_old = FALSE;
569   while(clist) {
570     if(strequal(clist->name, co->name)) {
571       /* the names are identical */
572
573       if(clist->domain && co->domain) {
574         if(strequal(clist->domain, co->domain))
575           /* The domains are identical */
576           replace_old=TRUE;
577       }
578       else if(!clist->domain && !co->domain)
579         replace_old = TRUE;
580
581       if(replace_old) {
582         /* the domains were identical */
583
584         if(clist->path && co->path) {
585           if(strequal(clist->path, co->path)) {
586             replace_old = TRUE;
587           }
588           else
589             replace_old = FALSE;
590         }
591         else if(!clist->path && !co->path)
592           replace_old = TRUE;
593         else
594           replace_old = FALSE;
595
596       }
597
598       if(replace_old && !co->livecookie && clist->livecookie) {
599         /* Both cookies matched fine, except that the already present
600            cookie is "live", which means it was set from a header, while
601            the new one isn't "live" and thus only read from a file. We let
602            live cookies stay alive */
603
604         /* Free the newcomer and get out of here! */
605         freecookie(co);
606         return NULL;
607       }
608
609       if(replace_old) {
610         co->next = clist->next; /* get the next-pointer first */
611
612         /* then free all the old pointers */
613         if(clist->name)
614           free(clist->name);
615         if(clist->value)
616           free(clist->value);
617         if(clist->domain)
618           free(clist->domain);
619         if(clist->path)
620           free(clist->path);
621         if(clist->expirestr)
622           free(clist->expirestr);
623
624         if(clist->version)
625           free(clist->version);
626         if(clist->maxage)
627           free(clist->maxage);
628
629         *clist = *co;  /* then store all the new data */
630
631         free(co);   /* free the newly alloced memory */
632         co = clist; /* point to the previous struct instead */
633
634         /* We have replaced a cookie, now skip the rest of the list but
635            make sure the 'lastc' pointer is properly set */
636         do {
637           lastc = clist;
638           clist = clist->next;
639         } while(clist);
640         break;
641       }
642     }
643     lastc = clist;
644     clist = clist->next;
645   }
646
647   if(c->running)
648     /* Only show this when NOT reading the cookies from a file */
649     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, expire %d\n",
650           replace_old?"Replaced":"Added", co->name, co->value,
651           co->domain, co->path, co->expires);
652
653   if(!replace_old) {
654     /* then make the last item point on this new one */
655     if(lastc)
656       lastc->next = co;
657     else
658       c->cookies = co;
659   }
660
661   c->numcookies++; /* one more cookie in the jar */
662   return co;
663 }
664
665 /*****************************************************************************
666  *
667  * Curl_cookie_init()
668  *
669  * Inits a cookie struct to read data from a local file. This is always
670  * called before any cookies are set. File may be NULL.
671  *
672  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
673  *
674  ****************************************************************************/
675 struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
676                                     const char *file,
677                                     struct CookieInfo *inc,
678                                     bool newsession)
679 {
680   struct CookieInfo *c;
681   FILE *fp;
682   bool fromfile=TRUE;
683
684   if(NULL == inc) {
685     /* we didn't get a struct, create one */
686     c = (struct CookieInfo *)calloc(1, sizeof(struct CookieInfo));
687     if(!c)
688       return NULL; /* failed to get memory */
689     c->filename = strdup(file?file:"none"); /* copy the name just in case */
690   }
691   else {
692     /* we got an already existing one, use that */
693     c = inc;
694   }
695   c->running = FALSE; /* this is not running, this is init */
696
697   if(file && strequal(file, "-")) {
698     fp = stdin;
699     fromfile=FALSE;
700   }
701   else if(file && !*file) {
702     /* points to a "" string */
703     fp = NULL;
704   }
705   else
706     fp = file?fopen(file, "r"):NULL;
707
708   c->newsession = newsession; /* new session? */
709
710   if(fp) {
711     char *lineptr;
712     bool headerline;
713
714     char *line = (char *)malloc(MAX_COOKIE_LINE);
715     if(line) {
716       while(fgets(line, MAX_COOKIE_LINE, fp)) {
717         if(checkprefix("Set-Cookie:", line)) {
718           /* This is a cookie line, get it! */
719           lineptr=&line[11];
720           headerline=TRUE;
721         }
722         else {
723           lineptr=line;
724           headerline=FALSE;
725         }
726         while(*lineptr && ISBLANK(*lineptr))
727           lineptr++;
728
729         Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
730       }
731       free(line); /* free the line buffer */
732     }
733     if(fromfile)
734       fclose(fp);
735   }
736
737   c->running = TRUE;          /* now, we're running */
738
739   return c;
740 }
741
742 /*****************************************************************************
743  *
744  * Curl_cookie_getlist()
745  *
746  * For a given host and path, return a linked list of cookies that the
747  * client should send to the server if used now. The secure boolean informs
748  * the cookie if a secure connection is achieved or not.
749  *
750  * It shall only return cookies that haven't expired.
751  *
752  ****************************************************************************/
753
754 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
755                                    const char *host, const char *path,
756                                    bool secure)
757 {
758   struct Cookie *newco;
759   struct Cookie *co;
760   time_t now = time(NULL);
761   struct Cookie *mainco=NULL;
762
763   if(!c || !c->cookies)
764     return NULL; /* no cookie struct or no cookies in the struct */
765
766   co = c->cookies;
767
768   while(co) {
769     /* only process this cookie if it is not expired or had no expire
770        date AND that if the cookie requires we're secure we must only
771        continue if we are! */
772     if( (co->expires<=0 || (co->expires> now)) &&
773         (co->secure?secure:TRUE) ) {
774
775       /* now check if the domain is correct */
776       if(!co->domain ||
777          (co->tailmatch && tailmatch(co->domain, host)) ||
778          (!co->tailmatch && strequal(host, co->domain)) ) {
779         /* the right part of the host matches the domain stuff in the
780            cookie data */
781
782         /* now check the left part of the path with the cookies path
783            requirement */
784         if(!co->path ||
785            /* not using checkprefix() because matching should be
786               case-sensitive */
787            !strncmp(co->path, path, strlen(co->path)) ) {
788
789           /* and now, we know this is a match and we should create an
790              entry for the return-linked-list */
791
792           newco = (struct Cookie *)malloc(sizeof(struct Cookie));
793           if(newco) {
794             /* first, copy the whole source cookie: */
795             memcpy(newco, co, sizeof(struct Cookie));
796
797             /* then modify our next */
798             newco->next = mainco;
799
800             /* point the main to us */
801             mainco = newco;
802           }
803           else {
804             /* failure, clear up the allocated chain and return NULL */
805             while(mainco) {
806               co = mainco->next;
807               free(mainco);
808               mainco = co;
809             }
810
811             return NULL;
812           }
813         }
814       }
815     }
816     co = co->next;
817   }
818
819   return mainco; /* return the new list */
820 }
821
822 /*****************************************************************************
823  *
824  * Curl_cookie_clearall()
825  *
826  * Clear all existing cookies and reset the counter.
827  *
828  ****************************************************************************/
829 void Curl_cookie_clearall(struct CookieInfo *cookies)
830 {
831   if(cookies) {
832     Curl_cookie_freelist(cookies->cookies, TRUE);
833     cookies->cookies = NULL;
834     cookies->numcookies = 0;
835   }
836 }
837
838 /*****************************************************************************
839  *
840  * Curl_cookie_freelist()
841  *
842  * Free a list of cookies previously returned by Curl_cookie_getlist();
843  *
844  * The 'cookiestoo' argument tells this function whether to just free the
845  * list or actually also free all cookies within the list as well.
846  *
847  ****************************************************************************/
848
849 void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
850 {
851   struct Cookie *next;
852   if(co) {
853     while(co) {
854       next = co->next;
855       if(cookiestoo)
856         freecookie(co);
857       else
858         free(co); /* we only free the struct since the "members" are all just
859                      pointed out in the main cookie list! */
860       co = next;
861     }
862   }
863 }
864
865
866 /*****************************************************************************
867  *
868  * Curl_cookie_clearsess()
869  *
870  * Free all session cookies in the cookies list.
871  *
872  ****************************************************************************/
873 void Curl_cookie_clearsess(struct CookieInfo *cookies)
874 {
875   struct Cookie *first, *curr, *next, *prev = NULL;
876
877   if(!cookies->cookies)
878     return;
879
880   first = curr = prev = cookies->cookies;
881
882   for(; curr; curr = next) {
883     next = curr->next;
884     if(!curr->expires) {
885       if(first == curr)
886         first = next;
887
888       if(prev == curr)
889         prev = next;
890       else
891         prev->next = next;
892
893       freecookie(curr);
894       cookies->numcookies--;
895     }
896     else
897       prev = curr;
898   }
899
900   cookies->cookies = first;
901 }
902
903
904 /*****************************************************************************
905  *
906  * Curl_cookie_cleanup()
907  *
908  * Free a "cookie object" previous created with cookie_init().
909  *
910  ****************************************************************************/
911 void Curl_cookie_cleanup(struct CookieInfo *c)
912 {
913   struct Cookie *co;
914   struct Cookie *next;
915   if(c) {
916     if(c->filename)
917       free(c->filename);
918     co = c->cookies;
919
920     while(co) {
921       next = co->next;
922       freecookie(co);
923       co = next;
924     }
925     free(c); /* free the base struct as well */
926   }
927 }
928
929 /* get_netscape_format()
930  *
931  * Formats a string for Netscape output file, w/o a newline at the end.
932  *
933  * Function returns a char * to a formatted line. Has to be free()d
934 */
935 static char *get_netscape_format(const struct Cookie *co)
936 {
937   return aprintf(
938     "%s"     /* httponly preamble */
939     "%s%s\t" /* domain */
940     "%s\t"   /* tailmatch */
941     "%s\t"   /* path */
942     "%s\t"   /* secure */
943     "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
944     "%s\t"   /* name */
945     "%s",    /* value */
946     co->httponly?"#HttpOnly_":"",
947     /* Make sure all domains are prefixed with a dot if they allow
948        tailmatching. This is Mozilla-style. */
949     (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
950     co->domain?co->domain:"unknown",
951     co->tailmatch?"TRUE":"FALSE",
952     co->path?co->path:"/",
953     co->secure?"TRUE":"FALSE",
954     co->expires,
955     co->name,
956     co->value?co->value:"");
957 }
958
959 /*
960  * Curl_cookie_output()
961  *
962  * Writes all internally known cookies to the specified file. Specify
963  * "-" as file name to write to stdout.
964  *
965  * The function returns non-zero on write failure.
966  */
967 int Curl_cookie_output(struct CookieInfo *c, const char *dumphere)
968 {
969   struct Cookie *co;
970   FILE *out;
971   bool use_stdout=FALSE;
972
973   if((NULL == c) || (0 == c->numcookies))
974     /* If there are no known cookies, we don't write or even create any
975        destination file */
976     return 0;
977
978   if(strequal("-", dumphere)) {
979     /* use stdout */
980     out = stdout;
981     use_stdout=TRUE;
982   }
983   else {
984     out = fopen(dumphere, "w");
985     if(!out)
986       return 1; /* failure */
987   }
988
989   if(c) {
990     char *format_ptr;
991
992     fputs("# Netscape HTTP Cookie File\n"
993           "# http://curlm.haxx.se/rfc/cookie_spec.html\n"
994           "# This file was generated by libcurl! Edit at your own risk.\n\n",
995           out);
996     co = c->cookies;
997
998     while(co) {
999       format_ptr = get_netscape_format(co);
1000       if(format_ptr == NULL) {
1001         fprintf(out, "#\n# Fatal libcurl error\n");
1002         fclose(out);
1003         return 1;
1004       }
1005       fprintf(out, "%s\n", format_ptr);
1006       free(format_ptr);
1007       co=co->next;
1008     }
1009   }
1010
1011   if(!use_stdout)
1012     fclose(out);
1013
1014   return 0;
1015 }
1016
1017 struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1018 {
1019   struct curl_slist *list = NULL;
1020   struct curl_slist *beg;
1021   struct Cookie *c;
1022   char *line;
1023
1024   if((data->cookies == NULL) ||
1025       (data->cookies->numcookies == 0))
1026     return NULL;
1027
1028   c = data->cookies->cookies;
1029
1030   beg = list;
1031   while(c) {
1032     /* fill the list with _all_ the cookies we know */
1033     line = get_netscape_format(c);
1034     if(line == NULL) {
1035       curl_slist_free_all(beg);
1036       return NULL;
1037     }
1038     list = curl_slist_append(list, line);
1039     free(line);
1040     if(list == NULL) {
1041       curl_slist_free_all(beg);
1042       return NULL;
1043     }
1044     else if(beg == NULL) {
1045       beg = list;
1046     }
1047     c = c->next;
1048   }
1049
1050   return list;
1051 }
1052
1053 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */