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