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#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
101static 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
121static 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 */
135void 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_slist_free_all(data->change.cookielist); /* clean up list */
148    data->change.cookielist = NULL; /* don't do this again! */
149    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
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 */
159static 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
175struct Cookie *
176Curl_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 ****************************************************************************/
708struct 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 */
776static 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
799struct 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 ****************************************************************************/
908void 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
928void 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 ****************************************************************************/
952void 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 ****************************************************************************/
990void 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*/
1014static 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 */
1046static 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
1097struct 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
1130void 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 */
1164