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