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
26RECEIVING COOKIE INFORMATION
27============================
28
29struct 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
34int 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
47SENDING COOKIE INFORMATION
48==========================
49
50struct 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
61Example 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
104static void freecookie(struct Cookie *co)
105{
106  if(co->expirestr)
107    free(co->expirestr);
108  if(co->domain)
109    free(co->domain);
110  if(co->path)
111    free(co->path);
112  if(co->name)
113    free(co->name);
114  if(co->value)
115    free(co->value);
116  if(co->maxage)
117    free(co->maxage);
118  if(co->version)
119    free(co->version);
120
121  free(co);
122}
123
124static bool tailmatch(const char *little, const char *bigone)
125{
126  size_t littlelen = strlen(little);
127  size_t biglen = strlen(bigone);
128
129  if(littlelen > biglen)
130    return FALSE;
131
132  return (bool)Curl_raw_equal(little, bigone+biglen-littlelen);
133}
134
135/*
136 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
137 */
138void Curl_cookie_loadfiles(struct SessionHandle *data)
139{
140  struct curl_slist *list = data->change.cookielist;
141  if(list) {
142    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
143    while(list) {
144      data->cookies = Curl_cookie_init(data,
145                                       list->data,
146                                       data->cookies,
147                                       data->set.cookiesession);
148      list = list->next;
149    }
150    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
151    curl_slist_free_all(data->change.cookielist); /* clean up list */
152    data->change.cookielist = NULL; /* don't do this again! */
153  }
154}
155
156/*
157 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
158 * that will be freed before the allocated string is stored there.
159 *
160 * It is meant to easily replace strdup()
161 */
162static void strstore(char **str, const char *newstr)
163{
164  if(*str)
165    free(*str);
166  *str = strdup(newstr);
167}
168
169
170/****************************************************************************
171 *
172 * Curl_cookie_add()
173 *
174 * Add a single cookie line to the cookie keeping object.
175 *
176 ***************************************************************************/
177
178struct Cookie *
179Curl_cookie_add(struct SessionHandle *data,
180                /* The 'data' pointer here may be NULL at times, and thus
181                   must only be used very carefully for things that can deal
182                   with data being NULL. Such as infof() and similar */
183
184                struct CookieInfo *c,
185                bool httpheader, /* TRUE if HTTP header-style line */
186                char *lineptr,   /* first character of the line */
187                const char *domain, /* default domain */
188                const char *path)   /* full path used when this cookie is set,
189                                       used to get default path for the cookie
190                                       unless set */
191{
192  struct Cookie *clist;
193  char name[MAX_NAME];
194  struct Cookie *co;
195  struct Cookie *lastc=NULL;
196  time_t now = time(NULL);
197  bool replace_old = FALSE;
198  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
199
200#ifdef CURL_DISABLE_VERBOSE_STRINGS
201  (void)data;
202#endif
203
204  /* First, alloc and init a new struct for it */
205  co = calloc(1, sizeof(struct Cookie));
206  if(!co)
207    return NULL; /* bail out if we're this low on memory */
208
209  if(httpheader) {
210    /* This line was read off a HTTP-header */
211    const char *ptr;
212    const char *sep;
213    const char *semiptr;
214    char *what;
215
216    what = malloc(MAX_COOKIE_LINE);
217    if(!what) {
218      free(co);
219      return NULL;
220    }
221
222    semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
223
224    while(*lineptr && ISBLANK(*lineptr))
225      lineptr++;
226
227    ptr = lineptr;
228    do {
229      /* we have a <what>=<this> pair or a 'secure' word here */
230      sep = strchr(ptr, '=');
231      if(sep && (!semiptr || (semiptr>sep)) ) {
232        /*
233         * There is a = sign and if there was a semicolon too, which make sure
234         * that the semicolon comes _after_ the equal sign.
235         */
236
237        name[0]=what[0]=0; /* init the buffers */
238        if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;=]=%"
239                       MAX_COOKIE_LINE_TXT "[^;\r\n]",
240                       name, what)) {
241          /* this is a <name>=<what> pair. We use strstore() below to properly
242             deal with received cookie headers that have the same string
243             property set more than once, and then we use the last one. */
244
245          const char *whatptr;
246
247          /* Strip off trailing whitespace from the 'what' */
248          size_t len=strlen(what);
249          while(len && ISBLANK(what[len-1])) {
250            what[len-1]=0;
251            len--;
252          }
253
254          /* Skip leading whitespace from the 'what' */
255          whatptr=what;
256          while(*whatptr && ISBLANK(*whatptr)) {
257            whatptr++;
258          }
259
260          if(Curl_raw_equal("path", name)) {
261            strstore(&co->path, whatptr);
262            if(!co->path) {
263              badcookie = TRUE; /* out of memory bad */
264              break;
265            }
266          }
267          else if(Curl_raw_equal("domain", name)) {
268            /* note that this name may or may not have a preceding dot, but
269               we don't care about that, we treat the names the same anyway */
270
271            const char *domptr=whatptr;
272            const char *nextptr;
273            int dotcount=1;
274
275            /* Count the dots, we need to make sure that there are enough
276               of them. */
277
278            if('.' == whatptr[0])
279              /* don't count the initial dot, assume it */
280              domptr++;
281
282            do {
283              nextptr = strchr(domptr, '.');
284              if(nextptr) {
285                if(domptr != nextptr)
286                  dotcount++;
287                domptr = nextptr+1;
288              }
289            } while(nextptr);
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 preceding 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              strtol((*co->maxage=='\"')?&co->maxage[1]:&co->maxage[0],NULL,10)
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 preceded 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 preceding 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");
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, "
685          "expire %" FORMAT_OFF_T "\n",
686          replace_old?"Replaced":"Added", co->name, co->value,
687          co->domain, co->path, co->expires);
688
689  if(!replace_old) {
690    /* then make the last item point on this new one */
691    if(lastc)
692      lastc->next = co;
693    else
694      c->cookies = co;
695  }
696
697  c->numcookies++; /* one more cookie in the jar */
698  return co;
699}
700
701/*****************************************************************************
702 *
703 * Curl_cookie_init()
704 *
705 * Inits a cookie struct to read data from a local file. This is always
706 * called before any cookies are set. File may be NULL.
707 *
708 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
709 *
710 ****************************************************************************/
711struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
712                                    const char *file,
713                                    struct CookieInfo *inc,
714                                    bool newsession)
715{
716  struct CookieInfo *c;
717  FILE *fp;
718  bool fromfile=TRUE;
719
720  if(NULL == inc) {
721    /* we didn't get a struct, create one */
722    c = calloc(1, sizeof(struct CookieInfo));
723    if(!c)
724      return NULL; /* failed to get memory */
725    c->filename = strdup(file?file:"none"); /* copy the name just in case */
726  }
727  else {
728    /* we got an already existing one, use that */
729    c = inc;
730  }
731  c->running = FALSE; /* this is not running, this is init */
732
733  if(file && strequal(file, "-")) {
734    fp = stdin;
735    fromfile=FALSE;
736  }
737  else if(file && !*file) {
738    /* points to a "" string */
739    fp = NULL;
740  }
741  else
742    fp = file?fopen(file, "r"):NULL;
743
744  c->newsession = newsession; /* new session? */
745
746  if(fp) {
747    char *lineptr;
748    bool headerline;
749
750    char *line = malloc(MAX_COOKIE_LINE);
751    if(line) {
752      while(fgets(line, MAX_COOKIE_LINE, fp)) {
753        if(checkprefix("Set-Cookie:", line)) {
754          /* This is a cookie line, get it! */
755          lineptr=&line[11];
756          headerline=TRUE;
757        }
758        else {
759          lineptr=line;
760          headerline=FALSE;
761        }
762        while(*lineptr && ISBLANK(*lineptr))
763          lineptr++;
764
765        Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
766      }
767      free(line); /* free the line buffer */
768    }
769    if(fromfile)
770      fclose(fp);
771  }
772
773  c->running = TRUE;          /* now, we're running */
774
775  return c;
776}
777
778/* sort this so that the longest path gets before the shorter path */
779static int cookie_sort(const void *p1, const void *p2)
780{
781  struct Cookie *c1 = *(struct Cookie **)p1;
782  struct Cookie *c2 = *(struct Cookie **)p2;
783
784  size_t l1 = c1->path?strlen(c1->path):0;
785  size_t l2 = c2->path?strlen(c2->path):0;
786
787  return (l2 > l1) ? 1 : (l2 < l1) ? -1 : 0 ;
788}
789
790/*****************************************************************************
791 *
792 * Curl_cookie_getlist()
793 *
794 * For a given host and path, return a linked list of cookies that the
795 * client should send to the server if used now. The secure boolean informs
796 * the cookie if a secure connection is achieved or not.
797 *
798 * It shall only return cookies that haven't expired.
799 *
800 ****************************************************************************/
801
802struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
803                                   const char *host, const char *path,
804                                   bool secure)
805{
806  struct Cookie *newco;
807  struct Cookie *co;
808  time_t now = time(NULL);
809  struct Cookie *mainco=NULL;
810  size_t matches = 0;
811
812  if(!c || !c->cookies)
813    return NULL; /* no cookie struct or no cookies in the struct */
814
815  co = c->cookies;
816
817  while(co) {
818    /* only process this cookie if it is not expired or had no expire
819       date AND that if the cookie requires we're secure we must only
820       continue if we are! */
821    if((!co->expires || (co->expires > now)) &&
822       (co->secure?secure:TRUE)) {
823
824      /* now check if the domain is correct */
825      if(!co->domain ||
826         (co->tailmatch && tailmatch(co->domain, host)) ||
827         (!co->tailmatch && Curl_raw_equal(host, co->domain)) ) {
828        /* the right part of the host matches the domain stuff in the
829           cookie data */
830
831        /* now check the left part of the path with the cookies path
832           requirement */
833        if(!co->path ||
834           /* not using checkprefix() because matching should be
835              case-sensitive */
836           !strncmp(co->path, path, strlen(co->path)) ) {
837
838          /* and now, we know this is a match and we should create an
839             entry for the return-linked-list */
840
841          newco = malloc(sizeof(struct Cookie));
842          if(newco) {
843            /* first, copy the whole source cookie: */
844            memcpy(newco, co, sizeof(struct Cookie));
845
846            /* then modify our next */
847            newco->next = mainco;
848
849            /* point the main to us */
850            mainco = newco;
851
852            matches++;
853          }
854          else {
855            fail:
856            /* failure, clear up the allocated chain and return NULL */
857            while(mainco) {
858              co = mainco->next;
859              free(mainco);
860              mainco = co;
861            }
862
863            return NULL;
864          }
865        }
866      }
867    }
868    co = co->next;
869  }
870
871  if(matches) {
872    /* Now we need to make sure that if there is a name appearing more than
873       once, the longest specified path version comes first. To make this
874       the swiftest way, we just sort them all based on path length. */
875    struct Cookie **array;
876    size_t i;
877
878    /* alloc an array and store all cookie pointers */
879    array = malloc(sizeof(struct Cookie *) * matches);
880    if(!array)
881      goto fail;
882
883    co = mainco;
884
885    for(i=0; co; co = co->next)
886      array[i++] = co;
887
888    /* now sort the cookie pointers in path lenth order */
889    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
890
891    /* remake the linked list order according to the new order */
892
893    mainco = array[0]; /* start here */
894    for(i=0; i<matches-1; i++)
895      array[i]->next = array[i+1];
896    array[matches-1]->next = NULL; /* terminate the list */
897
898    free(array); /* remove the temporary data again */
899  }
900
901  return mainco; /* return the new list */
902}
903
904/*****************************************************************************
905 *
906 * Curl_cookie_clearall()
907 *
908 * Clear all existing cookies and reset the counter.
909 *
910 ****************************************************************************/
911void Curl_cookie_clearall(struct CookieInfo *cookies)
912{
913  if(cookies) {
914    Curl_cookie_freelist(cookies->cookies, TRUE);
915    cookies->cookies = NULL;
916    cookies->numcookies = 0;
917  }
918}
919
920/*****************************************************************************
921 *
922 * Curl_cookie_freelist()
923 *
924 * Free a list of cookies previously returned by Curl_cookie_getlist();
925 *
926 * The 'cookiestoo' argument tells this function whether to just free the
927 * list or actually also free all cookies within the list as well.
928 *
929 ****************************************************************************/
930
931void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
932{
933  struct Cookie *next;
934  if(co) {
935    while(co) {
936      next = co->next;
937      if(cookiestoo)
938        freecookie(co);
939      else
940        free(co); /* we only free the struct since the "members" are all just
941                     pointed out in the main cookie list! */
942      co = next;
943    }
944  }
945}
946
947
948/*****************************************************************************
949 *
950 * Curl_cookie_clearsess()
951 *
952 * Free all session cookies in the cookies list.
953 *
954 ****************************************************************************/
955void Curl_cookie_clearsess(struct CookieInfo *cookies)
956{
957  struct Cookie *first, *curr, *next, *prev = NULL;
958
959  if(!cookies || !cookies->cookies)
960    return;
961
962  first = curr = prev = cookies->cookies;
963
964  for(; curr; curr = next) {
965    next = curr->next;
966    if(!curr->expires) {
967      if(first == curr)
968        first = next;
969
970      if(prev == curr)
971        prev = next;
972      else
973        prev->next = next;
974
975      freecookie(curr);
976      cookies->numcookies--;
977    }
978    else
979      prev = curr;
980  }
981
982  cookies->cookies = first;
983}
984
985
986/*****************************************************************************
987 *
988 * Curl_cookie_cleanup()
989 *
990 * Free a "cookie object" previous created with cookie_init().
991 *
992 ****************************************************************************/
993void Curl_cookie_cleanup(struct CookieInfo *c)
994{
995  struct Cookie *co;
996  struct Cookie *next;
997  if(c) {
998    if(c->filename)
999      free(c->filename);
1000    co = c->cookies;
1001
1002    while(co) {
1003      next = co->next;
1004      freecookie(co);
1005      co = next;
1006    }
1007    free(c); /* free the base struct as well */
1008  }
1009}
1010
1011/* get_netscape_format()
1012 *
1013 * Formats a string for Netscape output file, w/o a newline at the end.
1014 *
1015 * Function returns a char * to a formatted line. Has to be free()d
1016*/
1017static char *get_netscape_format(const struct Cookie *co)
1018{
1019  return aprintf(
1020    "%s"     /* httponly preamble */
1021    "%s%s\t" /* domain */
1022    "%s\t"   /* tailmatch */
1023    "%s\t"   /* path */
1024    "%s\t"   /* secure */
1025    "%" FORMAT_OFF_T "\t"   /* expires */
1026    "%s\t"   /* name */
1027    "%s",    /* value */
1028    co->httponly?"#HttpOnly_":"",
1029    /* Make sure all domains are prefixed with a dot if they allow
1030       tailmatching. This is Mozilla-style. */
1031    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
1032    co->domain?co->domain:"unknown",
1033    co->tailmatch?"TRUE":"FALSE",
1034    co->path?co->path:"/",
1035    co->secure?"TRUE":"FALSE",
1036    co->expires,
1037    co->name,
1038    co->value?co->value:"");
1039}
1040
1041/*
1042 * cookie_output()
1043 *
1044 * Writes all internally known cookies to the specified file. Specify
1045 * "-" as file name to write to stdout.
1046 *
1047 * The function returns non-zero on write failure.
1048 */
1049static int cookie_output(struct CookieInfo *c, const char *dumphere)
1050{
1051  struct Cookie *co;
1052  FILE *out;
1053  bool use_stdout=FALSE;
1054
1055  if((NULL == c) || (0 == c->numcookies))
1056    /* If there are no known cookies, we don't write or even create any
1057       destination file */
1058    return 0;
1059
1060  if(strequal("-", dumphere)) {
1061    /* use stdout */
1062    out = stdout;
1063    use_stdout=TRUE;
1064  }
1065  else {
1066    out = fopen(dumphere, "w");
1067    if(!out)
1068      return 1; /* failure */
1069  }
1070
1071  if(c) {
1072    char *format_ptr;
1073
1074    fputs("# Netscape HTTP Cookie File\n"
1075          "# http://curl.haxx.se/rfc/cookie_spec.html\n"
1076          "# This file was generated by libcurl! Edit at your own risk.\n\n",
1077          out);
1078    co = c->cookies;
1079
1080    while(co) {
1081      format_ptr = get_netscape_format(co);
1082      if(format_ptr == NULL) {
1083        fprintf(out, "#\n# Fatal libcurl error\n");
1084        if(!use_stdout)
1085          fclose(out);
1086        return 1;
1087      }
1088      fprintf(out, "%s\n", format_ptr);
1089      free(format_ptr);
1090      co=co->next;
1091    }
1092  }
1093
1094  if(!use_stdout)
1095    fclose(out);
1096
1097  return 0;
1098}
1099
1100struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
1101{
1102  struct curl_slist *list = NULL;
1103  struct curl_slist *beg;
1104  struct Cookie *c;
1105  char *line;
1106
1107  if((data->cookies == NULL) ||
1108      (data->cookies->numcookies == 0))
1109    return NULL;
1110
1111  c = data->cookies->cookies;
1112
1113  beg = list;
1114  while(c) {
1115    /* fill the list with _all_ the cookies we know */
1116    line = get_netscape_format(c);
1117    if(line == NULL) {
1118      curl_slist_free_all(beg);
1119      return NULL;
1120    }
1121    list = curl_slist_append(list, line);
1122    free(line);
1123    if(list == NULL) {
1124      curl_slist_free_all(beg);
1125      return NULL;
1126    }
1127    else if(beg == NULL) {
1128      beg = list;
1129    }
1130    c = c->next;
1131  }
1132
1133  return list;
1134}
1135
1136void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
1137{
1138  if(data->set.str[STRING_COOKIEJAR]) {
1139    if(data->change.cookielist) {
1140      /* If there is a list of cookie files to read, do it first so that
1141         we have all the told files read before we write the new jar.
1142         Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
1143      Curl_cookie_loadfiles(data);
1144    }
1145
1146    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1147
1148    /* if we have a destination file for all the cookies to get dumped to */
1149    if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
1150      infof(data, "WARNING: failed to save cookies in %s\n",
1151            data->set.str[STRING_COOKIEJAR]);
1152  }
1153  else {
1154    if(cleanup && data->change.cookielist)
1155      /* since nothing is written, we can just free the list of cookie file
1156         names */
1157      curl_slist_free_all(data->change.cookielist); /* clean up list */
1158    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
1159  }
1160
1161  if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
1162    Curl_cookie_cleanup(data->cookies);
1163  }
1164  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
1165}
1166
1167#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
1168