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