Annotation of embedaddon/curl/lib/cookie.c, revision 1.1

1.1     ! misho       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>