File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / cookie.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jun 3 10:01:15 2020 UTC (5 years ago) by misho
Branches: curl, MAIN
CVS tags: v7_70_0p4, HEAD
curl

    1: /***************************************************************************
    2:  *                                  _   _ ____  _
    3:  *  Project                     ___| | | |  _ \| |
    4:  *                             / __| | | | |_) | |
    5:  *                            | (__| |_| |  _ <| |___
    6:  *                             \___|\___/|_| \_\_____|
    7:  *
    8:  * Copyright (C) 1998 - 2020, 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 https://curl.haxx.se/docs/copyright.html.
   13:  *
   14:  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
   15:  * copies of the Software, and permit persons to whom the Software is
   16:  * furnished to do so, under the terms of the COPYING file.
   17:  *
   18:  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
   19:  * KIND, either express or implied.
   20:  *
   21:  ***************************************************************************/
   22: 
   23: /***
   24: 
   25: 
   26: RECEIVING COOKIE INFORMATION
   27: ============================
   28: 
   29: struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
   30:                     const char *file, struct CookieInfo *inc, bool newsession);
   31: 
   32:         Inits a cookie struct to store data in a local file. This is always
   33:         called before any cookies are set.
   34: 
   35: struct Cookie *Curl_cookie_add(struct Curl_easy *data,
   36:                  struct CookieInfo *c, bool httpheader, char *lineptr,
   37:                  const char *domain, const char *path);
   38: 
   39:         The 'lineptr' parameter is a full "Set-cookie:" line as
   40:         received from a server.
   41: 
   42:         The function need to replace previously stored lines that this new
   43:         line supersedes.
   44: 
   45:         It may remove lines that are expired.
   46: 
   47:         It should return an indication of success/error.
   48: 
   49: 
   50: SENDING COOKIE INFORMATION
   51: ==========================
   52: 
   53: struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
   54:                                     char *host, char *path, bool secure);
   55: 
   56:         For a given host and path, return a linked list of cookies that
   57:         the client should send to the server if used now. The secure
   58:         boolean informs the cookie if a secure connection is achieved or
   59:         not.
   60: 
   61:         It shall only return cookies that haven't expired.
   62: 
   63: 
   64: Example set of cookies:
   65: 
   66:     Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
   67:     Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
   68:     domain=.fidelity.com; path=/ftgw; secure
   69:     Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
   70:     domain=.fidelity.com; path=/; secure
   71:     Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
   72:     domain=.fidelity.com; path=/; secure
   73:     Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
   74:     domain=.fidelity.com; path=/; secure
   75:     Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
   76:     domain=.fidelity.com; path=/; secure
   77:     Set-cookie:
   78:     Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
   79:     13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
   80: ****/
   81: 
   82: 
   83: #include "curl_setup.h"
   84: 
   85: #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
   86: 
   87: #include "urldata.h"
   88: #include "cookie.h"
   89: #include "psl.h"
   90: #include "strtok.h"
   91: #include "sendf.h"
   92: #include "slist.h"
   93: #include "share.h"
   94: #include "strtoofft.h"
   95: #include "strcase.h"
   96: #include "curl_get_line.h"
   97: #include "curl_memrchr.h"
   98: #include "inet_pton.h"
   99: #include "parsedate.h"
  100: #include "rand.h"
  101: #include "rename.h"
  102: 
  103: /* The last 3 #include files should be in this order */
  104: #include "curl_printf.h"
  105: #include "curl_memory.h"
  106: #include "memdebug.h"
  107: 
  108: static void freecookie(struct Cookie *co)
  109: {
  110:   free(co->expirestr);
  111:   free(co->domain);
  112:   free(co->path);
  113:   free(co->spath);
  114:   free(co->name);
  115:   free(co->value);
  116:   free(co->maxage);
  117:   free(co->version);
  118:   free(co);
  119: }
  120: 
  121: static bool tailmatch(const char *cooke_domain, const char *hostname)
  122: {
  123:   size_t cookie_domain_len = strlen(cooke_domain);
  124:   size_t hostname_len = strlen(hostname);
  125: 
  126:   if(hostname_len < cookie_domain_len)
  127:     return FALSE;
  128: 
  129:   if(!strcasecompare(cooke_domain, hostname + hostname_len-cookie_domain_len))
  130:     return FALSE;
  131: 
  132:   /* A lead char of cookie_domain is not '.'.
  133:      RFC6265 4.1.2.3. The Domain Attribute says:
  134:        For example, if the value of the Domain attribute is
  135:        "example.com", the user agent will include the cookie in the Cookie
  136:        header when making HTTP requests to example.com, www.example.com, and
  137:        www.corp.example.com.
  138:    */
  139:   if(hostname_len == cookie_domain_len)
  140:     return TRUE;
  141:   if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
  142:     return TRUE;
  143:   return FALSE;
  144: }
  145: 
  146: /*
  147:  * Return true if the given string is an IP(v4|v6) address.
  148:  */
  149: static bool isip(const char *domain)
  150: {
  151:   struct in_addr addr;
  152: #ifdef ENABLE_IPV6
  153:   struct in6_addr addr6;
  154: #endif
  155: 
  156:   if(Curl_inet_pton(AF_INET, domain, &addr)
  157: #ifdef ENABLE_IPV6
  158:      || Curl_inet_pton(AF_INET6, domain, &addr6)
  159: #endif
  160:     ) {
  161:     /* domain name given as IP address */
  162:     return TRUE;
  163:   }
  164: 
  165:   return FALSE;
  166: }
  167: 
  168: /*
  169:  * matching cookie path and url path
  170:  * RFC6265 5.1.4 Paths and Path-Match
  171:  */
  172: static bool pathmatch(const char *cookie_path, const char *request_uri)
  173: {
  174:   size_t cookie_path_len;
  175:   size_t uri_path_len;
  176:   char *uri_path = NULL;
  177:   char *pos;
  178:   bool ret = FALSE;
  179: 
  180:   /* cookie_path must not have last '/' separator. ex: /sample */
  181:   cookie_path_len = strlen(cookie_path);
  182:   if(1 == cookie_path_len) {
  183:     /* cookie_path must be '/' */
  184:     return TRUE;
  185:   }
  186: 
  187:   uri_path = strdup(request_uri);
  188:   if(!uri_path)
  189:     return FALSE;
  190:   pos = strchr(uri_path, '?');
  191:   if(pos)
  192:     *pos = 0x0;
  193: 
  194:   /* #-fragments are already cut off! */
  195:   if(0 == strlen(uri_path) || uri_path[0] != '/') {
  196:     free(uri_path);
  197:     uri_path = strdup("/");
  198:     if(!uri_path)
  199:       return FALSE;
  200:   }
  201: 
  202:   /* here, RFC6265 5.1.4 says
  203:      4. Output the characters of the uri-path from the first character up
  204:         to, but not including, the right-most %x2F ("/").
  205:      but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
  206:      without redirect.
  207:      Ignore this algorithm because /hoge is uri path for this case
  208:      (uri path is not /).
  209:    */
  210: 
  211:   uri_path_len = strlen(uri_path);
  212: 
  213:   if(uri_path_len < cookie_path_len) {
  214:     ret = FALSE;
  215:     goto pathmatched;
  216:   }
  217: 
  218:   /* not using checkprefix() because matching should be case-sensitive */
  219:   if(strncmp(cookie_path, uri_path, cookie_path_len)) {
  220:     ret = FALSE;
  221:     goto pathmatched;
  222:   }
  223: 
  224:   /* The cookie-path and the uri-path are identical. */
  225:   if(cookie_path_len == uri_path_len) {
  226:     ret = TRUE;
  227:     goto pathmatched;
  228:   }
  229: 
  230:   /* here, cookie_path_len < uri_path_len */
  231:   if(uri_path[cookie_path_len] == '/') {
  232:     ret = TRUE;
  233:     goto pathmatched;
  234:   }
  235: 
  236:   ret = FALSE;
  237: 
  238: pathmatched:
  239:   free(uri_path);
  240:   return ret;
  241: }
  242: 
  243: /*
  244:  * Return the top-level domain, for optimal hashing.
  245:  */
  246: static const char *get_top_domain(const char * const domain, size_t *outlen)
  247: {
  248:   size_t len = 0;
  249:   const char *first = NULL, *last;
  250: 
  251:   if(domain) {
  252:     len = strlen(domain);
  253:     last = memrchr(domain, '.', len);
  254:     if(last) {
  255:       first = memrchr(domain, '.', (last - domain));
  256:       if(first)
  257:         len -= (++first - domain);
  258:     }
  259:   }
  260: 
  261:   if(outlen)
  262:     *outlen = len;
  263: 
  264:   return first? first: domain;
  265: }
  266: 
  267: /*
  268:  * A case-insensitive hash for the cookie domains.
  269:  */
  270: static size_t cookie_hash_domain(const char *domain, const size_t len)
  271: {
  272:   const char *end = domain + len;
  273:   size_t h = 5381;
  274: 
  275:   while(domain < end) {
  276:     h += h << 5;
  277:     h ^= Curl_raw_toupper(*domain++);
  278:   }
  279: 
  280:   return (h % COOKIE_HASH_SIZE);
  281: }
  282: 
  283: /*
  284:  * Hash this domain.
  285:  */
  286: static size_t cookiehash(const char * const domain)
  287: {
  288:   const char *top;
  289:   size_t len;
  290: 
  291:   if(!domain || isip(domain))
  292:     return 0;
  293: 
  294:   top = get_top_domain(domain, &len);
  295:   return cookie_hash_domain(top, len);
  296: }
  297: 
  298: /*
  299:  * cookie path sanitize
  300:  */
  301: static char *sanitize_cookie_path(const char *cookie_path)
  302: {
  303:   size_t len;
  304:   char *new_path = strdup(cookie_path);
  305:   if(!new_path)
  306:     return NULL;
  307: 
  308:   /* some stupid site sends path attribute with '"'. */
  309:   len = strlen(new_path);
  310:   if(new_path[0] == '\"') {
  311:     memmove((void *)new_path, (const void *)(new_path + 1), len);
  312:     len--;
  313:   }
  314:   if(len && (new_path[len - 1] == '\"')) {
  315:     new_path[len - 1] = 0x0;
  316:     len--;
  317:   }
  318: 
  319:   /* RFC6265 5.2.4 The Path Attribute */
  320:   if(new_path[0] != '/') {
  321:     /* Let cookie-path be the default-path. */
  322:     free(new_path);
  323:     new_path = strdup("/");
  324:     return new_path;
  325:   }
  326: 
  327:   /* convert /hoge/ to /hoge */
  328:   if(len && new_path[len - 1] == '/') {
  329:     new_path[len - 1] = 0x0;
  330:   }
  331: 
  332:   return new_path;
  333: }
  334: 
  335: /*
  336:  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
  337:  *
  338:  * NOTE: OOM or cookie parsing failures are ignored.
  339:  */
  340: void Curl_cookie_loadfiles(struct Curl_easy *data)
  341: {
  342:   struct curl_slist *list = data->change.cookielist;
  343:   if(list) {
  344:     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
  345:     while(list) {
  346:       struct CookieInfo *newcookies = Curl_cookie_init(data,
  347:                                         list->data,
  348:                                         data->cookies,
  349:                                         data->set.cookiesession);
  350:       if(!newcookies)
  351:         /* Failure may be due to OOM or a bad cookie; both are ignored
  352:          * but only the first should be
  353:          */
  354:         infof(data, "ignoring failed cookie_init for %s\n", list->data);
  355:       else
  356:         data->cookies = newcookies;
  357:       list = list->next;
  358:     }
  359:     curl_slist_free_all(data->change.cookielist); /* clean up list */
  360:     data->change.cookielist = NULL; /* don't do this again! */
  361:     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
  362:   }
  363: }
  364: 
  365: /*
  366:  * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
  367:  * that will be freed before the allocated string is stored there.
  368:  *
  369:  * It is meant to easily replace strdup()
  370:  */
  371: static void strstore(char **str, const char *newstr)
  372: {
  373:   free(*str);
  374:   *str = strdup(newstr);
  375: }
  376: 
  377: /*
  378:  * remove_expired() removes expired cookies.
  379:  */
  380: static void remove_expired(struct CookieInfo *cookies)
  381: {
  382:   struct Cookie *co, *nx;
  383:   curl_off_t now = (curl_off_t)time(NULL);
  384:   unsigned int i;
  385: 
  386:   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
  387:     struct Cookie *pv = NULL;
  388:     co = cookies->cookies[i];
  389:     while(co) {
  390:       nx = co->next;
  391:       if(co->expires && co->expires < now) {
  392:         if(!pv) {
  393:           cookies->cookies[i] = co->next;
  394:         }
  395:         else {
  396:           pv->next = co->next;
  397:         }
  398:         cookies->numcookies--;
  399:         freecookie(co);
  400:       }
  401:       else {
  402:         pv = co;
  403:       }
  404:       co = nx;
  405:     }
  406:   }
  407: }
  408: 
  409: /* Make sure domain contains a dot or is localhost. */
  410: static bool bad_domain(const char *domain)
  411: {
  412:   return !strchr(domain, '.') && !strcasecompare(domain, "localhost");
  413: }
  414: 
  415: /****************************************************************************
  416:  *
  417:  * Curl_cookie_add()
  418:  *
  419:  * Add a single cookie line to the cookie keeping object.
  420:  *
  421:  * Be aware that sometimes we get an IP-only host name, and that might also be
  422:  * a numerical IPv6 address.
  423:  *
  424:  * Returns NULL on out of memory or invalid cookie. This is suboptimal,
  425:  * as they should be treated separately.
  426:  ***************************************************************************/
  427: 
  428: struct Cookie *
  429: Curl_cookie_add(struct Curl_easy *data,
  430:                 /* The 'data' pointer here may be NULL at times, and thus
  431:                    must only be used very carefully for things that can deal
  432:                    with data being NULL. Such as infof() and similar */
  433: 
  434:                 struct CookieInfo *c,
  435:                 bool httpheader, /* TRUE if HTTP header-style line */
  436:                 bool noexpire, /* if TRUE, skip remove_expired() */
  437:                 char *lineptr,   /* first character of the line */
  438:                 const char *domain, /* default domain */
  439:                 const char *path,   /* full path used when this cookie is set,
  440:                                        used to get default path for the cookie
  441:                                        unless set */
  442:                 bool secure)  /* TRUE if connection is over secure origin */
  443: {
  444:   struct Cookie *clist;
  445:   struct Cookie *co;
  446:   struct Cookie *lastc = NULL;
  447:   time_t now = time(NULL);
  448:   bool replace_old = FALSE;
  449:   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
  450:   size_t myhash;
  451: 
  452: #ifdef CURL_DISABLE_VERBOSE_STRINGS
  453:   (void)data;
  454: #endif
  455: 
  456:   /* First, alloc and init a new struct for it */
  457:   co = calloc(1, sizeof(struct Cookie));
  458:   if(!co)
  459:     return NULL; /* bail out if we're this low on memory */
  460: 
  461:   if(httpheader) {
  462:     /* This line was read off a HTTP-header */
  463:     char name[MAX_NAME];
  464:     char what[MAX_NAME];
  465:     const char *ptr;
  466:     const char *semiptr;
  467: 
  468:     size_t linelength = strlen(lineptr);
  469:     if(linelength > MAX_COOKIE_LINE) {
  470:       /* discard overly long lines at once */
  471:       free(co);
  472:       return NULL;
  473:     }
  474: 
  475:     semiptr = strchr(lineptr, ';'); /* first, find a semicolon */
  476: 
  477:     while(*lineptr && ISBLANK(*lineptr))
  478:       lineptr++;
  479: 
  480:     ptr = lineptr;
  481:     do {
  482:       /* we have a <what>=<this> pair or a stand-alone word here */
  483:       name[0] = what[0] = 0; /* init the buffers */
  484:       if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%"
  485:                      MAX_NAME_TXT "[^;\r\n]",
  486:                      name, what)) {
  487:         /* Use strstore() below to properly deal with received cookie
  488:            headers that have the same string property set more than once,
  489:            and then we use the last one. */
  490:         const char *whatptr;
  491:         bool done = FALSE;
  492:         bool sep;
  493:         size_t len = strlen(what);
  494:         size_t nlen = strlen(name);
  495:         const char *endofn = &ptr[ nlen ];
  496: 
  497:         if(nlen >= (MAX_NAME-1) || len >= (MAX_NAME-1) ||
  498:            ((nlen + len) > MAX_NAME)) {
  499:           /* too long individual name or contents, or too long combination of
  500:              name + contents. Chrome and Firefox support 4095 or 4096 bytes
  501:              combo. */
  502:           freecookie(co);
  503:           infof(data, "oversized cookie dropped, name/val %zu + %zu bytes\n",
  504:                 nlen, len);
  505:           return NULL;
  506:         }
  507: 
  508:         /* name ends with a '=' ? */
  509:         sep = (*endofn == '=')?TRUE:FALSE;
  510: 
  511:         if(nlen) {
  512:           endofn--; /* move to the last character */
  513:           if(ISBLANK(*endofn)) {
  514:             /* skip trailing spaces in name */
  515:             while(*endofn && ISBLANK(*endofn) && nlen) {
  516:               endofn--;
  517:               nlen--;
  518:             }
  519:             name[nlen] = 0; /* new end of name */
  520:           }
  521:         }
  522: 
  523:         /* Strip off trailing whitespace from the 'what' */
  524:         while(len && ISBLANK(what[len-1])) {
  525:           what[len-1] = 0;
  526:           len--;
  527:         }
  528: 
  529:         /* Skip leading whitespace from the 'what' */
  530:         whatptr = what;
  531:         while(*whatptr && ISBLANK(*whatptr))
  532:           whatptr++;
  533: 
  534:         /*
  535:          * Check if we have a reserved prefix set before anything else, as we
  536:          * otherwise have to test for the prefix in both the cookie name and
  537:          * "the rest". Prefixes must start with '__' and end with a '-', so
  538:          * only test for names where that can possibly be true.
  539:          */
  540:         if(nlen > 3 && name[0] == '_' && name[1] == '_') {
  541:           if(!strncmp("__Secure-", name, 9))
  542:             co->prefix |= COOKIE_PREFIX__SECURE;
  543:           else if(!strncmp("__Host-", name, 7))
  544:             co->prefix |= COOKIE_PREFIX__HOST;
  545:         }
  546: 
  547:         if(!co->name) {
  548:           /* The very first name/value pair is the actual cookie name */
  549:           if(!sep) {
  550:             /* Bad name/value pair. */
  551:             badcookie = TRUE;
  552:             break;
  553:           }
  554:           co->name = strdup(name);
  555:           co->value = strdup(whatptr);
  556:           done = TRUE;
  557:           if(!co->name || !co->value) {
  558:             badcookie = TRUE;
  559:             break;
  560:           }
  561:         }
  562:         else if(!len) {
  563:           /* this was a "<name>=" with no content, and we must allow
  564:              'secure' and 'httponly' specified this weirdly */
  565:           done = TRUE;
  566:           /*
  567:            * secure cookies are only allowed to be set when the connection is
  568:            * using a secure protocol, or when the cookie is being set by
  569:            * reading from file
  570:            */
  571:           if(strcasecompare("secure", name)) {
  572:             if(secure || !c->running) {
  573:               co->secure = TRUE;
  574:             }
  575:             else {
  576:               badcookie = TRUE;
  577:               break;
  578:             }
  579:           }
  580:           else if(strcasecompare("httponly", name))
  581:             co->httponly = TRUE;
  582:           else if(sep)
  583:             /* there was a '=' so we're not done parsing this field */
  584:             done = FALSE;
  585:         }
  586:         if(done)
  587:           ;
  588:         else if(strcasecompare("path", name)) {
  589:           strstore(&co->path, whatptr);
  590:           if(!co->path) {
  591:             badcookie = TRUE; /* out of memory bad */
  592:             break;
  593:           }
  594:           free(co->spath); /* if this is set again */
  595:           co->spath = sanitize_cookie_path(co->path);
  596:           if(!co->spath) {
  597:             badcookie = TRUE; /* out of memory bad */
  598:             break;
  599:           }
  600:         }
  601:         else if(strcasecompare("domain", name)) {
  602:           bool is_ip;
  603: 
  604:           /* Now, we make sure that our host is within the given domain,
  605:              or the given domain is not valid and thus cannot be set. */
  606: 
  607:           if('.' == whatptr[0])
  608:             whatptr++; /* ignore preceding dot */
  609: 
  610: #ifndef USE_LIBPSL
  611:           /*
  612:            * Without PSL we don't know when the incoming cookie is set on a
  613:            * TLD or otherwise "protected" suffix. To reduce risk, we require a
  614:            * dot OR the exact host name being "localhost".
  615:            */
  616:           if(bad_domain(whatptr))
  617:             domain = ":";
  618: #endif
  619: 
  620:           is_ip = isip(domain ? domain : whatptr);
  621: 
  622:           if(!domain
  623:              || (is_ip && !strcmp(whatptr, domain))
  624:              || (!is_ip && tailmatch(whatptr, domain))) {
  625:             strstore(&co->domain, whatptr);
  626:             if(!co->domain) {
  627:               badcookie = TRUE;
  628:               break;
  629:             }
  630:             if(!is_ip)
  631:               co->tailmatch = TRUE; /* we always do that if the domain name was
  632:                                        given */
  633:           }
  634:           else {
  635:             /* we did not get a tailmatch and then the attempted set domain
  636:                is not a domain to which the current host belongs. Mark as
  637:                bad. */
  638:             badcookie = TRUE;
  639:             infof(data, "skipped cookie with bad tailmatch domain: %s\n",
  640:                   whatptr);
  641:           }
  642:         }
  643:         else if(strcasecompare("version", name)) {
  644:           strstore(&co->version, whatptr);
  645:           if(!co->version) {
  646:             badcookie = TRUE;
  647:             break;
  648:           }
  649:         }
  650:         else if(strcasecompare("max-age", name)) {
  651:           /* Defined in RFC2109:
  652: 
  653:              Optional.  The Max-Age attribute defines the lifetime of the
  654:              cookie, in seconds.  The delta-seconds value is a decimal non-
  655:              negative integer.  After delta-seconds seconds elapse, the
  656:              client should discard the cookie.  A value of zero means the
  657:              cookie should be discarded immediately.
  658: 
  659:           */
  660:           strstore(&co->maxage, whatptr);
  661:           if(!co->maxage) {
  662:             badcookie = TRUE;
  663:             break;
  664:           }
  665:         }
  666:         else if(strcasecompare("expires", name)) {
  667:           strstore(&co->expirestr, whatptr);
  668:           if(!co->expirestr) {
  669:             badcookie = TRUE;
  670:             break;
  671:           }
  672:         }
  673:         /*
  674:           else this is the second (or more) name we don't know
  675:           about! */
  676:       }
  677:       else {
  678:         /* this is an "illegal" <what>=<this> pair */
  679:       }
  680: 
  681:       if(!semiptr || !*semiptr) {
  682:         /* we already know there are no more cookies */
  683:         semiptr = NULL;
  684:         continue;
  685:       }
  686: 
  687:       ptr = semiptr + 1;
  688:       while(*ptr && ISBLANK(*ptr))
  689:         ptr++;
  690:       semiptr = strchr(ptr, ';'); /* now, find the next semicolon */
  691: 
  692:       if(!semiptr && *ptr)
  693:         /* There are no more semicolons, but there's a final name=value pair
  694:            coming up */
  695:         semiptr = strchr(ptr, '\0');
  696:     } while(semiptr);
  697: 
  698:     if(co->maxage) {
  699:       CURLofft offt;
  700:       offt = curlx_strtoofft((*co->maxage == '\"')?
  701:                              &co->maxage[1]:&co->maxage[0], NULL, 10,
  702:                              &co->expires);
  703:       if(offt == CURL_OFFT_FLOW)
  704:         /* overflow, used max value */
  705:         co->expires = CURL_OFF_T_MAX;
  706:       else if(!offt) {
  707:         if(!co->expires)
  708:           /* already expired */
  709:           co->expires = 1;
  710:         else if(CURL_OFF_T_MAX - now < co->expires)
  711:           /* would overflow */
  712:           co->expires = CURL_OFF_T_MAX;
  713:         else
  714:           co->expires += now;
  715:       }
  716:     }
  717:     else if(co->expirestr) {
  718:       /* Note that if the date couldn't get parsed for whatever reason,
  719:          the cookie will be treated as a session cookie */
  720:       co->expires = Curl_getdate_capped(co->expirestr);
  721: 
  722:       /* Session cookies have expires set to 0 so if we get that back
  723:          from the date parser let's add a second to make it a
  724:          non-session cookie */
  725:       if(co->expires == 0)
  726:         co->expires = 1;
  727:       else if(co->expires < 0)
  728:         co->expires = 0;
  729:     }
  730: 
  731:     if(!badcookie && !co->domain) {
  732:       if(domain) {
  733:         /* no domain was given in the header line, set the default */
  734:         co->domain = strdup(domain);
  735:         if(!co->domain)
  736:           badcookie = TRUE;
  737:       }
  738:     }
  739: 
  740:     if(!badcookie && !co->path && path) {
  741:       /* No path was given in the header line, set the default.
  742:          Note that the passed-in path to this function MAY have a '?' and
  743:          following part that MUST not be stored as part of the path. */
  744:       char *queryp = strchr(path, '?');
  745: 
  746:       /* queryp is where the interesting part of the path ends, so now we
  747:          want to the find the last */
  748:       char *endslash;
  749:       if(!queryp)
  750:         endslash = strrchr(path, '/');
  751:       else
  752:         endslash = memrchr(path, '/', (queryp - path));
  753:       if(endslash) {
  754:         size_t pathlen = (endslash-path + 1); /* include end slash */
  755:         co->path = malloc(pathlen + 1); /* one extra for the zero byte */
  756:         if(co->path) {
  757:           memcpy(co->path, path, pathlen);
  758:           co->path[pathlen] = 0; /* zero terminate */
  759:           co->spath = sanitize_cookie_path(co->path);
  760:           if(!co->spath)
  761:             badcookie = TRUE; /* out of memory bad */
  762:         }
  763:         else
  764:           badcookie = TRUE;
  765:       }
  766:     }
  767: 
  768:     if(badcookie || !co->name) {
  769:       /* we didn't get a cookie name or a bad one,
  770:          this is an illegal line, bail out */
  771:       freecookie(co);
  772:       return NULL;
  773:     }
  774: 
  775:   }
  776:   else {
  777:     /* This line is NOT a HTTP header style line, we do offer support for
  778:        reading the odd netscape cookies-file format here */
  779:     char *ptr;
  780:     char *firstptr;
  781:     char *tok_buf = NULL;
  782:     int fields;
  783: 
  784:     /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
  785:        marked with httpOnly after the domain name are not accessible
  786:        from javascripts, but since curl does not operate at javascript
  787:        level, we include them anyway. In Firefox's cookie files, these
  788:        lines are preceded with #HttpOnly_ and then everything is
  789:        as usual, so we skip 10 characters of the line..
  790:     */
  791:     if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
  792:       lineptr += 10;
  793:       co->httponly = TRUE;
  794:     }
  795: 
  796:     if(lineptr[0]=='#') {
  797:       /* don't even try the comments */
  798:       free(co);
  799:       return NULL;
  800:     }
  801:     /* strip off the possible end-of-line characters */
  802:     ptr = strchr(lineptr, '\r');
  803:     if(ptr)
  804:       *ptr = 0; /* clear it */
  805:     ptr = strchr(lineptr, '\n');
  806:     if(ptr)
  807:       *ptr = 0; /* clear it */
  808: 
  809:     firstptr = strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
  810: 
  811:     /* Now loop through the fields and init the struct we already have
  812:        allocated */
  813:     for(ptr = firstptr, fields = 0; ptr && !badcookie;
  814:         ptr = strtok_r(NULL, "\t", &tok_buf), fields++) {
  815:       switch(fields) {
  816:       case 0:
  817:         if(ptr[0]=='.') /* skip preceding dots */
  818:           ptr++;
  819:         co->domain = strdup(ptr);
  820:         if(!co->domain)
  821:           badcookie = TRUE;
  822:         break;
  823:       case 1:
  824:         /* flag: A TRUE/FALSE value indicating if all machines within a given
  825:            domain can access the variable. Set TRUE when the cookie says
  826:            .domain.com and to false when the domain is complete www.domain.com
  827:         */
  828:         co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
  829:         break;
  830:       case 2:
  831:         /* The file format allows the path field to remain not filled in */
  832:         if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
  833:           /* only if the path doesn't look like a boolean option! */
  834:           co->path = strdup(ptr);
  835:           if(!co->path)
  836:             badcookie = TRUE;
  837:           else {
  838:             co->spath = sanitize_cookie_path(co->path);
  839:             if(!co->spath) {
  840:               badcookie = TRUE; /* out of memory bad */
  841:             }
  842:           }
  843:           break;
  844:         }
  845:         /* this doesn't look like a path, make one up! */
  846:         co->path = strdup("/");
  847:         if(!co->path)
  848:           badcookie = TRUE;
  849:         co->spath = strdup("/");
  850:         if(!co->spath)
  851:           badcookie = TRUE;
  852:         fields++; /* add a field and fall down to secure */
  853:         /* FALLTHROUGH */
  854:       case 3:
  855:         co->secure = FALSE;
  856:         if(strcasecompare(ptr, "TRUE")) {
  857:           if(secure || c->running)
  858:             co->secure = TRUE;
  859:           else
  860:             badcookie = TRUE;
  861:         }
  862:         break;
  863:       case 4:
  864:         if(curlx_strtoofft(ptr, NULL, 10, &co->expires))
  865:           badcookie = TRUE;
  866:         break;
  867:       case 5:
  868:         co->name = strdup(ptr);
  869:         if(!co->name)
  870:           badcookie = TRUE;
  871:         else {
  872:           /* For Netscape file format cookies we check prefix on the name */
  873:           if(strncasecompare("__Secure-", co->name, 9))
  874:             co->prefix |= COOKIE_PREFIX__SECURE;
  875:           else if(strncasecompare("__Host-", co->name, 7))
  876:             co->prefix |= COOKIE_PREFIX__HOST;
  877:         }
  878:         break;
  879:       case 6:
  880:         co->value = strdup(ptr);
  881:         if(!co->value)
  882:           badcookie = TRUE;
  883:         break;
  884:       }
  885:     }
  886:     if(6 == fields) {
  887:       /* we got a cookie with blank contents, fix it */
  888:       co->value = strdup("");
  889:       if(!co->value)
  890:         badcookie = TRUE;
  891:       else
  892:         fields++;
  893:     }
  894: 
  895:     if(!badcookie && (7 != fields))
  896:       /* we did not find the sufficient number of fields */
  897:       badcookie = TRUE;
  898: 
  899:     if(badcookie) {
  900:       freecookie(co);
  901:       return NULL;
  902:     }
  903: 
  904:   }
  905: 
  906:   if(co->prefix & COOKIE_PREFIX__SECURE) {
  907:     /* The __Secure- prefix only requires that the cookie be set secure */
  908:     if(!co->secure) {
  909:       freecookie(co);
  910:       return NULL;
  911:     }
  912:   }
  913:   if(co->prefix & COOKIE_PREFIX__HOST) {
  914:     /*
  915:      * The __Host- prefix requires the cookie to be secure, have a "/" path
  916:      * and not have a domain set.
  917:      */
  918:     if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch)
  919:       ;
  920:     else {
  921:       freecookie(co);
  922:       return NULL;
  923:     }
  924:   }
  925: 
  926:   if(!c->running &&    /* read from a file */
  927:      c->newsession &&  /* clean session cookies */
  928:      !co->expires) {   /* this is a session cookie since it doesn't expire! */
  929:     freecookie(co);
  930:     return NULL;
  931:   }
  932: 
  933:   co->livecookie = c->running;
  934:   co->creationtime = ++c->lastct;
  935: 
  936:   /* now, we have parsed the incoming line, we must now check if this
  937:      supersedes an already existing cookie, which it may if the previous have
  938:      the same domain and path as this */
  939: 
  940:   /* at first, remove expired cookies */
  941:   if(!noexpire)
  942:     remove_expired(c);
  943: 
  944: #ifdef USE_LIBPSL
  945:   /* Check if the domain is a Public Suffix and if yes, ignore the cookie. */
  946:   if(domain && co->domain && !isip(co->domain)) {
  947:     const psl_ctx_t *psl = Curl_psl_use(data);
  948:     int acceptable;
  949: 
  950:     if(psl) {
  951:       acceptable = psl_is_cookie_domain_acceptable(psl, domain, co->domain);
  952:       Curl_psl_release(data);
  953:     }
  954:     else
  955:       acceptable = !bad_domain(domain);
  956: 
  957:     if(!acceptable) {
  958:       infof(data, "cookie '%s' dropped, domain '%s' must not "
  959:                   "set cookies for '%s'\n", co->name, domain, co->domain);
  960:       freecookie(co);
  961:       return NULL;
  962:     }
  963:   }
  964: #endif
  965: 
  966:   myhash = cookiehash(co->domain);
  967:   clist = c->cookies[myhash];
  968:   replace_old = FALSE;
  969:   while(clist) {
  970:     if(strcasecompare(clist->name, co->name)) {
  971:       /* the names are identical */
  972: 
  973:       if(clist->domain && co->domain) {
  974:         if(strcasecompare(clist->domain, co->domain) &&
  975:           (clist->tailmatch == co->tailmatch))
  976:           /* The domains are identical */
  977:           replace_old = TRUE;
  978:       }
  979:       else if(!clist->domain && !co->domain)
  980:         replace_old = TRUE;
  981: 
  982:       if(replace_old) {
  983:         /* the domains were identical */
  984: 
  985:         if(clist->spath && co->spath) {
  986:           if(clist->secure && !co->secure && !secure) {
  987:             size_t cllen;
  988:             const char *sep;
  989: 
  990:             /*
  991:              * A non-secure cookie may not overlay an existing secure cookie.
  992:              * For an existing cookie "a" with path "/login", refuse a new
  993:              * cookie "a" with for example path "/login/en", while the path
  994:              * "/loginhelper" is ok.
  995:              */
  996: 
  997:             sep = strchr(clist->spath + 1, '/');
  998: 
  999:             if(sep)
 1000:               cllen = sep - clist->spath;
 1001:             else
 1002:               cllen = strlen(clist->spath);
 1003: 
 1004:             if(strncasecompare(clist->spath, co->spath, cllen)) {
 1005:               freecookie(co);
 1006:               return NULL;
 1007:             }
 1008:           }
 1009:           else if(strcasecompare(clist->spath, co->spath))
 1010:             replace_old = TRUE;
 1011:           else
 1012:             replace_old = FALSE;
 1013:         }
 1014:         else if(!clist->spath && !co->spath)
 1015:           replace_old = TRUE;
 1016:         else
 1017:           replace_old = FALSE;
 1018: 
 1019:       }
 1020: 
 1021:       if(replace_old && !co->livecookie && clist->livecookie) {
 1022:         /* Both cookies matched fine, except that the already present
 1023:            cookie is "live", which means it was set from a header, while
 1024:            the new one isn't "live" and thus only read from a file. We let
 1025:            live cookies stay alive */
 1026: 
 1027:         /* Free the newcomer and get out of here! */
 1028:         freecookie(co);
 1029:         return NULL;
 1030:       }
 1031: 
 1032:       if(replace_old) {
 1033:         co->next = clist->next; /* get the next-pointer first */
 1034: 
 1035:         /* when replacing, creationtime is kept from old */
 1036:         co->creationtime = clist->creationtime;
 1037: 
 1038:         /* then free all the old pointers */
 1039:         free(clist->name);
 1040:         free(clist->value);
 1041:         free(clist->domain);
 1042:         free(clist->path);
 1043:         free(clist->spath);
 1044:         free(clist->expirestr);
 1045:         free(clist->version);
 1046:         free(clist->maxage);
 1047: 
 1048:         *clist = *co;  /* then store all the new data */
 1049: 
 1050:         free(co);   /* free the newly allocated memory */
 1051:         co = clist; /* point to the previous struct instead */
 1052: 
 1053:         /* We have replaced a cookie, now skip the rest of the list but
 1054:            make sure the 'lastc' pointer is properly set */
 1055:         do {
 1056:           lastc = clist;
 1057:           clist = clist->next;
 1058:         } while(clist);
 1059:         break;
 1060:       }
 1061:     }
 1062:     lastc = clist;
 1063:     clist = clist->next;
 1064:   }
 1065: 
 1066:   if(c->running)
 1067:     /* Only show this when NOT reading the cookies from a file */
 1068:     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
 1069:           "expire %" CURL_FORMAT_CURL_OFF_T "\n",
 1070:           replace_old?"Replaced":"Added", co->name, co->value,
 1071:           co->domain, co->path, co->expires);
 1072: 
 1073:   if(!replace_old) {
 1074:     /* then make the last item point on this new one */
 1075:     if(lastc)
 1076:       lastc->next = co;
 1077:     else
 1078:       c->cookies[myhash] = co;
 1079:     c->numcookies++; /* one more cookie in the jar */
 1080:   }
 1081: 
 1082:   return co;
 1083: }
 1084: 
 1085: 
 1086: /*****************************************************************************
 1087:  *
 1088:  * Curl_cookie_init()
 1089:  *
 1090:  * Inits a cookie struct to read data from a local file. This is always
 1091:  * called before any cookies are set. File may be NULL.
 1092:  *
 1093:  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
 1094:  *
 1095:  * Note that 'data' might be called as NULL pointer.
 1096:  *
 1097:  * Returns NULL on out of memory. Invalid cookies are ignored.
 1098:  ****************************************************************************/
 1099: struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
 1100:                                     const char *file,
 1101:                                     struct CookieInfo *inc,
 1102:                                     bool newsession)
 1103: {
 1104:   struct CookieInfo *c;
 1105:   FILE *fp = NULL;
 1106:   bool fromfile = TRUE;
 1107:   char *line = NULL;
 1108: 
 1109:   if(NULL == inc) {
 1110:     /* we didn't get a struct, create one */
 1111:     c = calloc(1, sizeof(struct CookieInfo));
 1112:     if(!c)
 1113:       return NULL; /* failed to get memory */
 1114:     c->filename = strdup(file?file:"none"); /* copy the name just in case */
 1115:     if(!c->filename)
 1116:       goto fail; /* failed to get memory */
 1117:   }
 1118:   else {
 1119:     /* we got an already existing one, use that */
 1120:     c = inc;
 1121:   }
 1122:   c->running = FALSE; /* this is not running, this is init */
 1123: 
 1124:   if(file && !strcmp(file, "-")) {
 1125:     fp = stdin;
 1126:     fromfile = FALSE;
 1127:   }
 1128:   else if(file && !*file) {
 1129:     /* points to a "" string */
 1130:     fp = NULL;
 1131:   }
 1132:   else
 1133:     fp = file?fopen(file, FOPEN_READTEXT):NULL;
 1134: 
 1135:   c->newsession = newsession; /* new session? */
 1136: 
 1137:   if(fp) {
 1138:     char *lineptr;
 1139:     bool headerline;
 1140: 
 1141:     line = malloc(MAX_COOKIE_LINE);
 1142:     if(!line)
 1143:       goto fail;
 1144:     while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) {
 1145:       if(checkprefix("Set-Cookie:", line)) {
 1146:         /* This is a cookie line, get it! */
 1147:         lineptr = &line[11];
 1148:         headerline = TRUE;
 1149:       }
 1150:       else {
 1151:         lineptr = line;
 1152:         headerline = FALSE;
 1153:       }
 1154:       while(*lineptr && ISBLANK(*lineptr))
 1155:         lineptr++;
 1156: 
 1157:       Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE);
 1158:     }
 1159:     free(line); /* free the line buffer */
 1160:     remove_expired(c); /* run this once, not on every cookie */
 1161: 
 1162:     if(fromfile)
 1163:       fclose(fp);
 1164:   }
 1165: 
 1166:   c->running = TRUE;          /* now, we're running */
 1167:   if(data)
 1168:     data->state.cookie_engine = TRUE;
 1169: 
 1170:   return c;
 1171: 
 1172: fail:
 1173:   free(line);
 1174:   if(!inc)
 1175:     /* Only clean up if we allocated it here, as the original could still be in
 1176:      * use by a share handle */
 1177:     Curl_cookie_cleanup(c);
 1178:   if(fromfile && fp)
 1179:     fclose(fp);
 1180:   return NULL; /* out of memory */
 1181: }
 1182: 
 1183: /* sort this so that the longest path gets before the shorter path */
 1184: static int cookie_sort(const void *p1, const void *p2)
 1185: {
 1186:   struct Cookie *c1 = *(struct Cookie **)p1;
 1187:   struct Cookie *c2 = *(struct Cookie **)p2;
 1188:   size_t l1, l2;
 1189: 
 1190:   /* 1 - compare cookie path lengths */
 1191:   l1 = c1->path ? strlen(c1->path) : 0;
 1192:   l2 = c2->path ? strlen(c2->path) : 0;
 1193: 
 1194:   if(l1 != l2)
 1195:     return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
 1196: 
 1197:   /* 2 - compare cookie domain lengths */
 1198:   l1 = c1->domain ? strlen(c1->domain) : 0;
 1199:   l2 = c2->domain ? strlen(c2->domain) : 0;
 1200: 
 1201:   if(l1 != l2)
 1202:     return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
 1203: 
 1204:   /* 3 - compare cookie name lengths */
 1205:   l1 = c1->name ? strlen(c1->name) : 0;
 1206:   l2 = c2->name ? strlen(c2->name) : 0;
 1207: 
 1208:   if(l1 != l2)
 1209:     return (l2 > l1) ? 1 : -1;
 1210: 
 1211:   /* 4 - compare cookie creation time */
 1212:   return (c2->creationtime > c1->creationtime) ? 1 : -1;
 1213: }
 1214: 
 1215: /* sort cookies only according to creation time */
 1216: static int cookie_sort_ct(const void *p1, const void *p2)
 1217: {
 1218:   struct Cookie *c1 = *(struct Cookie **)p1;
 1219:   struct Cookie *c2 = *(struct Cookie **)p2;
 1220: 
 1221:   return (c2->creationtime > c1->creationtime) ? 1 : -1;
 1222: }
 1223: 
 1224: #define CLONE(field)                     \
 1225:   do {                                   \
 1226:     if(src->field) {                     \
 1227:       d->field = strdup(src->field);     \
 1228:       if(!d->field)                      \
 1229:         goto fail;                       \
 1230:     }                                    \
 1231:   } while(0)
 1232: 
 1233: static struct Cookie *dup_cookie(struct Cookie *src)
 1234: {
 1235:   struct Cookie *d = calloc(sizeof(struct Cookie), 1);
 1236:   if(d) {
 1237:     CLONE(expirestr);
 1238:     CLONE(domain);
 1239:     CLONE(path);
 1240:     CLONE(spath);
 1241:     CLONE(name);
 1242:     CLONE(value);
 1243:     CLONE(maxage);
 1244:     CLONE(version);
 1245:     d->expires = src->expires;
 1246:     d->tailmatch = src->tailmatch;
 1247:     d->secure = src->secure;
 1248:     d->livecookie = src->livecookie;
 1249:     d->httponly = src->httponly;
 1250:     d->creationtime = src->creationtime;
 1251:   }
 1252:   return d;
 1253: 
 1254:   fail:
 1255:   freecookie(d);
 1256:   return NULL;
 1257: }
 1258: 
 1259: /*****************************************************************************
 1260:  *
 1261:  * Curl_cookie_getlist()
 1262:  *
 1263:  * For a given host and path, return a linked list of cookies that the
 1264:  * client should send to the server if used now. The secure boolean informs
 1265:  * the cookie if a secure connection is achieved or not.
 1266:  *
 1267:  * It shall only return cookies that haven't expired.
 1268:  *
 1269:  ****************************************************************************/
 1270: 
 1271: struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
 1272:                                    const char *host, const char *path,
 1273:                                    bool secure)
 1274: {
 1275:   struct Cookie *newco;
 1276:   struct Cookie *co;
 1277:   struct Cookie *mainco = NULL;
 1278:   size_t matches = 0;
 1279:   bool is_ip;
 1280:   const size_t myhash = cookiehash(host);
 1281: 
 1282:   if(!c || !c->cookies[myhash])
 1283:     return NULL; /* no cookie struct or no cookies in the struct */
 1284: 
 1285:   /* at first, remove expired cookies */
 1286:   remove_expired(c);
 1287: 
 1288:   /* check if host is an IP(v4|v6) address */
 1289:   is_ip = isip(host);
 1290: 
 1291:   co = c->cookies[myhash];
 1292: 
 1293:   while(co) {
 1294:     /* if the cookie requires we're secure we must only continue if we are! */
 1295:     if(co->secure?secure:TRUE) {
 1296: 
 1297:       /* now check if the domain is correct */
 1298:       if(!co->domain ||
 1299:          (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
 1300:          ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
 1301:         /* the right part of the host matches the domain stuff in the
 1302:            cookie data */
 1303: 
 1304:         /* now check the left part of the path with the cookies path
 1305:            requirement */
 1306:         if(!co->spath || pathmatch(co->spath, path) ) {
 1307: 
 1308:           /* and now, we know this is a match and we should create an
 1309:              entry for the return-linked-list */
 1310: 
 1311:           newco = dup_cookie(co);
 1312:           if(newco) {
 1313:             /* then modify our next */
 1314:             newco->next = mainco;
 1315: 
 1316:             /* point the main to us */
 1317:             mainco = newco;
 1318: 
 1319:             matches++;
 1320:           }
 1321:           else
 1322:             goto fail;
 1323:         }
 1324:       }
 1325:     }
 1326:     co = co->next;
 1327:   }
 1328: 
 1329:   if(matches) {
 1330:     /* Now we need to make sure that if there is a name appearing more than
 1331:        once, the longest specified path version comes first. To make this
 1332:        the swiftest way, we just sort them all based on path length. */
 1333:     struct Cookie **array;
 1334:     size_t i;
 1335: 
 1336:     /* alloc an array and store all cookie pointers */
 1337:     array = malloc(sizeof(struct Cookie *) * matches);
 1338:     if(!array)
 1339:       goto fail;
 1340: 
 1341:     co = mainco;
 1342: 
 1343:     for(i = 0; co; co = co->next)
 1344:       array[i++] = co;
 1345: 
 1346:     /* now sort the cookie pointers in path length order */
 1347:     qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
 1348: 
 1349:     /* remake the linked list order according to the new order */
 1350: 
 1351:     mainco = array[0]; /* start here */
 1352:     for(i = 0; i<matches-1; i++)
 1353:       array[i]->next = array[i + 1];
 1354:     array[matches-1]->next = NULL; /* terminate the list */
 1355: 
 1356:     free(array); /* remove the temporary data again */
 1357:   }
 1358: 
 1359:   return mainco; /* return the new list */
 1360: 
 1361: fail:
 1362:   /* failure, clear up the allocated chain and return NULL */
 1363:   Curl_cookie_freelist(mainco);
 1364:   return NULL;
 1365: }
 1366: 
 1367: /*****************************************************************************
 1368:  *
 1369:  * Curl_cookie_clearall()
 1370:  *
 1371:  * Clear all existing cookies and reset the counter.
 1372:  *
 1373:  ****************************************************************************/
 1374: void Curl_cookie_clearall(struct CookieInfo *cookies)
 1375: {
 1376:   if(cookies) {
 1377:     unsigned int i;
 1378:     for(i = 0; i < COOKIE_HASH_SIZE; i++) {
 1379:       Curl_cookie_freelist(cookies->cookies[i]);
 1380:       cookies->cookies[i] = NULL;
 1381:     }
 1382:     cookies->numcookies = 0;
 1383:   }
 1384: }
 1385: 
 1386: /*****************************************************************************
 1387:  *
 1388:  * Curl_cookie_freelist()
 1389:  *
 1390:  * Free a list of cookies previously returned by Curl_cookie_getlist();
 1391:  *
 1392:  ****************************************************************************/
 1393: 
 1394: void Curl_cookie_freelist(struct Cookie *co)
 1395: {
 1396:   struct Cookie *next;
 1397:   while(co) {
 1398:     next = co->next;
 1399:     freecookie(co);
 1400:     co = next;
 1401:   }
 1402: }
 1403: 
 1404: 
 1405: /*****************************************************************************
 1406:  *
 1407:  * Curl_cookie_clearsess()
 1408:  *
 1409:  * Free all session cookies in the cookies list.
 1410:  *
 1411:  ****************************************************************************/
 1412: void Curl_cookie_clearsess(struct CookieInfo *cookies)
 1413: {
 1414:   struct Cookie *first, *curr, *next, *prev = NULL;
 1415:   unsigned int i;
 1416: 
 1417:   if(!cookies)
 1418:     return;
 1419: 
 1420:   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
 1421:     if(!cookies->cookies[i])
 1422:       continue;
 1423: 
 1424:     first = curr = prev = cookies->cookies[i];
 1425: 
 1426:     for(; curr; curr = next) {
 1427:       next = curr->next;
 1428:       if(!curr->expires) {
 1429:         if(first == curr)
 1430:           first = next;
 1431: 
 1432:         if(prev == curr)
 1433:           prev = next;
 1434:         else
 1435:           prev->next = next;
 1436: 
 1437:         freecookie(curr);
 1438:         cookies->numcookies--;
 1439:       }
 1440:       else
 1441:         prev = curr;
 1442:     }
 1443: 
 1444:     cookies->cookies[i] = first;
 1445:   }
 1446: }
 1447: 
 1448: 
 1449: /*****************************************************************************
 1450:  *
 1451:  * Curl_cookie_cleanup()
 1452:  *
 1453:  * Free a "cookie object" previous created with Curl_cookie_init().
 1454:  *
 1455:  ****************************************************************************/
 1456: void Curl_cookie_cleanup(struct CookieInfo *c)
 1457: {
 1458:   if(c) {
 1459:     unsigned int i;
 1460:     free(c->filename);
 1461:     for(i = 0; i < COOKIE_HASH_SIZE; i++)
 1462:       Curl_cookie_freelist(c->cookies[i]);
 1463:     free(c); /* free the base struct as well */
 1464:   }
 1465: }
 1466: 
 1467: /* get_netscape_format()
 1468:  *
 1469:  * Formats a string for Netscape output file, w/o a newline at the end.
 1470:  *
 1471:  * Function returns a char * to a formatted line. Has to be free()d
 1472: */
 1473: static char *get_netscape_format(const struct Cookie *co)
 1474: {
 1475:   return aprintf(
 1476:     "%s"     /* httponly preamble */
 1477:     "%s%s\t" /* domain */
 1478:     "%s\t"   /* tailmatch */
 1479:     "%s\t"   /* path */
 1480:     "%s\t"   /* secure */
 1481:     "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
 1482:     "%s\t"   /* name */
 1483:     "%s",    /* value */
 1484:     co->httponly?"#HttpOnly_":"",
 1485:     /* Make sure all domains are prefixed with a dot if they allow
 1486:        tailmatching. This is Mozilla-style. */
 1487:     (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
 1488:     co->domain?co->domain:"unknown",
 1489:     co->tailmatch?"TRUE":"FALSE",
 1490:     co->path?co->path:"/",
 1491:     co->secure?"TRUE":"FALSE",
 1492:     co->expires,
 1493:     co->name,
 1494:     co->value?co->value:"");
 1495: }
 1496: 
 1497: /*
 1498:  * cookie_output()
 1499:  *
 1500:  * Writes all internally known cookies to the specified file. Specify
 1501:  * "-" as file name to write to stdout.
 1502:  *
 1503:  * The function returns non-zero on write failure.
 1504:  */
 1505: static int cookie_output(struct Curl_easy *data,
 1506:                          struct CookieInfo *c, const char *filename)
 1507: {
 1508:   struct Cookie *co;
 1509:   FILE *out = NULL;
 1510:   bool use_stdout = FALSE;
 1511:   char *tempstore = NULL;
 1512:   bool error = false;
 1513: 
 1514:   if(!c)
 1515:     /* no cookie engine alive */
 1516:     return 0;
 1517: 
 1518:   /* at first, remove expired cookies */
 1519:   remove_expired(c);
 1520: 
 1521:   if(!strcmp("-", filename)) {
 1522:     /* use stdout */
 1523:     out = stdout;
 1524:     use_stdout = TRUE;
 1525:   }
 1526:   else {
 1527:     unsigned char randsuffix[9];
 1528: 
 1529:     if(Curl_rand_hex(data, randsuffix, sizeof(randsuffix)))
 1530:       return 2;
 1531: 
 1532:     tempstore = aprintf("%s.%s.tmp", filename, randsuffix);
 1533:     if(!tempstore)
 1534:       return 1;
 1535: 
 1536:     out = fopen(tempstore, FOPEN_WRITETEXT);
 1537:     if(!out)
 1538:       goto error;
 1539:   }
 1540: 
 1541:   fputs("# Netscape HTTP Cookie File\n"
 1542:         "# https://curl.haxx.se/docs/http-cookies.html\n"
 1543:         "# This file was generated by libcurl! Edit at your own risk.\n\n",
 1544:         out);
 1545: 
 1546:   if(c->numcookies) {
 1547:     unsigned int i;
 1548:     size_t nvalid = 0;
 1549:     struct Cookie **array;
 1550: 
 1551:     array = calloc(1, sizeof(struct Cookie *) * c->numcookies);
 1552:     if(!array) {
 1553:       goto error;
 1554:     }
 1555: 
 1556:     /* only sort the cookies with a domain property */
 1557:     for(i = 0; i < COOKIE_HASH_SIZE; i++) {
 1558:       for(co = c->cookies[i]; co; co = co->next) {
 1559:         if(!co->domain)
 1560:           continue;
 1561:         array[nvalid++] = co;
 1562:       }
 1563:     }
 1564: 
 1565:     qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct);
 1566: 
 1567:     for(i = 0; i < nvalid; i++) {
 1568:       char *format_ptr = get_netscape_format(array[i]);
 1569:       if(format_ptr == NULL) {
 1570:         fprintf(out, "#\n# Fatal libcurl error\n");
 1571:         free(array);
 1572:         goto error;
 1573:       }
 1574:       fprintf(out, "%s\n", format_ptr);
 1575:       free(format_ptr);
 1576:     }
 1577: 
 1578:     free(array);
 1579:   }
 1580: 
 1581:   if(!use_stdout) {
 1582:     fclose(out);
 1583:     out = NULL;
 1584:     if(Curl_rename(tempstore, filename)) {
 1585:       unlink(tempstore);
 1586:       goto error;
 1587:     }
 1588:   }
 1589: 
 1590:   goto cleanup;
 1591: error:
 1592:   error = true;
 1593: cleanup:
 1594:   if(out && !use_stdout)
 1595:     fclose(out);
 1596:   free(tempstore);
 1597:   return error ? 1 : 0;
 1598: }
 1599: 
 1600: static struct curl_slist *cookie_list(struct Curl_easy *data)
 1601: {
 1602:   struct curl_slist *list = NULL;
 1603:   struct curl_slist *beg;
 1604:   struct Cookie *c;
 1605:   char *line;
 1606:   unsigned int i;
 1607: 
 1608:   if((data->cookies == NULL) ||
 1609:       (data->cookies->numcookies == 0))
 1610:     return NULL;
 1611: 
 1612:   for(i = 0; i < COOKIE_HASH_SIZE; i++) {
 1613:     for(c = data->cookies->cookies[i]; c; c = c->next) {
 1614:       if(!c->domain)
 1615:         continue;
 1616:       line = get_netscape_format(c);
 1617:       if(!line) {
 1618:         curl_slist_free_all(list);
 1619:         return NULL;
 1620:       }
 1621:       beg = Curl_slist_append_nodup(list, line);
 1622:       if(!beg) {
 1623:         free(line);
 1624:         curl_slist_free_all(list);
 1625:         return NULL;
 1626:       }
 1627:       list = beg;
 1628:     }
 1629:   }
 1630: 
 1631:   return list;
 1632: }
 1633: 
 1634: struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
 1635: {
 1636:   struct curl_slist *list;
 1637:   Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 1638:   list = cookie_list(data);
 1639:   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
 1640:   return list;
 1641: }
 1642: 
 1643: void Curl_flush_cookies(struct Curl_easy *data, bool cleanup)
 1644: {
 1645:   if(data->set.str[STRING_COOKIEJAR]) {
 1646:     if(data->change.cookielist) {
 1647:       /* If there is a list of cookie files to read, do it first so that
 1648:          we have all the told files read before we write the new jar.
 1649:          Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
 1650:       Curl_cookie_loadfiles(data);
 1651:     }
 1652: 
 1653:     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 1654: 
 1655:     /* if we have a destination file for all the cookies to get dumped to */
 1656:     if(cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]))
 1657:       infof(data, "WARNING: failed to save cookies in %s\n",
 1658:             data->set.str[STRING_COOKIEJAR]);
 1659:   }
 1660:   else {
 1661:     if(cleanup && data->change.cookielist) {
 1662:       /* since nothing is written, we can just free the list of cookie file
 1663:          names */
 1664:       curl_slist_free_all(data->change.cookielist); /* clean up list */
 1665:       data->change.cookielist = NULL;
 1666:     }
 1667:     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
 1668:   }
 1669: 
 1670:   if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
 1671:     Curl_cookie_cleanup(data->cookies);
 1672:     data->cookies = NULL;
 1673:   }
 1674:   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
 1675: }
 1676: 
 1677: #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>