git-svn-id: svn://10.65.10.50/branches/R_10_00@23289 c028cbd2-c16b-5b4b-a496-9718f37d4682
		
			
				
	
	
		
			1366 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1366 lines
		
	
	
		
			38 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/***************************************************************************
 | 
						||
 *                                  _   _ ____  _
 | 
						||
 *  Project                     ___| | | |  _ \| |
 | 
						||
 *                             / __| | | | |_) | |
 | 
						||
 *                            | (__| |_| |  _ <| |___
 | 
						||
 *                             \___|\___/|_| \_\_____|
 | 
						||
 *
 | 
						||
 * Copyright (C) 1998 - 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
 | 
						||
 *
 | 
						||
 * This software is licensed as described in the file COPYING, which
 | 
						||
 * you should have received as part of this distribution. The terms
 | 
						||
 * are also available at http://curl.haxx.se/docs/copyright.html.
 | 
						||
 *
 | 
						||
 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
 | 
						||
 * copies of the Software, and permit persons to whom the Software is
 | 
						||
 * furnished to do so, under the terms of the COPYING file.
 | 
						||
 *
 | 
						||
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 | 
						||
 * KIND, either express or implied.
 | 
						||
 *
 | 
						||
 ***************************************************************************/
 | 
						||
 | 
						||
/***
 | 
						||
 | 
						||
 | 
						||
RECEIVING COOKIE INFORMATION
 | 
						||
============================
 | 
						||
 | 
						||
struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
 | 
						||
                    const char *file, struct CookieInfo *inc, bool newsession);
 | 
						||
 | 
						||
        Inits a cookie struct to store data in a local file. This is always
 | 
						||
        called before any cookies are set.
 | 
						||
 | 
						||
struct Cookie *Curl_cookie_add(struct SessionHandle *data,
 | 
						||
                 struct CookieInfo *c, bool httpheader, char *lineptr,
 | 
						||
                 const char *domain, const char *path);
 | 
						||
 | 
						||
        The 'lineptr' parameter is a full "Set-cookie:" line as
 | 
						||
        received from a server.
 | 
						||
 | 
						||
        The function need to replace previously stored lines that this new
 | 
						||
        line superceeds.
 | 
						||
 | 
						||
        It may remove lines that are expired.
 | 
						||
 | 
						||
        It should return an indication of success/error.
 | 
						||
 | 
						||
 | 
						||
SENDING COOKIE INFORMATION
 | 
						||
==========================
 | 
						||
 | 
						||
struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
 | 
						||
                                    char *host, char *path, bool secure);
 | 
						||
 | 
						||
        For a given host and path, return a linked list of cookies that
 | 
						||
        the client should send to the server if used now. The secure
 | 
						||
        boolean informs the cookie if a secure connection is achieved or
 | 
						||
        not.
 | 
						||
 | 
						||
        It shall only return cookies that haven't expired.
 | 
						||
 | 
						||
 | 
						||
Example set of cookies:
 | 
						||
 | 
						||
    Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
 | 
						||
    Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
 | 
						||
    domain=.fidelity.com; path=/ftgw; secure
 | 
						||
    Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
 | 
						||
    domain=.fidelity.com; path=/; secure
 | 
						||
    Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
 | 
						||
    domain=.fidelity.com; path=/; secure
 | 
						||
    Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
 | 
						||
    domain=.fidelity.com; path=/; secure
 | 
						||
    Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
 | 
						||
    domain=.fidelity.com; path=/; secure
 | 
						||
    Set-cookie:
 | 
						||
    Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
 | 
						||
    13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
 | 
						||
****/
 | 
						||
 | 
						||
 | 
						||
#include "curl_setup.h"
 | 
						||
 | 
						||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
 | 
						||
 | 
						||
#include "curl_printf.h"
 | 
						||
#include "urldata.h"
 | 
						||
#include "cookie.h"
 | 
						||
#include "strequal.h"
 | 
						||
#include "strtok.h"
 | 
						||
#include "sendf.h"
 | 
						||
#include "slist.h"
 | 
						||
#include "share.h"
 | 
						||
#include "strtoofft.h"
 | 
						||
#include "rawstr.h"
 | 
						||
#include "curl_memrchr.h"
 | 
						||
#include "inet_pton.h"
 | 
						||
 | 
						||
/* The last #include files should be: */
 | 
						||
#include "curl_memory.h"
 | 
						||
#include "memdebug.h"
 | 
						||
 | 
						||
static void freecookie(struct Cookie *co)
 | 
						||
{
 | 
						||
  free(co->expirestr);
 | 
						||
  free(co->domain);
 | 
						||
  free(co->path);
 | 
						||
  free(co->spath);
 | 
						||
  free(co->name);
 | 
						||
  free(co->value);
 | 
						||
  free(co->maxage);
 | 
						||
  free(co->version);
 | 
						||
  free(co);
 | 
						||
}
 | 
						||
 | 
						||
static bool tailmatch(const char *cooke_domain, const char *hostname)
 | 
						||
{
 | 
						||
  size_t cookie_domain_len = strlen(cooke_domain);
 | 
						||
  size_t hostname_len = strlen(hostname);
 | 
						||
 | 
						||
  if(hostname_len < cookie_domain_len)
 | 
						||
    return FALSE;
 | 
						||
 | 
						||
  if(!Curl_raw_equal(cooke_domain, hostname+hostname_len-cookie_domain_len))
 | 
						||
    return FALSE;
 | 
						||
 | 
						||
  /* A lead char of cookie_domain is not '.'.
 | 
						||
     RFC6265 4.1.2.3. The Domain Attribute says:
 | 
						||
       For example, if the value of the Domain attribute is
 | 
						||
       "example.com", the user agent will include the cookie in the Cookie
 | 
						||
       header when making HTTP requests to example.com, www.example.com, and
 | 
						||
       www.corp.example.com.
 | 
						||
   */
 | 
						||
  if(hostname_len == cookie_domain_len)
 | 
						||
    return TRUE;
 | 
						||
  if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
 | 
						||
    return TRUE;
 | 
						||
  return FALSE;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * matching cookie path and url path
 | 
						||
 * RFC6265 5.1.4 Paths and Path-Match
 | 
						||
 */
 | 
						||
static bool pathmatch(const char* cookie_path, const char* request_uri)
 | 
						||
{
 | 
						||
  size_t cookie_path_len;
 | 
						||
  size_t uri_path_len;
 | 
						||
  char* uri_path = NULL;
 | 
						||
  char* pos;
 | 
						||
  bool ret = FALSE;
 | 
						||
 | 
						||
  /* cookie_path must not have last '/' separator. ex: /sample */
 | 
						||
  cookie_path_len = strlen(cookie_path);
 | 
						||
  if(1 == cookie_path_len) {
 | 
						||
    /* cookie_path must be '/' */
 | 
						||
    return TRUE;
 | 
						||
  }
 | 
						||
 | 
						||
  uri_path = strdup(request_uri);
 | 
						||
  if(!uri_path)
 | 
						||
    return FALSE;
 | 
						||
  pos = strchr(uri_path, '?');
 | 
						||
  if(pos)
 | 
						||
    *pos = 0x0;
 | 
						||
 | 
						||
  /* #-fragments are already cut off! */
 | 
						||
  if(0 == strlen(uri_path) || uri_path[0] != '/') {
 | 
						||
    free(uri_path);
 | 
						||
    uri_path = strdup("/");
 | 
						||
    if(!uri_path)
 | 
						||
      return FALSE;
 | 
						||
  }
 | 
						||
 | 
						||
  /* here, RFC6265 5.1.4 says
 | 
						||
     4. Output the characters of the uri-path from the first character up
 | 
						||
        to, but not including, the right-most %x2F ("/").
 | 
						||
     but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
 | 
						||
     without redirect.
 | 
						||
     Ignore this algorithm because /hoge is uri path for this case
 | 
						||
     (uri path is not /).
 | 
						||
   */
 | 
						||
 | 
						||
  uri_path_len = strlen(uri_path);
 | 
						||
 | 
						||
  if(uri_path_len < cookie_path_len) {
 | 
						||
    ret = FALSE;
 | 
						||
    goto pathmatched;
 | 
						||
  }
 | 
						||
 | 
						||
  /* not using checkprefix() because matching should be case-sensitive */
 | 
						||
  if(strncmp(cookie_path, uri_path, cookie_path_len)) {
 | 
						||
    ret = FALSE;
 | 
						||
    goto pathmatched;
 | 
						||
  }
 | 
						||
 | 
						||
  /* The cookie-path and the uri-path are identical. */
 | 
						||
  if(cookie_path_len == uri_path_len) {
 | 
						||
    ret = TRUE;
 | 
						||
    goto pathmatched;
 | 
						||
  }
 | 
						||
 | 
						||
  /* here, cookie_path_len < url_path_len */
 | 
						||
  if(uri_path[cookie_path_len] == '/') {
 | 
						||
    ret = TRUE;
 | 
						||
    goto pathmatched;
 | 
						||
  }
 | 
						||
 | 
						||
  ret = FALSE;
 | 
						||
 | 
						||
pathmatched:
 | 
						||
  free(uri_path);
 | 
						||
  return ret;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * cookie path sanitize
 | 
						||
 */
 | 
						||
static char *sanitize_cookie_path(const char *cookie_path)
 | 
						||
{
 | 
						||
  size_t len;
 | 
						||
  char *new_path = strdup(cookie_path);
 | 
						||
  if(!new_path)
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  /* some stupid site sends path attribute with '"'. */
 | 
						||
  len = strlen(new_path);
 | 
						||
  if(new_path[0] == '\"') {
 | 
						||
    memmove((void *)new_path, (const void *)(new_path + 1), len);
 | 
						||
    len--;
 | 
						||
  }
 | 
						||
  if(len && (new_path[len - 1] == '\"')) {
 | 
						||
    new_path[len - 1] = 0x0;
 | 
						||
    len--;
 | 
						||
  }
 | 
						||
 | 
						||
  /* RFC6265 5.2.4 The Path Attribute */
 | 
						||
  if(new_path[0] != '/') {
 | 
						||
    /* Let cookie-path be the default-path. */
 | 
						||
    free(new_path);
 | 
						||
    new_path = strdup("/");
 | 
						||
    return new_path;
 | 
						||
  }
 | 
						||
 | 
						||
  /* convert /hoge/ to /hoge */
 | 
						||
  if(len && new_path[len - 1] == '/') {
 | 
						||
    new_path[len - 1] = 0x0;
 | 
						||
  }
 | 
						||
 | 
						||
  return new_path;
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
 | 
						||
 *
 | 
						||
 * NOTE: OOM or cookie parsing failures are ignored.
 | 
						||
 */
 | 
						||
void Curl_cookie_loadfiles(struct SessionHandle *data)
 | 
						||
{
 | 
						||
  struct curl_slist *list = data->change.cookielist;
 | 
						||
  if(list) {
 | 
						||
    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 | 
						||
    while(list) {
 | 
						||
      struct CookieInfo *newcookies = Curl_cookie_init(data,
 | 
						||
                                        list->data,
 | 
						||
                                        data->cookies,
 | 
						||
                                        data->set.cookiesession);
 | 
						||
      if(!newcookies)
 | 
						||
        /* Failure may be due to OOM or a bad cookie; both are ignored
 | 
						||
         * but only the first should be
 | 
						||
         */
 | 
						||
        infof(data, "ignoring failed cookie_init for %s\n", list->data);
 | 
						||
      else
 | 
						||
        data->cookies = newcookies;
 | 
						||
      list = list->next;
 | 
						||
    }
 | 
						||
    curl_slist_free_all(data->change.cookielist); /* clean up list */
 | 
						||
    data->change.cookielist = NULL; /* don't do this again! */
 | 
						||
    Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
 | 
						||
 * that will be freed before the allocated string is stored there.
 | 
						||
 *
 | 
						||
 * It is meant to easily replace strdup()
 | 
						||
 */
 | 
						||
static void strstore(char **str, const char *newstr)
 | 
						||
{
 | 
						||
  free(*str);
 | 
						||
  *str = strdup(newstr);
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * remove_expired() removes expired cookies.
 | 
						||
 */
 | 
						||
static void remove_expired(struct CookieInfo *cookies)
 | 
						||
{
 | 
						||
  struct Cookie *co, *nx, *pv;
 | 
						||
  curl_off_t now = (curl_off_t)time(NULL);
 | 
						||
 | 
						||
  co = cookies->cookies;
 | 
						||
  pv = NULL;
 | 
						||
  while(co) {
 | 
						||
    nx = co->next;
 | 
						||
    if((co->expirestr || co->maxage) && co->expires < now) {
 | 
						||
      if(co == cookies->cookies) {
 | 
						||
        cookies->cookies = co->next;
 | 
						||
      }
 | 
						||
      else {
 | 
						||
        pv->next = co->next;
 | 
						||
      }
 | 
						||
      cookies->numcookies--;
 | 
						||
      freecookie(co);
 | 
						||
    }
 | 
						||
    else {
 | 
						||
      pv = co;
 | 
						||
    }
 | 
						||
    co = nx;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * Return true if the given string is an IP(v4|v6) address.
 | 
						||
 */
 | 
						||
static bool isip(const char *domain)
 | 
						||
{
 | 
						||
  struct in_addr addr;
 | 
						||
#ifdef ENABLE_IPV6
 | 
						||
  struct in6_addr addr6;
 | 
						||
#endif
 | 
						||
 | 
						||
  if(Curl_inet_pton(AF_INET, domain, &addr)
 | 
						||
#ifdef ENABLE_IPV6
 | 
						||
     || Curl_inet_pton(AF_INET6, domain, &addr6)
 | 
						||
#endif
 | 
						||
    ) {
 | 
						||
    /* domain name given as IP address */
 | 
						||
    return TRUE;
 | 
						||
  }
 | 
						||
 | 
						||
  return FALSE;
 | 
						||
}
 | 
						||
 | 
						||
/****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_add()
 | 
						||
 *
 | 
						||
 * Add a single cookie line to the cookie keeping object.
 | 
						||
 *
 | 
						||
 * Be aware that sometimes we get an IP-only host name, and that might also be
 | 
						||
 * a numerical IPv6 address.
 | 
						||
 *
 | 
						||
 * Returns NULL on out of memory or invalid cookie. This is suboptimal,
 | 
						||
 * as they should be treated separately.
 | 
						||
 ***************************************************************************/
 | 
						||
 | 
						||
struct Cookie *
 | 
						||
Curl_cookie_add(struct SessionHandle *data,
 | 
						||
                /* The 'data' pointer here may be NULL at times, and thus
 | 
						||
                   must only be used very carefully for things that can deal
 | 
						||
                   with data being NULL. Such as infof() and similar */
 | 
						||
 | 
						||
                struct CookieInfo *c,
 | 
						||
                bool httpheader, /* TRUE if HTTP header-style line */
 | 
						||
                char *lineptr,   /* first character of the line */
 | 
						||
                const char *domain, /* default domain */
 | 
						||
                const char *path)   /* full path used when this cookie is set,
 | 
						||
                                       used to get default path for the cookie
 | 
						||
                                       unless set */
 | 
						||
{
 | 
						||
  struct Cookie *clist;
 | 
						||
  char name[MAX_NAME];
 | 
						||
  struct Cookie *co;
 | 
						||
  struct Cookie *lastc=NULL;
 | 
						||
  time_t now = time(NULL);
 | 
						||
  bool replace_old = FALSE;
 | 
						||
  bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
 | 
						||
 | 
						||
#ifdef CURL_DISABLE_VERBOSE_STRINGS
 | 
						||
  (void)data;
 | 
						||
#endif
 | 
						||
 | 
						||
  /* First, alloc and init a new struct for it */
 | 
						||
  co = calloc(1, sizeof(struct Cookie));
 | 
						||
  if(!co)
 | 
						||
    return NULL; /* bail out if we're this low on memory */
 | 
						||
 | 
						||
  if(httpheader) {
 | 
						||
    /* This line was read off a HTTP-header */
 | 
						||
    const char *ptr;
 | 
						||
    const char *semiptr;
 | 
						||
    char *what;
 | 
						||
 | 
						||
    what = malloc(MAX_COOKIE_LINE);
 | 
						||
    if(!what) {
 | 
						||
      free(co);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
    semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
 | 
						||
 | 
						||
    while(*lineptr && ISBLANK(*lineptr))
 | 
						||
      lineptr++;
 | 
						||
 | 
						||
    ptr = lineptr;
 | 
						||
    do {
 | 
						||
      /* we have a <what>=<this> pair or a stand-alone word here */
 | 
						||
      name[0]=what[0]=0; /* init the buffers */
 | 
						||
      if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n =] =%"
 | 
						||
                     MAX_COOKIE_LINE_TXT "[^;\r\n]",
 | 
						||
                     name, what)) {
 | 
						||
        /* Use strstore() below to properly deal with received cookie
 | 
						||
           headers that have the same string property set more than once,
 | 
						||
           and then we use the last one. */
 | 
						||
        const char *whatptr;
 | 
						||
        bool done = FALSE;
 | 
						||
        bool sep;
 | 
						||
        size_t len=strlen(what);
 | 
						||
        const char *endofn = &ptr[ strlen(name) ];
 | 
						||
 | 
						||
        /* skip trailing spaces in name */
 | 
						||
        while(*endofn && ISBLANK(*endofn))
 | 
						||
          endofn++;
 | 
						||
 | 
						||
        /* name ends with a '=' ? */
 | 
						||
        sep = (*endofn == '=')?TRUE:FALSE;
 | 
						||
 | 
						||
        /* Strip off trailing whitespace from the 'what' */
 | 
						||
        while(len && ISBLANK(what[len-1])) {
 | 
						||
          what[len-1]=0;
 | 
						||
          len--;
 | 
						||
        }
 | 
						||
 | 
						||
        /* Skip leading whitespace from the 'what' */
 | 
						||
        whatptr=what;
 | 
						||
        while(*whatptr && ISBLANK(*whatptr))
 | 
						||
          whatptr++;
 | 
						||
 | 
						||
        if(!len) {
 | 
						||
          /* this was a "<name>=" with no content, and we must allow
 | 
						||
             'secure' and 'httponly' specified this weirdly */
 | 
						||
          done = TRUE;
 | 
						||
          if(Curl_raw_equal("secure", name))
 | 
						||
            co->secure = TRUE;
 | 
						||
          else if(Curl_raw_equal("httponly", name))
 | 
						||
            co->httponly = TRUE;
 | 
						||
          else if(sep)
 | 
						||
            /* there was a '=' so we're not done parsing this field */
 | 
						||
            done = FALSE;
 | 
						||
        }
 | 
						||
        if(done)
 | 
						||
          ;
 | 
						||
        else if(Curl_raw_equal("path", name)) {
 | 
						||
          strstore(&co->path, whatptr);
 | 
						||
          if(!co->path) {
 | 
						||
            badcookie = TRUE; /* out of memory bad */
 | 
						||
            break;
 | 
						||
          }
 | 
						||
          co->spath = sanitize_cookie_path(co->path);
 | 
						||
          if(!co->spath) {
 | 
						||
            badcookie = TRUE; /* out of memory bad */
 | 
						||
            break;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        else if(Curl_raw_equal("domain", name)) {
 | 
						||
          bool is_ip;
 | 
						||
          const char *dotp;
 | 
						||
 | 
						||
          /* Now, we make sure that our host is within the given domain,
 | 
						||
             or the given domain is not valid and thus cannot be set. */
 | 
						||
 | 
						||
          if('.' == whatptr[0])
 | 
						||
            whatptr++; /* ignore preceding dot */
 | 
						||
 | 
						||
          is_ip = isip(domain ? domain : whatptr);
 | 
						||
 | 
						||
          /* check for more dots */
 | 
						||
          dotp = strchr(whatptr, '.');
 | 
						||
          if(!dotp)
 | 
						||
            domain=":";
 | 
						||
 | 
						||
          if(!domain
 | 
						||
             || (is_ip && !strcmp(whatptr, domain))
 | 
						||
             || (!is_ip && tailmatch(whatptr, domain))) {
 | 
						||
            strstore(&co->domain, whatptr);
 | 
						||
            if(!co->domain) {
 | 
						||
              badcookie = TRUE;
 | 
						||
              break;
 | 
						||
            }
 | 
						||
            if(!is_ip)
 | 
						||
              co->tailmatch=TRUE; /* we always do that if the domain name was
 | 
						||
                                     given */
 | 
						||
          }
 | 
						||
          else {
 | 
						||
            /* we did not get a tailmatch and then the attempted set domain
 | 
						||
               is not a domain to which the current host belongs. Mark as
 | 
						||
               bad. */
 | 
						||
            badcookie=TRUE;
 | 
						||
            infof(data, "skipped cookie with bad tailmatch domain: %s\n",
 | 
						||
                  whatptr);
 | 
						||
          }
 | 
						||
        }
 | 
						||
        else if(Curl_raw_equal("version", name)) {
 | 
						||
          strstore(&co->version, whatptr);
 | 
						||
          if(!co->version) {
 | 
						||
            badcookie = TRUE;
 | 
						||
            break;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        else if(Curl_raw_equal("max-age", name)) {
 | 
						||
          /* Defined in RFC2109:
 | 
						||
 | 
						||
             Optional.  The Max-Age attribute defines the lifetime of the
 | 
						||
             cookie, in seconds.  The delta-seconds value is a decimal non-
 | 
						||
             negative integer.  After delta-seconds seconds elapse, the
 | 
						||
             client should discard the cookie.  A value of zero means the
 | 
						||
             cookie should be discarded immediately.
 | 
						||
 | 
						||
          */
 | 
						||
          strstore(&co->maxage, whatptr);
 | 
						||
          if(!co->maxage) {
 | 
						||
            badcookie = TRUE;
 | 
						||
            break;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        else if(Curl_raw_equal("expires", name)) {
 | 
						||
          strstore(&co->expirestr, whatptr);
 | 
						||
          if(!co->expirestr) {
 | 
						||
            badcookie = TRUE;
 | 
						||
            break;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        else if(!co->name) {
 | 
						||
          co->name = strdup(name);
 | 
						||
          co->value = strdup(whatptr);
 | 
						||
          if(!co->name || !co->value) {
 | 
						||
            badcookie = TRUE;
 | 
						||
            break;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        /*
 | 
						||
          else this is the second (or more) name we don't know
 | 
						||
          about! */
 | 
						||
      }
 | 
						||
      else {
 | 
						||
        /* this is an "illegal" <what>=<this> pair */
 | 
						||
      }
 | 
						||
 | 
						||
      if(!semiptr || !*semiptr) {
 | 
						||
        /* we already know there are no more cookies */
 | 
						||
        semiptr = NULL;
 | 
						||
        continue;
 | 
						||
      }
 | 
						||
 | 
						||
      ptr=semiptr+1;
 | 
						||
      while(*ptr && ISBLANK(*ptr))
 | 
						||
        ptr++;
 | 
						||
      semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
 | 
						||
 | 
						||
      if(!semiptr && *ptr)
 | 
						||
        /* There are no more semicolons, but there's a final name=value pair
 | 
						||
           coming up */
 | 
						||
        semiptr=strchr(ptr, '\0');
 | 
						||
    } while(semiptr);
 | 
						||
 | 
						||
    if(co->maxage) {
 | 
						||
      co->expires =
 | 
						||
        curlx_strtoofft((*co->maxage=='\"')?
 | 
						||
                        &co->maxage[1]:&co->maxage[0], NULL, 10);
 | 
						||
      if(CURL_OFF_T_MAX - now < co->expires)
 | 
						||
        /* avoid overflow */
 | 
						||
        co->expires = CURL_OFF_T_MAX;
 | 
						||
      else
 | 
						||
        co->expires += now;
 | 
						||
    }
 | 
						||
    else if(co->expirestr) {
 | 
						||
      /* Note that if the date couldn't get parsed for whatever reason,
 | 
						||
         the cookie will be treated as a session cookie */
 | 
						||
      co->expires = curl_getdate(co->expirestr, NULL);
 | 
						||
 | 
						||
      /* Session cookies have expires set to 0 so if we get that back
 | 
						||
         from the date parser let's add a second to make it a
 | 
						||
         non-session cookie */
 | 
						||
      if(co->expires == 0)
 | 
						||
        co->expires = 1;
 | 
						||
      else if(co->expires < 0)
 | 
						||
        co->expires = 0;
 | 
						||
    }
 | 
						||
 | 
						||
    if(!badcookie && !co->domain) {
 | 
						||
      if(domain) {
 | 
						||
        /* no domain was given in the header line, set the default */
 | 
						||
        co->domain=strdup(domain);
 | 
						||
        if(!co->domain)
 | 
						||
          badcookie = TRUE;
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    if(!badcookie && !co->path && path) {
 | 
						||
      /* No path was given in the header line, set the default.
 | 
						||
         Note that the passed-in path to this function MAY have a '?' and
 | 
						||
         following part that MUST not be stored as part of the path. */
 | 
						||
      char *queryp = strchr(path, '?');
 | 
						||
 | 
						||
      /* queryp is where the interesting part of the path ends, so now we
 | 
						||
         want to the find the last */
 | 
						||
      char *endslash;
 | 
						||
      if(!queryp)
 | 
						||
        endslash = strrchr(path, '/');
 | 
						||
      else
 | 
						||
        endslash = memrchr(path, '/', (size_t)(queryp - path));
 | 
						||
      if(endslash) {
 | 
						||
        size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
 | 
						||
        co->path=malloc(pathlen+1); /* one extra for the zero byte */
 | 
						||
        if(co->path) {
 | 
						||
          memcpy(co->path, path, pathlen);
 | 
						||
          co->path[pathlen]=0; /* zero terminate */
 | 
						||
          co->spath = sanitize_cookie_path(co->path);
 | 
						||
          if(!co->spath)
 | 
						||
            badcookie = TRUE; /* out of memory bad */
 | 
						||
        }
 | 
						||
        else
 | 
						||
          badcookie = TRUE;
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    free(what);
 | 
						||
 | 
						||
    if(badcookie || !co->name) {
 | 
						||
      /* we didn't get a cookie name or a bad one,
 | 
						||
         this is an illegal line, bail out */
 | 
						||
      freecookie(co);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  }
 | 
						||
  else {
 | 
						||
    /* This line is NOT a HTTP header style line, we do offer support for
 | 
						||
       reading the odd netscape cookies-file format here */
 | 
						||
    char *ptr;
 | 
						||
    char *firstptr;
 | 
						||
    char *tok_buf=NULL;
 | 
						||
    int fields;
 | 
						||
 | 
						||
    /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
 | 
						||
       marked with httpOnly after the domain name are not accessible
 | 
						||
       from javascripts, but since curl does not operate at javascript
 | 
						||
       level, we include them anyway. In Firefox's cookie files, these
 | 
						||
       lines are preceded with #HttpOnly_ and then everything is
 | 
						||
       as usual, so we skip 10 characters of the line..
 | 
						||
    */
 | 
						||
    if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
 | 
						||
      lineptr += 10;
 | 
						||
      co->httponly = TRUE;
 | 
						||
    }
 | 
						||
 | 
						||
    if(lineptr[0]=='#') {
 | 
						||
      /* don't even try the comments */
 | 
						||
      free(co);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
    /* strip off the possible end-of-line characters */
 | 
						||
    ptr=strchr(lineptr, '\r');
 | 
						||
    if(ptr)
 | 
						||
      *ptr=0; /* clear it */
 | 
						||
    ptr=strchr(lineptr, '\n');
 | 
						||
    if(ptr)
 | 
						||
      *ptr=0; /* clear it */
 | 
						||
 | 
						||
    firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
 | 
						||
 | 
						||
    /* Now loop through the fields and init the struct we already have
 | 
						||
       allocated */
 | 
						||
    for(ptr=firstptr, fields=0; ptr && !badcookie;
 | 
						||
        ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
 | 
						||
      switch(fields) {
 | 
						||
      case 0:
 | 
						||
        if(ptr[0]=='.') /* skip preceding dots */
 | 
						||
          ptr++;
 | 
						||
        co->domain = strdup(ptr);
 | 
						||
        if(!co->domain)
 | 
						||
          badcookie = TRUE;
 | 
						||
        break;
 | 
						||
      case 1:
 | 
						||
        /* This field got its explanation on the 23rd of May 2001 by
 | 
						||
           Andr<64>s Garc<72>a:
 | 
						||
 | 
						||
           flag: A TRUE/FALSE value indicating if all machines within a given
 | 
						||
           domain can access the variable. This value is set automatically by
 | 
						||
           the browser, depending on the value you set for the domain.
 | 
						||
 | 
						||
           As far as I can see, it is set to true when the cookie says
 | 
						||
           .domain.com and to false when the domain is complete www.domain.com
 | 
						||
        */
 | 
						||
        co->tailmatch = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
 | 
						||
        break;
 | 
						||
      case 2:
 | 
						||
        /* It turns out, that sometimes the file format allows the path
 | 
						||
           field to remain not filled in, we try to detect this and work
 | 
						||
           around it! Andr<64>s Garc<72>a made us aware of this... */
 | 
						||
        if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
 | 
						||
          /* only if the path doesn't look like a boolean option! */
 | 
						||
          co->path = strdup(ptr);
 | 
						||
          if(!co->path)
 | 
						||
            badcookie = TRUE;
 | 
						||
          else {
 | 
						||
            co->spath = sanitize_cookie_path(co->path);
 | 
						||
            if(!co->spath) {
 | 
						||
              badcookie = TRUE; /* out of memory bad */
 | 
						||
            }
 | 
						||
          }
 | 
						||
          break;
 | 
						||
        }
 | 
						||
        /* this doesn't look like a path, make one up! */
 | 
						||
        co->path = strdup("/");
 | 
						||
        if(!co->path)
 | 
						||
          badcookie = TRUE;
 | 
						||
        co->spath = strdup("/");
 | 
						||
        if(!co->spath)
 | 
						||
          badcookie = TRUE;
 | 
						||
        fields++; /* add a field and fall down to secure */
 | 
						||
        /* FALLTHROUGH */
 | 
						||
      case 3:
 | 
						||
        co->secure = Curl_raw_equal(ptr, "TRUE")?TRUE:FALSE;
 | 
						||
        break;
 | 
						||
      case 4:
 | 
						||
        co->expires = curlx_strtoofft(ptr, NULL, 10);
 | 
						||
        break;
 | 
						||
      case 5:
 | 
						||
        co->name = strdup(ptr);
 | 
						||
        if(!co->name)
 | 
						||
          badcookie = TRUE;
 | 
						||
        break;
 | 
						||
      case 6:
 | 
						||
        co->value = strdup(ptr);
 | 
						||
        if(!co->value)
 | 
						||
          badcookie = TRUE;
 | 
						||
        break;
 | 
						||
      }
 | 
						||
    }
 | 
						||
    if(6 == fields) {
 | 
						||
      /* we got a cookie with blank contents, fix it */
 | 
						||
      co->value = strdup("");
 | 
						||
      if(!co->value)
 | 
						||
        badcookie = TRUE;
 | 
						||
      else
 | 
						||
        fields++;
 | 
						||
    }
 | 
						||
 | 
						||
    if(!badcookie && (7 != fields))
 | 
						||
      /* we did not find the sufficient number of fields */
 | 
						||
      badcookie = TRUE;
 | 
						||
 | 
						||
    if(badcookie) {
 | 
						||
      freecookie(co);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
 | 
						||
  }
 | 
						||
 | 
						||
  if(!c->running &&    /* read from a file */
 | 
						||
     c->newsession &&  /* clean session cookies */
 | 
						||
     !co->expires) {   /* this is a session cookie since it doesn't expire! */
 | 
						||
    freecookie(co);
 | 
						||
    return NULL;
 | 
						||
  }
 | 
						||
 | 
						||
  co->livecookie = c->running;
 | 
						||
 | 
						||
  /* now, we have parsed the incoming line, we must now check if this
 | 
						||
     superceeds an already existing cookie, which it may if the previous have
 | 
						||
     the same domain and path as this */
 | 
						||
 | 
						||
  /* at first, remove expired cookies */
 | 
						||
  remove_expired(c);
 | 
						||
 | 
						||
  clist = c->cookies;
 | 
						||
  replace_old = FALSE;
 | 
						||
  while(clist) {
 | 
						||
    if(Curl_raw_equal(clist->name, co->name)) {
 | 
						||
      /* the names are identical */
 | 
						||
 | 
						||
      if(clist->domain && co->domain) {
 | 
						||
        if(Curl_raw_equal(clist->domain, co->domain))
 | 
						||
          /* The domains are identical */
 | 
						||
          replace_old=TRUE;
 | 
						||
      }
 | 
						||
      else if(!clist->domain && !co->domain)
 | 
						||
        replace_old = TRUE;
 | 
						||
 | 
						||
      if(replace_old) {
 | 
						||
        /* the domains were identical */
 | 
						||
 | 
						||
        if(clist->spath && co->spath) {
 | 
						||
          if(Curl_raw_equal(clist->spath, co->spath)) {
 | 
						||
            replace_old = TRUE;
 | 
						||
          }
 | 
						||
          else
 | 
						||
            replace_old = FALSE;
 | 
						||
        }
 | 
						||
        else if(!clist->spath && !co->spath)
 | 
						||
          replace_old = TRUE;
 | 
						||
        else
 | 
						||
          replace_old = FALSE;
 | 
						||
 | 
						||
      }
 | 
						||
 | 
						||
      if(replace_old && !co->livecookie && clist->livecookie) {
 | 
						||
        /* Both cookies matched fine, except that the already present
 | 
						||
           cookie is "live", which means it was set from a header, while
 | 
						||
           the new one isn't "live" and thus only read from a file. We let
 | 
						||
           live cookies stay alive */
 | 
						||
 | 
						||
        /* Free the newcomer and get out of here! */
 | 
						||
        freecookie(co);
 | 
						||
        return NULL;
 | 
						||
      }
 | 
						||
 | 
						||
      if(replace_old) {
 | 
						||
        co->next = clist->next; /* get the next-pointer first */
 | 
						||
 | 
						||
        /* then free all the old pointers */
 | 
						||
        free(clist->name);
 | 
						||
        free(clist->value);
 | 
						||
        free(clist->domain);
 | 
						||
        free(clist->path);
 | 
						||
        free(clist->spath);
 | 
						||
        free(clist->expirestr);
 | 
						||
        free(clist->version);
 | 
						||
        free(clist->maxage);
 | 
						||
 | 
						||
        *clist = *co;  /* then store all the new data */
 | 
						||
 | 
						||
        free(co);   /* free the newly alloced memory */
 | 
						||
        co = clist; /* point to the previous struct instead */
 | 
						||
 | 
						||
        /* We have replaced a cookie, now skip the rest of the list but
 | 
						||
           make sure the 'lastc' pointer is properly set */
 | 
						||
        do {
 | 
						||
          lastc = clist;
 | 
						||
          clist = clist->next;
 | 
						||
        } while(clist);
 | 
						||
        break;
 | 
						||
      }
 | 
						||
    }
 | 
						||
    lastc = clist;
 | 
						||
    clist = clist->next;
 | 
						||
  }
 | 
						||
 | 
						||
  if(c->running)
 | 
						||
    /* Only show this when NOT reading the cookies from a file */
 | 
						||
    infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
 | 
						||
          "expire %" CURL_FORMAT_CURL_OFF_T "\n",
 | 
						||
          replace_old?"Replaced":"Added", co->name, co->value,
 | 
						||
          co->domain, co->path, co->expires);
 | 
						||
 | 
						||
  if(!replace_old) {
 | 
						||
    /* then make the last item point on this new one */
 | 
						||
    if(lastc)
 | 
						||
      lastc->next = co;
 | 
						||
    else
 | 
						||
      c->cookies = co;
 | 
						||
    c->numcookies++; /* one more cookie in the jar */
 | 
						||
  }
 | 
						||
 | 
						||
  return co;
 | 
						||
}
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_init()
 | 
						||
 *
 | 
						||
 * Inits a cookie struct to read data from a local file. This is always
 | 
						||
 * called before any cookies are set. File may be NULL.
 | 
						||
 *
 | 
						||
 * If 'newsession' is TRUE, discard all "session cookies" on read from file.
 | 
						||
 *
 | 
						||
 * Returns NULL on out of memory. Invalid cookies are ignored.
 | 
						||
 ****************************************************************************/
 | 
						||
struct CookieInfo *Curl_cookie_init(struct SessionHandle *data,
 | 
						||
                                    const char *file,
 | 
						||
                                    struct CookieInfo *inc,
 | 
						||
                                    bool newsession)
 | 
						||
{
 | 
						||
  struct CookieInfo *c;
 | 
						||
  FILE *fp = NULL;
 | 
						||
  bool fromfile=TRUE;
 | 
						||
  char *line = NULL;
 | 
						||
 | 
						||
  if(NULL == inc) {
 | 
						||
    /* we didn't get a struct, create one */
 | 
						||
    c = calloc(1, sizeof(struct CookieInfo));
 | 
						||
    if(!c)
 | 
						||
      return NULL; /* failed to get memory */
 | 
						||
    c->filename = strdup(file?file:"none"); /* copy the name just in case */
 | 
						||
    if(!c->filename)
 | 
						||
      goto fail; /* failed to get memory */
 | 
						||
  }
 | 
						||
  else {
 | 
						||
    /* we got an already existing one, use that */
 | 
						||
    c = inc;
 | 
						||
  }
 | 
						||
  c->running = FALSE; /* this is not running, this is init */
 | 
						||
 | 
						||
  if(file && strequal(file, "-")) {
 | 
						||
    fp = stdin;
 | 
						||
    fromfile=FALSE;
 | 
						||
  }
 | 
						||
  else if(file && !*file) {
 | 
						||
    /* points to a "" string */
 | 
						||
    fp = NULL;
 | 
						||
  }
 | 
						||
  else
 | 
						||
    fp = file?fopen(file, "r"):NULL;
 | 
						||
 | 
						||
  c->newsession = newsession; /* new session? */
 | 
						||
 | 
						||
  if(fp) {
 | 
						||
    char *lineptr;
 | 
						||
    bool headerline;
 | 
						||
 | 
						||
    line = malloc(MAX_COOKIE_LINE);
 | 
						||
    if(!line)
 | 
						||
      goto fail;
 | 
						||
    while(fgets(line, MAX_COOKIE_LINE, fp)) {
 | 
						||
      if(checkprefix("Set-Cookie:", line)) {
 | 
						||
        /* This is a cookie line, get it! */
 | 
						||
        lineptr=&line[11];
 | 
						||
        headerline=TRUE;
 | 
						||
      }
 | 
						||
      else {
 | 
						||
        lineptr=line;
 | 
						||
        headerline=FALSE;
 | 
						||
      }
 | 
						||
      while(*lineptr && ISBLANK(*lineptr))
 | 
						||
        lineptr++;
 | 
						||
 | 
						||
      Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
 | 
						||
    }
 | 
						||
    free(line); /* free the line buffer */
 | 
						||
 | 
						||
    if(fromfile)
 | 
						||
      fclose(fp);
 | 
						||
  }
 | 
						||
 | 
						||
  c->running = TRUE;          /* now, we're running */
 | 
						||
 | 
						||
  return c;
 | 
						||
 | 
						||
fail:
 | 
						||
  free(line);
 | 
						||
  if(!inc)
 | 
						||
    /* Only clean up if we allocated it here, as the original could still be in
 | 
						||
     * use by a share handle */
 | 
						||
    Curl_cookie_cleanup(c);
 | 
						||
  if(fromfile && fp)
 | 
						||
    fclose(fp);
 | 
						||
  return NULL; /* out of memory */
 | 
						||
}
 | 
						||
 | 
						||
/* sort this so that the longest path gets before the shorter path */
 | 
						||
static int cookie_sort(const void *p1, const void *p2)
 | 
						||
{
 | 
						||
  struct Cookie *c1 = *(struct Cookie **)p1;
 | 
						||
  struct Cookie *c2 = *(struct Cookie **)p2;
 | 
						||
  size_t l1, l2;
 | 
						||
 | 
						||
  /* 1 - compare cookie path lengths */
 | 
						||
  l1 = c1->path ? strlen(c1->path) : 0;
 | 
						||
  l2 = c2->path ? strlen(c2->path) : 0;
 | 
						||
 | 
						||
  if(l1 != l2)
 | 
						||
    return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
 | 
						||
 | 
						||
  /* 2 - compare cookie domain lengths */
 | 
						||
  l1 = c1->domain ? strlen(c1->domain) : 0;
 | 
						||
  l2 = c2->domain ? strlen(c2->domain) : 0;
 | 
						||
 | 
						||
  if(l1 != l2)
 | 
						||
    return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
 | 
						||
 | 
						||
  /* 3 - compare cookie names */
 | 
						||
  if(c1->name && c2->name)
 | 
						||
    return strcmp(c1->name, c2->name);
 | 
						||
 | 
						||
  /* sorry, can't be more deterministic */
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_getlist()
 | 
						||
 *
 | 
						||
 * For a given host and path, return a linked list of cookies that the
 | 
						||
 * client should send to the server if used now. The secure boolean informs
 | 
						||
 * the cookie if a secure connection is achieved or not.
 | 
						||
 *
 | 
						||
 * It shall only return cookies that haven't expired.
 | 
						||
 *
 | 
						||
 ****************************************************************************/
 | 
						||
 | 
						||
struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 | 
						||
                                   const char *host, const char *path,
 | 
						||
                                   bool secure)
 | 
						||
{
 | 
						||
  struct Cookie *newco;
 | 
						||
  struct Cookie *co;
 | 
						||
  time_t now = time(NULL);
 | 
						||
  struct Cookie *mainco=NULL;
 | 
						||
  size_t matches = 0;
 | 
						||
  bool is_ip;
 | 
						||
 | 
						||
  if(!c || !c->cookies)
 | 
						||
    return NULL; /* no cookie struct or no cookies in the struct */
 | 
						||
 | 
						||
  /* at first, remove expired cookies */
 | 
						||
  remove_expired(c);
 | 
						||
 | 
						||
  /* check if host is an IP(v4|v6) address */
 | 
						||
  is_ip = isip(host);
 | 
						||
 | 
						||
  co = c->cookies;
 | 
						||
 | 
						||
  while(co) {
 | 
						||
    /* only process this cookie if it is not expired or had no expire
 | 
						||
       date AND that if the cookie requires we're secure we must only
 | 
						||
       continue if we are! */
 | 
						||
    if((!co->expires || (co->expires > now)) &&
 | 
						||
       (co->secure?secure:TRUE)) {
 | 
						||
 | 
						||
      /* now check if the domain is correct */
 | 
						||
      if(!co->domain ||
 | 
						||
         (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
 | 
						||
         ((!co->tailmatch || is_ip) && Curl_raw_equal(host, co->domain)) ) {
 | 
						||
        /* the right part of the host matches the domain stuff in the
 | 
						||
           cookie data */
 | 
						||
 | 
						||
        /* now check the left part of the path with the cookies path
 | 
						||
           requirement */
 | 
						||
        if(!co->spath || pathmatch(co->spath, path) ) {
 | 
						||
 | 
						||
          /* and now, we know this is a match and we should create an
 | 
						||
             entry for the return-linked-list */
 | 
						||
 | 
						||
          newco = malloc(sizeof(struct Cookie));
 | 
						||
          if(newco) {
 | 
						||
            /* first, copy the whole source cookie: */
 | 
						||
            memcpy(newco, co, sizeof(struct Cookie));
 | 
						||
 | 
						||
            /* then modify our next */
 | 
						||
            newco->next = mainco;
 | 
						||
 | 
						||
            /* point the main to us */
 | 
						||
            mainco = newco;
 | 
						||
 | 
						||
            matches++;
 | 
						||
          }
 | 
						||
          else {
 | 
						||
            fail:
 | 
						||
            /* failure, clear up the allocated chain and return NULL */
 | 
						||
            while(mainco) {
 | 
						||
              co = mainco->next;
 | 
						||
              free(mainco);
 | 
						||
              mainco = co;
 | 
						||
            }
 | 
						||
 | 
						||
            return NULL;
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
    co = co->next;
 | 
						||
  }
 | 
						||
 | 
						||
  if(matches) {
 | 
						||
    /* Now we need to make sure that if there is a name appearing more than
 | 
						||
       once, the longest specified path version comes first. To make this
 | 
						||
       the swiftest way, we just sort them all based on path length. */
 | 
						||
    struct Cookie **array;
 | 
						||
    size_t i;
 | 
						||
 | 
						||
    /* alloc an array and store all cookie pointers */
 | 
						||
    array = malloc(sizeof(struct Cookie *) * matches);
 | 
						||
    if(!array)
 | 
						||
      goto fail;
 | 
						||
 | 
						||
    co = mainco;
 | 
						||
 | 
						||
    for(i=0; co; co = co->next)
 | 
						||
      array[i++] = co;
 | 
						||
 | 
						||
    /* now sort the cookie pointers in path length order */
 | 
						||
    qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
 | 
						||
 | 
						||
    /* remake the linked list order according to the new order */
 | 
						||
 | 
						||
    mainco = array[0]; /* start here */
 | 
						||
    for(i=0; i<matches-1; i++)
 | 
						||
      array[i]->next = array[i+1];
 | 
						||
    array[matches-1]->next = NULL; /* terminate the list */
 | 
						||
 | 
						||
    free(array); /* remove the temporary data again */
 | 
						||
  }
 | 
						||
 | 
						||
  return mainco; /* return the new list */
 | 
						||
}
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_clearall()
 | 
						||
 *
 | 
						||
 * Clear all existing cookies and reset the counter.
 | 
						||
 *
 | 
						||
 ****************************************************************************/
 | 
						||
void Curl_cookie_clearall(struct CookieInfo *cookies)
 | 
						||
{
 | 
						||
  if(cookies) {
 | 
						||
    Curl_cookie_freelist(cookies->cookies, TRUE);
 | 
						||
    cookies->cookies = NULL;
 | 
						||
    cookies->numcookies = 0;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_freelist()
 | 
						||
 *
 | 
						||
 * Free a list of cookies previously returned by Curl_cookie_getlist();
 | 
						||
 *
 | 
						||
 * The 'cookiestoo' argument tells this function whether to just free the
 | 
						||
 * list or actually also free all cookies within the list as well.
 | 
						||
 *
 | 
						||
 ****************************************************************************/
 | 
						||
 | 
						||
void Curl_cookie_freelist(struct Cookie *co, bool cookiestoo)
 | 
						||
{
 | 
						||
  struct Cookie *next;
 | 
						||
  while(co) {
 | 
						||
    next = co->next;
 | 
						||
    if(cookiestoo)
 | 
						||
      freecookie(co);
 | 
						||
    else
 | 
						||
      free(co); /* we only free the struct since the "members" are all just
 | 
						||
                   pointed out in the main cookie list! */
 | 
						||
    co = next;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_clearsess()
 | 
						||
 *
 | 
						||
 * Free all session cookies in the cookies list.
 | 
						||
 *
 | 
						||
 ****************************************************************************/
 | 
						||
void Curl_cookie_clearsess(struct CookieInfo *cookies)
 | 
						||
{
 | 
						||
  struct Cookie *first, *curr, *next, *prev = NULL;
 | 
						||
 | 
						||
  if(!cookies || !cookies->cookies)
 | 
						||
    return;
 | 
						||
 | 
						||
  first = curr = prev = cookies->cookies;
 | 
						||
 | 
						||
  for(; curr; curr = next) {
 | 
						||
    next = curr->next;
 | 
						||
    if(!curr->expires) {
 | 
						||
      if(first == curr)
 | 
						||
        first = next;
 | 
						||
 | 
						||
      if(prev == curr)
 | 
						||
        prev = next;
 | 
						||
      else
 | 
						||
        prev->next = next;
 | 
						||
 | 
						||
      freecookie(curr);
 | 
						||
      cookies->numcookies--;
 | 
						||
    }
 | 
						||
    else
 | 
						||
      prev = curr;
 | 
						||
  }
 | 
						||
 | 
						||
  cookies->cookies = first;
 | 
						||
}
 | 
						||
 | 
						||
 | 
						||
/*****************************************************************************
 | 
						||
 *
 | 
						||
 * Curl_cookie_cleanup()
 | 
						||
 *
 | 
						||
 * Free a "cookie object" previous created with Curl_cookie_init().
 | 
						||
 *
 | 
						||
 ****************************************************************************/
 | 
						||
void Curl_cookie_cleanup(struct CookieInfo *c)
 | 
						||
{
 | 
						||
  if(c) {
 | 
						||
    free(c->filename);
 | 
						||
    Curl_cookie_freelist(c->cookies, TRUE);
 | 
						||
    free(c); /* free the base struct as well */
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
/* get_netscape_format()
 | 
						||
 *
 | 
						||
 * Formats a string for Netscape output file, w/o a newline at the end.
 | 
						||
 *
 | 
						||
 * Function returns a char * to a formatted line. Has to be free()d
 | 
						||
*/
 | 
						||
static char *get_netscape_format(const struct Cookie *co)
 | 
						||
{
 | 
						||
  return aprintf(
 | 
						||
    "%s"     /* httponly preamble */
 | 
						||
    "%s%s\t" /* domain */
 | 
						||
    "%s\t"   /* tailmatch */
 | 
						||
    "%s\t"   /* path */
 | 
						||
    "%s\t"   /* secure */
 | 
						||
    "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
 | 
						||
    "%s\t"   /* name */
 | 
						||
    "%s",    /* value */
 | 
						||
    co->httponly?"#HttpOnly_":"",
 | 
						||
    /* Make sure all domains are prefixed with a dot if they allow
 | 
						||
       tailmatching. This is Mozilla-style. */
 | 
						||
    (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
 | 
						||
    co->domain?co->domain:"unknown",
 | 
						||
    co->tailmatch?"TRUE":"FALSE",
 | 
						||
    co->path?co->path:"/",
 | 
						||
    co->secure?"TRUE":"FALSE",
 | 
						||
    co->expires,
 | 
						||
    co->name,
 | 
						||
    co->value?co->value:"");
 | 
						||
}
 | 
						||
 | 
						||
/*
 | 
						||
 * cookie_output()
 | 
						||
 *
 | 
						||
 * Writes all internally known cookies to the specified file. Specify
 | 
						||
 * "-" as file name to write to stdout.
 | 
						||
 *
 | 
						||
 * The function returns non-zero on write failure.
 | 
						||
 */
 | 
						||
static int cookie_output(struct CookieInfo *c, const char *dumphere)
 | 
						||
{
 | 
						||
  struct Cookie *co;
 | 
						||
  FILE *out;
 | 
						||
  bool use_stdout=FALSE;
 | 
						||
 | 
						||
  if((NULL == c) || (0 == c->numcookies))
 | 
						||
    /* If there are no known cookies, we don't write or even create any
 | 
						||
       destination file */
 | 
						||
    return 0;
 | 
						||
 | 
						||
  /* at first, remove expired cookies */
 | 
						||
  remove_expired(c);
 | 
						||
 | 
						||
  if(strequal("-", dumphere)) {
 | 
						||
    /* use stdout */
 | 
						||
    out = stdout;
 | 
						||
    use_stdout=TRUE;
 | 
						||
  }
 | 
						||
  else {
 | 
						||
    out = fopen(dumphere, "w");
 | 
						||
    if(!out)
 | 
						||
      return 1; /* failure */
 | 
						||
  }
 | 
						||
 | 
						||
  if(c) {
 | 
						||
    char *format_ptr;
 | 
						||
 | 
						||
    fputs("# Netscape HTTP Cookie File\n"
 | 
						||
          "# http://curl.haxx.se/docs/http-cookies.html\n"
 | 
						||
          "# This file was generated by libcurl! Edit at your own risk.\n\n",
 | 
						||
          out);
 | 
						||
    co = c->cookies;
 | 
						||
 | 
						||
    while(co) {
 | 
						||
      format_ptr = get_netscape_format(co);
 | 
						||
      if(format_ptr == NULL) {
 | 
						||
        fprintf(out, "#\n# Fatal libcurl error\n");
 | 
						||
        if(!use_stdout)
 | 
						||
          fclose(out);
 | 
						||
        return 1;
 | 
						||
      }
 | 
						||
      fprintf(out, "%s\n", format_ptr);
 | 
						||
      free(format_ptr);
 | 
						||
      co=co->next;
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  if(!use_stdout)
 | 
						||
    fclose(out);
 | 
						||
 | 
						||
  return 0;
 | 
						||
}
 | 
						||
 | 
						||
struct curl_slist *Curl_cookie_list(struct SessionHandle *data)
 | 
						||
{
 | 
						||
  struct curl_slist *list = NULL;
 | 
						||
  struct curl_slist *beg;
 | 
						||
  struct Cookie *c;
 | 
						||
  char *line;
 | 
						||
 | 
						||
  if((data->cookies == NULL) ||
 | 
						||
      (data->cookies->numcookies == 0))
 | 
						||
    return NULL;
 | 
						||
 | 
						||
  c = data->cookies->cookies;
 | 
						||
 | 
						||
  while(c) {
 | 
						||
    /* fill the list with _all_ the cookies we know */
 | 
						||
    line = get_netscape_format(c);
 | 
						||
    if(!line) {
 | 
						||
      curl_slist_free_all(list);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
    beg = Curl_slist_append_nodup(list, line);
 | 
						||
    if(!beg) {
 | 
						||
      free(line);
 | 
						||
      curl_slist_free_all(list);
 | 
						||
      return NULL;
 | 
						||
    }
 | 
						||
    list = beg;
 | 
						||
    c = c->next;
 | 
						||
  }
 | 
						||
 | 
						||
  return list;
 | 
						||
}
 | 
						||
 | 
						||
void Curl_flush_cookies(struct SessionHandle *data, int cleanup)
 | 
						||
{
 | 
						||
  if(data->set.str[STRING_COOKIEJAR]) {
 | 
						||
    if(data->change.cookielist) {
 | 
						||
      /* If there is a list of cookie files to read, do it first so that
 | 
						||
         we have all the told files read before we write the new jar.
 | 
						||
         Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
 | 
						||
      Curl_cookie_loadfiles(data);
 | 
						||
    }
 | 
						||
 | 
						||
    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 | 
						||
 | 
						||
    /* if we have a destination file for all the cookies to get dumped to */
 | 
						||
    if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
 | 
						||
      infof(data, "WARNING: failed to save cookies in %s\n",
 | 
						||
            data->set.str[STRING_COOKIEJAR]);
 | 
						||
  }
 | 
						||
  else {
 | 
						||
    if(cleanup && data->change.cookielist) {
 | 
						||
      /* since nothing is written, we can just free the list of cookie file
 | 
						||
         names */
 | 
						||
      curl_slist_free_all(data->change.cookielist); /* clean up list */
 | 
						||
      data->change.cookielist = NULL;
 | 
						||
    }
 | 
						||
    Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 | 
						||
  }
 | 
						||
 | 
						||
  if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
 | 
						||
    Curl_cookie_cleanup(data->cookies);
 | 
						||
  }
 | 
						||
  Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
 | 
						||
}
 | 
						||
 | 
						||
#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */
 |