cookie.c
Go to the documentation of this file.
00001 /***************************************************************************
00002  *                                  _   _ ____  _
00003  *  Project                     ___| | | |  _ \| |
00004  *                             / __| | | | |_) | |
00005  *                            | (__| |_| |  _ <| |___
00006  *                             \___|\___/|_| \_\_____|
00007  *
00008  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
00009  *
00010  * This software is licensed as described in the file COPYING, which
00011  * you should have received as part of this distribution. The terms
00012  * are also available at https://curl.haxx.se/docs/copyright.html.
00013  *
00014  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
00015  * copies of the Software, and permit persons to whom the Software is
00016  * furnished to do so, under the terms of the COPYING file.
00017  *
00018  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
00019  * KIND, either express or implied.
00020  *
00021  ***************************************************************************/
00022 
00023 /***
00024 
00025 
00026 RECEIVING COOKIE INFORMATION
00027 ============================
00028 
00029 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
00030                     const char *file, struct CookieInfo *inc, bool newsession);
00031 
00032         Inits a cookie struct to store data in a local file. This is always
00033         called before any cookies are set.
00034 
00035 struct Cookie *Curl_cookie_add(struct Curl_easy *data,
00036                  struct CookieInfo *c, bool httpheader, char *lineptr,
00037                  const char *domain, const char *path);
00038 
00039         The 'lineptr' parameter is a full "Set-cookie:" line as
00040         received from a server.
00041 
00042         The function need to replace previously stored lines that this new
00043         line superceeds.
00044 
00045         It may remove lines that are expired.
00046 
00047         It should return an indication of success/error.
00048 
00049 
00050 SENDING COOKIE INFORMATION
00051 ==========================
00052 
00053 struct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie,
00054                                     char *host, char *path, bool secure);
00055 
00056         For a given host and path, return a linked list of cookies that
00057         the client should send to the server if used now. The secure
00058         boolean informs the cookie if a secure connection is achieved or
00059         not.
00060 
00061         It shall only return cookies that haven't expired.
00062 
00063 
00064 Example set of cookies:
00065 
00066     Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure
00067     Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
00068     domain=.fidelity.com; path=/ftgw; secure
00069     Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
00070     domain=.fidelity.com; path=/; secure
00071     Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
00072     domain=.fidelity.com; path=/; secure
00073     Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
00074     domain=.fidelity.com; path=/; secure
00075     Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT;
00076     domain=.fidelity.com; path=/; secure
00077     Set-cookie:
00078     Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday,
00079     13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure
00080 ****/
00081 
00082 
00083 #include "curl_setup.h"
00084 
00085 #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES)
00086 
00087 #ifdef USE_LIBPSL
00088 # include <libpsl.h>
00089 #endif
00090 
00091 #include "urldata.h"
00092 #include "cookie.h"
00093 #include "strtok.h"
00094 #include "sendf.h"
00095 #include "slist.h"
00096 #include "share.h"
00097 #include "strtoofft.h"
00098 #include "strcase.h"
00099 #include "curl_memrchr.h"
00100 #include "inet_pton.h"
00101 
00102 /* The last 3 #include files should be in this order */
00103 #include "curl_printf.h"
00104 #include "curl_memory.h"
00105 #include "memdebug.h"
00106 
00107 static void freecookie(struct Cookie *co)
00108 {
00109   free(co->expirestr);
00110   free(co->domain);
00111   free(co->path);
00112   free(co->spath);
00113   free(co->name);
00114   free(co->value);
00115   free(co->maxage);
00116   free(co->version);
00117   free(co);
00118 }
00119 
00120 static bool tailmatch(const char *cooke_domain, const char *hostname)
00121 {
00122   size_t cookie_domain_len = strlen(cooke_domain);
00123   size_t hostname_len = strlen(hostname);
00124 
00125   if(hostname_len < cookie_domain_len)
00126     return FALSE;
00127 
00128   if(!strcasecompare(cooke_domain, hostname+hostname_len-cookie_domain_len))
00129     return FALSE;
00130 
00131   /* A lead char of cookie_domain is not '.'.
00132      RFC6265 4.1.2.3. The Domain Attribute says:
00133        For example, if the value of the Domain attribute is
00134        "example.com", the user agent will include the cookie in the Cookie
00135        header when making HTTP requests to example.com, www.example.com, and
00136        www.corp.example.com.
00137    */
00138   if(hostname_len == cookie_domain_len)
00139     return TRUE;
00140   if('.' == *(hostname + hostname_len - cookie_domain_len - 1))
00141     return TRUE;
00142   return FALSE;
00143 }
00144 
00145 /*
00146  * matching cookie path and url path
00147  * RFC6265 5.1.4 Paths and Path-Match
00148  */
00149 static bool pathmatch(const char *cookie_path, const char *request_uri)
00150 {
00151   size_t cookie_path_len;
00152   size_t uri_path_len;
00153   char *uri_path = NULL;
00154   char *pos;
00155   bool ret = FALSE;
00156 
00157   /* cookie_path must not have last '/' separator. ex: /sample */
00158   cookie_path_len = strlen(cookie_path);
00159   if(1 == cookie_path_len) {
00160     /* cookie_path must be '/' */
00161     return TRUE;
00162   }
00163 
00164   uri_path = strdup(request_uri);
00165   if(!uri_path)
00166     return FALSE;
00167   pos = strchr(uri_path, '?');
00168   if(pos)
00169     *pos = 0x0;
00170 
00171   /* #-fragments are already cut off! */
00172   if(0 == strlen(uri_path) || uri_path[0] != '/') {
00173     free(uri_path);
00174     uri_path = strdup("/");
00175     if(!uri_path)
00176       return FALSE;
00177   }
00178 
00179   /* here, RFC6265 5.1.4 says
00180      4. Output the characters of the uri-path from the first character up
00181         to, but not including, the right-most %x2F ("/").
00182      but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site
00183      without redirect.
00184      Ignore this algorithm because /hoge is uri path for this case
00185      (uri path is not /).
00186    */
00187 
00188   uri_path_len = strlen(uri_path);
00189 
00190   if(uri_path_len < cookie_path_len) {
00191     ret = FALSE;
00192     goto pathmatched;
00193   }
00194 
00195   /* not using checkprefix() because matching should be case-sensitive */
00196   if(strncmp(cookie_path, uri_path, cookie_path_len)) {
00197     ret = FALSE;
00198     goto pathmatched;
00199   }
00200 
00201   /* The cookie-path and the uri-path are identical. */
00202   if(cookie_path_len == uri_path_len) {
00203     ret = TRUE;
00204     goto pathmatched;
00205   }
00206 
00207   /* here, cookie_path_len < url_path_len */
00208   if(uri_path[cookie_path_len] == '/') {
00209     ret = TRUE;
00210     goto pathmatched;
00211   }
00212 
00213   ret = FALSE;
00214 
00215 pathmatched:
00216   free(uri_path);
00217   return ret;
00218 }
00219 
00220 /*
00221  * cookie path sanitize
00222  */
00223 static char *sanitize_cookie_path(const char *cookie_path)
00224 {
00225   size_t len;
00226   char *new_path = strdup(cookie_path);
00227   if(!new_path)
00228     return NULL;
00229 
00230   /* some stupid site sends path attribute with '"'. */
00231   len = strlen(new_path);
00232   if(new_path[0] == '\"') {
00233     memmove((void *)new_path, (const void *)(new_path + 1), len);
00234     len--;
00235   }
00236   if(len && (new_path[len - 1] == '\"')) {
00237     new_path[len - 1] = 0x0;
00238     len--;
00239   }
00240 
00241   /* RFC6265 5.2.4 The Path Attribute */
00242   if(new_path[0] != '/') {
00243     /* Let cookie-path be the default-path. */
00244     free(new_path);
00245     new_path = strdup("/");
00246     return new_path;
00247   }
00248 
00249   /* convert /hoge/ to /hoge */
00250   if(len && new_path[len - 1] == '/') {
00251     new_path[len - 1] = 0x0;
00252   }
00253 
00254   return new_path;
00255 }
00256 
00257 /*
00258  * Load cookies from all given cookie files (CURLOPT_COOKIEFILE).
00259  *
00260  * NOTE: OOM or cookie parsing failures are ignored.
00261  */
00262 void Curl_cookie_loadfiles(struct Curl_easy *data)
00263 {
00264   struct curl_slist *list = data->change.cookielist;
00265   if(list) {
00266     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
00267     while(list) {
00268       struct CookieInfo *newcookies = Curl_cookie_init(data,
00269                                         list->data,
00270                                         data->cookies,
00271                                         data->set.cookiesession);
00272       if(!newcookies)
00273         /* Failure may be due to OOM or a bad cookie; both are ignored
00274          * but only the first should be
00275          */
00276         infof(data, "ignoring failed cookie_init for %s\n", list->data);
00277       else
00278         data->cookies = newcookies;
00279       list = list->next;
00280     }
00281     curl_slist_free_all(data->change.cookielist); /* clean up list */
00282     data->change.cookielist = NULL; /* don't do this again! */
00283     Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
00284   }
00285 }
00286 
00287 /*
00288  * strstore() makes a strdup() on the 'newstr' and if '*str' is non-NULL
00289  * that will be freed before the allocated string is stored there.
00290  *
00291  * It is meant to easily replace strdup()
00292  */
00293 static void strstore(char **str, const char *newstr)
00294 {
00295   free(*str);
00296   *str = strdup(newstr);
00297 }
00298 
00299 /*
00300  * remove_expired() removes expired cookies.
00301  */
00302 static void remove_expired(struct CookieInfo *cookies)
00303 {
00304   struct Cookie *co, *nx, *pv;
00305   curl_off_t now = (curl_off_t)time(NULL);
00306 
00307   co = cookies->cookies;
00308   pv = NULL;
00309   while(co) {
00310     nx = co->next;
00311     if(co->expires && co->expires < now) {
00312       if(co == cookies->cookies) {
00313         cookies->cookies = co->next;
00314       }
00315       else {
00316         pv->next = co->next;
00317       }
00318       cookies->numcookies--;
00319       freecookie(co);
00320     }
00321     else {
00322       pv = co;
00323     }
00324     co = nx;
00325   }
00326 }
00327 
00328 /*
00329  * Return true if the given string is an IP(v4|v6) address.
00330  */
00331 static bool isip(const char *domain)
00332 {
00333   struct in_addr addr;
00334 #ifdef ENABLE_IPV6
00335   struct in6_addr addr6;
00336 #endif
00337 
00338   if(Curl_inet_pton(AF_INET, domain, &addr)
00339 #ifdef ENABLE_IPV6
00340      || Curl_inet_pton(AF_INET6, domain, &addr6)
00341 #endif
00342     ) {
00343     /* domain name given as IP address */
00344     return TRUE;
00345   }
00346 
00347   return FALSE;
00348 }
00349 
00350 /****************************************************************************
00351  *
00352  * Curl_cookie_add()
00353  *
00354  * Add a single cookie line to the cookie keeping object.
00355  *
00356  * Be aware that sometimes we get an IP-only host name, and that might also be
00357  * a numerical IPv6 address.
00358  *
00359  * Returns NULL on out of memory or invalid cookie. This is suboptimal,
00360  * as they should be treated separately.
00361  ***************************************************************************/
00362 
00363 struct Cookie *
00364 Curl_cookie_add(struct Curl_easy *data,
00365                 /* The 'data' pointer here may be NULL at times, and thus
00366                    must only be used very carefully for things that can deal
00367                    with data being NULL. Such as infof() and similar */
00368 
00369                 struct CookieInfo *c,
00370                 bool httpheader, /* TRUE if HTTP header-style line */
00371                 char *lineptr,   /* first character of the line */
00372                 const char *domain, /* default domain */
00373                 const char *path)   /* full path used when this cookie is set,
00374                                        used to get default path for the cookie
00375                                        unless set */
00376 {
00377   struct Cookie *clist;
00378   char name[MAX_NAME];
00379   struct Cookie *co;
00380   struct Cookie *lastc=NULL;
00381   time_t now = time(NULL);
00382   bool replace_old = FALSE;
00383   bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */
00384 
00385 #ifdef USE_LIBPSL
00386   const psl_ctx_t *psl;
00387 #endif
00388 
00389 #ifdef CURL_DISABLE_VERBOSE_STRINGS
00390   (void)data;
00391 #endif
00392 
00393   /* First, alloc and init a new struct for it */
00394   co = calloc(1, sizeof(struct Cookie));
00395   if(!co)
00396     return NULL; /* bail out if we're this low on memory */
00397 
00398   if(httpheader) {
00399     /* This line was read off a HTTP-header */
00400     const char *ptr;
00401     const char *semiptr;
00402     char *what;
00403 
00404     what = malloc(MAX_COOKIE_LINE);
00405     if(!what) {
00406       free(co);
00407       return NULL;
00408     }
00409 
00410     semiptr=strchr(lineptr, ';'); /* first, find a semicolon */
00411 
00412     while(*lineptr && ISBLANK(*lineptr))
00413       lineptr++;
00414 
00415     ptr = lineptr;
00416     do {
00417       /* we have a <what>=<this> pair or a stand-alone word here */
00418       name[0]=what[0]=0; /* init the buffers */
00419       if(1 <= sscanf(ptr, "%" MAX_NAME_TXT "[^;\r\n=] =%"
00420                      MAX_COOKIE_LINE_TXT "[^;\r\n]",
00421                      name, what)) {
00422         /* Use strstore() below to properly deal with received cookie
00423            headers that have the same string property set more than once,
00424            and then we use the last one. */
00425         const char *whatptr;
00426         bool done = FALSE;
00427         bool sep;
00428         size_t len=strlen(what);
00429         size_t nlen = strlen(name);
00430         const char *endofn = &ptr[ nlen ];
00431 
00432         /* name ends with a '=' ? */
00433         sep = (*endofn == '=')?TRUE:FALSE;
00434 
00435         if(nlen) {
00436           endofn--; /* move to the last character */
00437           if(ISBLANK(*endofn)) {
00438             /* skip trailing spaces in name */
00439             while(*endofn && ISBLANK(*endofn) && nlen) {
00440               endofn--;
00441               nlen--;
00442             }
00443             name[nlen]=0; /* new end of name */
00444           }
00445         }
00446 
00447         /* Strip off trailing whitespace from the 'what' */
00448         while(len && ISBLANK(what[len-1])) {
00449           what[len-1]=0;
00450           len--;
00451         }
00452 
00453         /* Skip leading whitespace from the 'what' */
00454         whatptr=what;
00455         while(*whatptr && ISBLANK(*whatptr))
00456           whatptr++;
00457 
00458         if(!co->name && sep) {
00459           /* The very first name/value pair is the actual cookie name */
00460           co->name = strdup(name);
00461           co->value = strdup(whatptr);
00462           if(!co->name || !co->value) {
00463             badcookie = TRUE;
00464             break;
00465           }
00466         }
00467         else if(!len) {
00468           /* this was a "<name>=" with no content, and we must allow
00469              'secure' and 'httponly' specified this weirdly */
00470           done = TRUE;
00471           if(strcasecompare("secure", name))
00472             co->secure = TRUE;
00473           else if(strcasecompare("httponly", name))
00474             co->httponly = TRUE;
00475           else if(sep)
00476             /* there was a '=' so we're not done parsing this field */
00477             done = FALSE;
00478         }
00479         if(done)
00480           ;
00481         else if(strcasecompare("path", name)) {
00482           strstore(&co->path, whatptr);
00483           if(!co->path) {
00484             badcookie = TRUE; /* out of memory bad */
00485             break;
00486           }
00487           co->spath = sanitize_cookie_path(co->path);
00488           if(!co->spath) {
00489             badcookie = TRUE; /* out of memory bad */
00490             break;
00491           }
00492         }
00493         else if(strcasecompare("domain", name)) {
00494           bool is_ip;
00495           const char *dotp;
00496 
00497           /* Now, we make sure that our host is within the given domain,
00498              or the given domain is not valid and thus cannot be set. */
00499 
00500           if('.' == whatptr[0])
00501             whatptr++; /* ignore preceding dot */
00502 
00503           is_ip = isip(domain ? domain : whatptr);
00504 
00505           /* check for more dots */
00506           dotp = strchr(whatptr, '.');
00507           if(!dotp)
00508             domain=":";
00509 
00510           if(!domain
00511              || (is_ip && !strcmp(whatptr, domain))
00512              || (!is_ip && tailmatch(whatptr, domain))) {
00513             strstore(&co->domain, whatptr);
00514             if(!co->domain) {
00515               badcookie = TRUE;
00516               break;
00517             }
00518             if(!is_ip)
00519               co->tailmatch=TRUE; /* we always do that if the domain name was
00520                                      given */
00521           }
00522           else {
00523             /* we did not get a tailmatch and then the attempted set domain
00524                is not a domain to which the current host belongs. Mark as
00525                bad. */
00526             badcookie=TRUE;
00527             infof(data, "skipped cookie with bad tailmatch domain: %s\n",
00528                   whatptr);
00529           }
00530         }
00531         else if(strcasecompare("version", name)) {
00532           strstore(&co->version, whatptr);
00533           if(!co->version) {
00534             badcookie = TRUE;
00535             break;
00536           }
00537         }
00538         else if(strcasecompare("max-age", name)) {
00539           /* Defined in RFC2109:
00540 
00541              Optional.  The Max-Age attribute defines the lifetime of the
00542              cookie, in seconds.  The delta-seconds value is a decimal non-
00543              negative integer.  After delta-seconds seconds elapse, the
00544              client should discard the cookie.  A value of zero means the
00545              cookie should be discarded immediately.
00546 
00547           */
00548           strstore(&co->maxage, whatptr);
00549           if(!co->maxage) {
00550             badcookie = TRUE;
00551             break;
00552           }
00553         }
00554         else if(strcasecompare("expires", name)) {
00555           strstore(&co->expirestr, whatptr);
00556           if(!co->expirestr) {
00557             badcookie = TRUE;
00558             break;
00559           }
00560         }
00561         /*
00562           else this is the second (or more) name we don't know
00563           about! */
00564       }
00565       else {
00566         /* this is an "illegal" <what>=<this> pair */
00567       }
00568 
00569       if(!semiptr || !*semiptr) {
00570         /* we already know there are no more cookies */
00571         semiptr = NULL;
00572         continue;
00573       }
00574 
00575       ptr=semiptr+1;
00576       while(*ptr && ISBLANK(*ptr))
00577         ptr++;
00578       semiptr=strchr(ptr, ';'); /* now, find the next semicolon */
00579 
00580       if(!semiptr && *ptr)
00581         /* There are no more semicolons, but there's a final name=value pair
00582            coming up */
00583         semiptr=strchr(ptr, '\0');
00584     } while(semiptr);
00585 
00586     if(co->maxage) {
00587       co->expires =
00588         curlx_strtoofft((*co->maxage=='\"')?
00589                         &co->maxage[1]:&co->maxage[0], NULL, 10);
00590       if(CURL_OFF_T_MAX - now < co->expires)
00591         /* avoid overflow */
00592         co->expires = CURL_OFF_T_MAX;
00593       else
00594         co->expires += now;
00595     }
00596     else if(co->expirestr) {
00597       /* Note that if the date couldn't get parsed for whatever reason,
00598          the cookie will be treated as a session cookie */
00599       co->expires = curl_getdate(co->expirestr, NULL);
00600 
00601       /* Session cookies have expires set to 0 so if we get that back
00602          from the date parser let's add a second to make it a
00603          non-session cookie */
00604       if(co->expires == 0)
00605         co->expires = 1;
00606       else if(co->expires < 0)
00607         co->expires = 0;
00608     }
00609 
00610     if(!badcookie && !co->domain) {
00611       if(domain) {
00612         /* no domain was given in the header line, set the default */
00613         co->domain=strdup(domain);
00614         if(!co->domain)
00615           badcookie = TRUE;
00616       }
00617     }
00618 
00619     if(!badcookie && !co->path && path) {
00620       /* No path was given in the header line, set the default.
00621          Note that the passed-in path to this function MAY have a '?' and
00622          following part that MUST not be stored as part of the path. */
00623       char *queryp = strchr(path, '?');
00624 
00625       /* queryp is where the interesting part of the path ends, so now we
00626          want to the find the last */
00627       char *endslash;
00628       if(!queryp)
00629         endslash = strrchr(path, '/');
00630       else
00631         endslash = memrchr(path, '/', (size_t)(queryp - path));
00632       if(endslash) {
00633         size_t pathlen = (size_t)(endslash-path+1); /* include ending slash */
00634         co->path=malloc(pathlen+1); /* one extra for the zero byte */
00635         if(co->path) {
00636           memcpy(co->path, path, pathlen);
00637           co->path[pathlen]=0; /* zero terminate */
00638           co->spath = sanitize_cookie_path(co->path);
00639           if(!co->spath)
00640             badcookie = TRUE; /* out of memory bad */
00641         }
00642         else
00643           badcookie = TRUE;
00644       }
00645     }
00646 
00647     free(what);
00648 
00649     if(badcookie || !co->name) {
00650       /* we didn't get a cookie name or a bad one,
00651          this is an illegal line, bail out */
00652       freecookie(co);
00653       return NULL;
00654     }
00655 
00656   }
00657   else {
00658     /* This line is NOT a HTTP header style line, we do offer support for
00659        reading the odd netscape cookies-file format here */
00660     char *ptr;
00661     char *firstptr;
00662     char *tok_buf=NULL;
00663     int fields;
00664 
00665     /* IE introduced HTTP-only cookies to prevent XSS attacks. Cookies
00666        marked with httpOnly after the domain name are not accessible
00667        from javascripts, but since curl does not operate at javascript
00668        level, we include them anyway. In Firefox's cookie files, these
00669        lines are preceded with #HttpOnly_ and then everything is
00670        as usual, so we skip 10 characters of the line..
00671     */
00672     if(strncmp(lineptr, "#HttpOnly_", 10) == 0) {
00673       lineptr += 10;
00674       co->httponly = TRUE;
00675     }
00676 
00677     if(lineptr[0]=='#') {
00678       /* don't even try the comments */
00679       free(co);
00680       return NULL;
00681     }
00682     /* strip off the possible end-of-line characters */
00683     ptr=strchr(lineptr, '\r');
00684     if(ptr)
00685       *ptr=0; /* clear it */
00686     ptr=strchr(lineptr, '\n');
00687     if(ptr)
00688       *ptr=0; /* clear it */
00689 
00690     firstptr=strtok_r(lineptr, "\t", &tok_buf); /* tokenize it on the TAB */
00691 
00692     /* Now loop through the fields and init the struct we already have
00693        allocated */
00694     for(ptr=firstptr, fields=0; ptr && !badcookie;
00695         ptr=strtok_r(NULL, "\t", &tok_buf), fields++) {
00696       switch(fields) {
00697       case 0:
00698         if(ptr[0]=='.') /* skip preceding dots */
00699           ptr++;
00700         co->domain = strdup(ptr);
00701         if(!co->domain)
00702           badcookie = TRUE;
00703         break;
00704       case 1:
00705         /* This field got its explanation on the 23rd of May 2001 by
00706            Andrés García:
00707 
00708            flag: A TRUE/FALSE value indicating if all machines within a given
00709            domain can access the variable. This value is set automatically by
00710            the browser, depending on the value you set for the domain.
00711 
00712            As far as I can see, it is set to true when the cookie says
00713            .domain.com and to false when the domain is complete www.domain.com
00714         */
00715         co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
00716         break;
00717       case 2:
00718         /* It turns out, that sometimes the file format allows the path
00719            field to remain not filled in, we try to detect this and work
00720            around it! Andrés García made us aware of this... */
00721         if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) {
00722           /* only if the path doesn't look like a boolean option! */
00723           co->path = strdup(ptr);
00724           if(!co->path)
00725             badcookie = TRUE;
00726           else {
00727             co->spath = sanitize_cookie_path(co->path);
00728             if(!co->spath) {
00729               badcookie = TRUE; /* out of memory bad */
00730             }
00731           }
00732           break;
00733         }
00734         /* this doesn't look like a path, make one up! */
00735         co->path = strdup("/");
00736         if(!co->path)
00737           badcookie = TRUE;
00738         co->spath = strdup("/");
00739         if(!co->spath)
00740           badcookie = TRUE;
00741         fields++; /* add a field and fall down to secure */
00742         /* FALLTHROUGH */
00743       case 3:
00744         co->secure = strcasecompare(ptr, "TRUE")?TRUE:FALSE;
00745         break;
00746       case 4:
00747         co->expires = curlx_strtoofft(ptr, NULL, 10);
00748         break;
00749       case 5:
00750         co->name = strdup(ptr);
00751         if(!co->name)
00752           badcookie = TRUE;
00753         break;
00754       case 6:
00755         co->value = strdup(ptr);
00756         if(!co->value)
00757           badcookie = TRUE;
00758         break;
00759       }
00760     }
00761     if(6 == fields) {
00762       /* we got a cookie with blank contents, fix it */
00763       co->value = strdup("");
00764       if(!co->value)
00765         badcookie = TRUE;
00766       else
00767         fields++;
00768     }
00769 
00770     if(!badcookie && (7 != fields))
00771       /* we did not find the sufficient number of fields */
00772       badcookie = TRUE;
00773 
00774     if(badcookie) {
00775       freecookie(co);
00776       return NULL;
00777     }
00778 
00779   }
00780 
00781   if(!c->running &&    /* read from a file */
00782      c->newsession &&  /* clean session cookies */
00783      !co->expires) {   /* this is a session cookie since it doesn't expire! */
00784     freecookie(co);
00785     return NULL;
00786   }
00787 
00788   co->livecookie = c->running;
00789 
00790   /* now, we have parsed the incoming line, we must now check if this
00791      superceeds an already existing cookie, which it may if the previous have
00792      the same domain and path as this */
00793 
00794   /* at first, remove expired cookies */
00795   remove_expired(c);
00796 
00797 #ifdef USE_LIBPSL
00798   /* Check if the domain is a Public Suffix and if yes, ignore the cookie.
00799      This needs a libpsl compiled with builtin data. */
00800   if(domain && co->domain && !isip(co->domain)) {
00801     psl = psl_builtin();
00802     if(psl && !psl_is_cookie_domain_acceptable(psl, domain, co->domain)) {
00803       infof(data,
00804             "cookie '%s' dropped, domain '%s' must not set cookies for '%s'\n",
00805             co->name, domain, co->domain);
00806       freecookie(co);
00807       return NULL;
00808     }
00809   }
00810 #endif
00811 
00812   clist = c->cookies;
00813   replace_old = FALSE;
00814   while(clist) {
00815     if(strcasecompare(clist->name, co->name)) {
00816       /* the names are identical */
00817 
00818       if(clist->domain && co->domain) {
00819         if(strcasecompare(clist->domain, co->domain) &&
00820           (clist->tailmatch == co->tailmatch))
00821           /* The domains are identical */
00822           replace_old=TRUE;
00823       }
00824       else if(!clist->domain && !co->domain)
00825         replace_old = TRUE;
00826 
00827       if(replace_old) {
00828         /* the domains were identical */
00829 
00830         if(clist->spath && co->spath) {
00831           if(strcasecompare(clist->spath, co->spath)) {
00832             replace_old = TRUE;
00833           }
00834           else
00835             replace_old = FALSE;
00836         }
00837         else if(!clist->spath && !co->spath)
00838           replace_old = TRUE;
00839         else
00840           replace_old = FALSE;
00841 
00842       }
00843 
00844       if(replace_old && !co->livecookie && clist->livecookie) {
00845         /* Both cookies matched fine, except that the already present
00846            cookie is "live", which means it was set from a header, while
00847            the new one isn't "live" and thus only read from a file. We let
00848            live cookies stay alive */
00849 
00850         /* Free the newcomer and get out of here! */
00851         freecookie(co);
00852         return NULL;
00853       }
00854 
00855       if(replace_old) {
00856         co->next = clist->next; /* get the next-pointer first */
00857 
00858         /* then free all the old pointers */
00859         free(clist->name);
00860         free(clist->value);
00861         free(clist->domain);
00862         free(clist->path);
00863         free(clist->spath);
00864         free(clist->expirestr);
00865         free(clist->version);
00866         free(clist->maxage);
00867 
00868         *clist = *co;  /* then store all the new data */
00869 
00870         free(co);   /* free the newly alloced memory */
00871         co = clist; /* point to the previous struct instead */
00872 
00873         /* We have replaced a cookie, now skip the rest of the list but
00874            make sure the 'lastc' pointer is properly set */
00875         do {
00876           lastc = clist;
00877           clist = clist->next;
00878         } while(clist);
00879         break;
00880       }
00881     }
00882     lastc = clist;
00883     clist = clist->next;
00884   }
00885 
00886   if(c->running)
00887     /* Only show this when NOT reading the cookies from a file */
00888     infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, "
00889           "expire %" CURL_FORMAT_CURL_OFF_T "\n",
00890           replace_old?"Replaced":"Added", co->name, co->value,
00891           co->domain, co->path, co->expires);
00892 
00893   if(!replace_old) {
00894     /* then make the last item point on this new one */
00895     if(lastc)
00896       lastc->next = co;
00897     else
00898       c->cookies = co;
00899     c->numcookies++; /* one more cookie in the jar */
00900   }
00901 
00902   return co;
00903 }
00904 
00905 /*
00906  * get_line() makes sure to only return complete whole lines that fit in 'len'
00907  * bytes and end with a newline.
00908  */
00909 static char *get_line(char *buf, int len, FILE *input)
00910 {
00911   bool partial = FALSE;
00912   while(1) {
00913     char *b = fgets(buf, len, input);
00914     if(b) {
00915       size_t rlen = strlen(b);
00916       if(rlen && (b[rlen-1] == '\n')) {
00917         if(partial) {
00918           partial = FALSE;
00919           continue;
00920         }
00921         return b;
00922       }
00923       else
00924         /* read a partial, discard the next piece that ends with newline */
00925         partial = TRUE;
00926     }
00927     else
00928       break;
00929   }
00930   return NULL;
00931 }
00932 
00933 
00934 /*****************************************************************************
00935  *
00936  * Curl_cookie_init()
00937  *
00938  * Inits a cookie struct to read data from a local file. This is always
00939  * called before any cookies are set. File may be NULL.
00940  *
00941  * If 'newsession' is TRUE, discard all "session cookies" on read from file.
00942  *
00943  * Returns NULL on out of memory. Invalid cookies are ignored.
00944  ****************************************************************************/
00945 struct CookieInfo *Curl_cookie_init(struct Curl_easy *data,
00946                                     const char *file,
00947                                     struct CookieInfo *inc,
00948                                     bool newsession)
00949 {
00950   struct CookieInfo *c;
00951   FILE *fp = NULL;
00952   bool fromfile=TRUE;
00953   char *line = NULL;
00954 
00955   if(NULL == inc) {
00956     /* we didn't get a struct, create one */
00957     c = calloc(1, sizeof(struct CookieInfo));
00958     if(!c)
00959       return NULL; /* failed to get memory */
00960     c->filename = strdup(file?file:"none"); /* copy the name just in case */
00961     if(!c->filename)
00962       goto fail; /* failed to get memory */
00963   }
00964   else {
00965     /* we got an already existing one, use that */
00966     c = inc;
00967   }
00968   c->running = FALSE; /* this is not running, this is init */
00969 
00970   if(file && !strcmp(file, "-")) {
00971     fp = stdin;
00972     fromfile=FALSE;
00973   }
00974   else if(file && !*file) {
00975     /* points to a "" string */
00976     fp = NULL;
00977   }
00978   else
00979     fp = file?fopen(file, FOPEN_READTEXT):NULL;
00980 
00981   c->newsession = newsession; /* new session? */
00982 
00983   if(fp) {
00984     char *lineptr;
00985     bool headerline;
00986 
00987     line = malloc(MAX_COOKIE_LINE);
00988     if(!line)
00989       goto fail;
00990     while(get_line(line, MAX_COOKIE_LINE, fp)) {
00991       if(checkprefix("Set-Cookie:", line)) {
00992         /* This is a cookie line, get it! */
00993         lineptr=&line[11];
00994         headerline=TRUE;
00995       }
00996       else {
00997         lineptr=line;
00998         headerline=FALSE;
00999       }
01000       while(*lineptr && ISBLANK(*lineptr))
01001         lineptr++;
01002 
01003       Curl_cookie_add(data, c, headerline, lineptr, NULL, NULL);
01004     }
01005     free(line); /* free the line buffer */
01006 
01007     if(fromfile)
01008       fclose(fp);
01009   }
01010 
01011   c->running = TRUE;          /* now, we're running */
01012 
01013   return c;
01014 
01015 fail:
01016   free(line);
01017   if(!inc)
01018     /* Only clean up if we allocated it here, as the original could still be in
01019      * use by a share handle */
01020     Curl_cookie_cleanup(c);
01021   if(fromfile && fp)
01022     fclose(fp);
01023   return NULL; /* out of memory */
01024 }
01025 
01026 /* sort this so that the longest path gets before the shorter path */
01027 static int cookie_sort(const void *p1, const void *p2)
01028 {
01029   struct Cookie *c1 = *(struct Cookie **)p1;
01030   struct Cookie *c2 = *(struct Cookie **)p2;
01031   size_t l1, l2;
01032 
01033   /* 1 - compare cookie path lengths */
01034   l1 = c1->path ? strlen(c1->path) : 0;
01035   l2 = c2->path ? strlen(c2->path) : 0;
01036 
01037   if(l1 != l2)
01038     return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */
01039 
01040   /* 2 - compare cookie domain lengths */
01041   l1 = c1->domain ? strlen(c1->domain) : 0;
01042   l2 = c2->domain ? strlen(c2->domain) : 0;
01043 
01044   if(l1 != l2)
01045     return (l2 > l1) ? 1 : -1 ;  /* avoid size_t <=> int conversions */
01046 
01047   /* 3 - compare cookie names */
01048   if(c1->name && c2->name)
01049     return strcmp(c1->name, c2->name);
01050 
01051   /* sorry, can't be more deterministic */
01052   return 0;
01053 }
01054 
01055 #define CLONE(field)                     \
01056   do {                                   \
01057     if(src->field) {                     \
01058       dup->field = strdup(src->field);   \
01059       if(!dup->field)                    \
01060         goto fail;                       \
01061     }                                    \
01062   } while(0)
01063 
01064 static struct Cookie *dup_cookie(struct Cookie *src)
01065 {
01066   struct Cookie *dup = calloc(sizeof(struct Cookie), 1);
01067   if(dup) {
01068     CLONE(expirestr);
01069     CLONE(domain);
01070     CLONE(path);
01071     CLONE(spath);
01072     CLONE(name);
01073     CLONE(value);
01074     CLONE(maxage);
01075     CLONE(version);
01076     dup->expires = src->expires;
01077     dup->tailmatch = src->tailmatch;
01078     dup->secure = src->secure;
01079     dup->livecookie = src->livecookie;
01080     dup->httponly = src->httponly;
01081   }
01082   return dup;
01083 
01084   fail:
01085   freecookie(dup);
01086   return NULL;
01087 }
01088 
01089 /*****************************************************************************
01090  *
01091  * Curl_cookie_getlist()
01092  *
01093  * For a given host and path, return a linked list of cookies that the
01094  * client should send to the server if used now. The secure boolean informs
01095  * the cookie if a secure connection is achieved or not.
01096  *
01097  * It shall only return cookies that haven't expired.
01098  *
01099  ****************************************************************************/
01100 
01101 struct Cookie *Curl_cookie_getlist(struct CookieInfo *c,
01102                                    const char *host, const char *path,
01103                                    bool secure)
01104 {
01105   struct Cookie *newco;
01106   struct Cookie *co;
01107   time_t now = time(NULL);
01108   struct Cookie *mainco=NULL;
01109   size_t matches = 0;
01110   bool is_ip;
01111 
01112   if(!c || !c->cookies)
01113     return NULL; /* no cookie struct or no cookies in the struct */
01114 
01115   /* at first, remove expired cookies */
01116   remove_expired(c);
01117 
01118   /* check if host is an IP(v4|v6) address */
01119   is_ip = isip(host);
01120 
01121   co = c->cookies;
01122 
01123   while(co) {
01124     /* only process this cookie if it is not expired or had no expire
01125        date AND that if the cookie requires we're secure we must only
01126        continue if we are! */
01127     if((!co->expires || (co->expires > now)) &&
01128        (co->secure?secure:TRUE)) {
01129 
01130       /* now check if the domain is correct */
01131       if(!co->domain ||
01132          (co->tailmatch && !is_ip && tailmatch(co->domain, host)) ||
01133          ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) {
01134         /* the right part of the host matches the domain stuff in the
01135            cookie data */
01136 
01137         /* now check the left part of the path with the cookies path
01138            requirement */
01139         if(!co->spath || pathmatch(co->spath, path) ) {
01140 
01141           /* and now, we know this is a match and we should create an
01142              entry for the return-linked-list */
01143 
01144           newco = dup_cookie(co);
01145           if(newco) {
01146             /* then modify our next */
01147             newco->next = mainco;
01148 
01149             /* point the main to us */
01150             mainco = newco;
01151 
01152             matches++;
01153           }
01154           else {
01155             fail:
01156             /* failure, clear up the allocated chain and return NULL */
01157             Curl_cookie_freelist(mainco);
01158             return NULL;
01159           }
01160         }
01161       }
01162     }
01163     co = co->next;
01164   }
01165 
01166   if(matches) {
01167     /* Now we need to make sure that if there is a name appearing more than
01168        once, the longest specified path version comes first. To make this
01169        the swiftest way, we just sort them all based on path length. */
01170     struct Cookie **array;
01171     size_t i;
01172 
01173     /* alloc an array and store all cookie pointers */
01174     array = malloc(sizeof(struct Cookie *) * matches);
01175     if(!array)
01176       goto fail;
01177 
01178     co = mainco;
01179 
01180     for(i=0; co; co = co->next)
01181       array[i++] = co;
01182 
01183     /* now sort the cookie pointers in path length order */
01184     qsort(array, matches, sizeof(struct Cookie *), cookie_sort);
01185 
01186     /* remake the linked list order according to the new order */
01187 
01188     mainco = array[0]; /* start here */
01189     for(i=0; i<matches-1; i++)
01190       array[i]->next = array[i+1];
01191     array[matches-1]->next = NULL; /* terminate the list */
01192 
01193     free(array); /* remove the temporary data again */
01194   }
01195 
01196   return mainco; /* return the new list */
01197 }
01198 
01199 /*****************************************************************************
01200  *
01201  * Curl_cookie_clearall()
01202  *
01203  * Clear all existing cookies and reset the counter.
01204  *
01205  ****************************************************************************/
01206 void Curl_cookie_clearall(struct CookieInfo *cookies)
01207 {
01208   if(cookies) {
01209     Curl_cookie_freelist(cookies->cookies);
01210     cookies->cookies = NULL;
01211     cookies->numcookies = 0;
01212   }
01213 }
01214 
01215 /*****************************************************************************
01216  *
01217  * Curl_cookie_freelist()
01218  *
01219  * Free a list of cookies previously returned by Curl_cookie_getlist();
01220  *
01221  ****************************************************************************/
01222 
01223 void Curl_cookie_freelist(struct Cookie *co)
01224 {
01225   struct Cookie *next;
01226   while(co) {
01227     next = co->next;
01228     freecookie(co);
01229     co = next;
01230   }
01231 }
01232 
01233 
01234 /*****************************************************************************
01235  *
01236  * Curl_cookie_clearsess()
01237  *
01238  * Free all session cookies in the cookies list.
01239  *
01240  ****************************************************************************/
01241 void Curl_cookie_clearsess(struct CookieInfo *cookies)
01242 {
01243   struct Cookie *first, *curr, *next, *prev = NULL;
01244 
01245   if(!cookies || !cookies->cookies)
01246     return;
01247 
01248   first = curr = prev = cookies->cookies;
01249 
01250   for(; curr; curr = next) {
01251     next = curr->next;
01252     if(!curr->expires) {
01253       if(first == curr)
01254         first = next;
01255 
01256       if(prev == curr)
01257         prev = next;
01258       else
01259         prev->next = next;
01260 
01261       freecookie(curr);
01262       cookies->numcookies--;
01263     }
01264     else
01265       prev = curr;
01266   }
01267 
01268   cookies->cookies = first;
01269 }
01270 
01271 
01272 /*****************************************************************************
01273  *
01274  * Curl_cookie_cleanup()
01275  *
01276  * Free a "cookie object" previous created with Curl_cookie_init().
01277  *
01278  ****************************************************************************/
01279 void Curl_cookie_cleanup(struct CookieInfo *c)
01280 {
01281   if(c) {
01282     free(c->filename);
01283     Curl_cookie_freelist(c->cookies);
01284     free(c); /* free the base struct as well */
01285   }
01286 }
01287 
01288 /* get_netscape_format()
01289  *
01290  * Formats a string for Netscape output file, w/o a newline at the end.
01291  *
01292  * Function returns a char * to a formatted line. Has to be free()d
01293 */
01294 static char *get_netscape_format(const struct Cookie *co)
01295 {
01296   return aprintf(
01297     "%s"     /* httponly preamble */
01298     "%s%s\t" /* domain */
01299     "%s\t"   /* tailmatch */
01300     "%s\t"   /* path */
01301     "%s\t"   /* secure */
01302     "%" CURL_FORMAT_CURL_OFF_T "\t"   /* expires */
01303     "%s\t"   /* name */
01304     "%s",    /* value */
01305     co->httponly?"#HttpOnly_":"",
01306     /* Make sure all domains are prefixed with a dot if they allow
01307        tailmatching. This is Mozilla-style. */
01308     (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"",
01309     co->domain?co->domain:"unknown",
01310     co->tailmatch?"TRUE":"FALSE",
01311     co->path?co->path:"/",
01312     co->secure?"TRUE":"FALSE",
01313     co->expires,
01314     co->name,
01315     co->value?co->value:"");
01316 }
01317 
01318 /*
01319  * cookie_output()
01320  *
01321  * Writes all internally known cookies to the specified file. Specify
01322  * "-" as file name to write to stdout.
01323  *
01324  * The function returns non-zero on write failure.
01325  */
01326 static int cookie_output(struct CookieInfo *c, const char *dumphere)
01327 {
01328   struct Cookie *co;
01329   FILE *out;
01330   bool use_stdout=FALSE;
01331   char *format_ptr;
01332 
01333   if((NULL == c) || (0 == c->numcookies))
01334     /* If there are no known cookies, we don't write or even create any
01335        destination file */
01336     return 0;
01337 
01338   /* at first, remove expired cookies */
01339   remove_expired(c);
01340 
01341   if(!strcmp("-", dumphere)) {
01342     /* use stdout */
01343     out = stdout;
01344     use_stdout=TRUE;
01345   }
01346   else {
01347     out = fopen(dumphere, FOPEN_WRITETEXT);
01348     if(!out)
01349       return 1; /* failure */
01350   }
01351 
01352   fputs("# Netscape HTTP Cookie File\n"
01353         "# https://curl.haxx.se/docs/http-cookies.html\n"
01354         "# This file was generated by libcurl! Edit at your own risk.\n\n",
01355         out);
01356 
01357   for(co = c->cookies; co; co = co->next) {
01358     if(!co->domain)
01359       continue;
01360     format_ptr = get_netscape_format(co);
01361     if(format_ptr == NULL) {
01362       fprintf(out, "#\n# Fatal libcurl error\n");
01363       if(!use_stdout)
01364         fclose(out);
01365       return 1;
01366     }
01367     fprintf(out, "%s\n", format_ptr);
01368     free(format_ptr);
01369   }
01370 
01371   if(!use_stdout)
01372     fclose(out);
01373 
01374   return 0;
01375 }
01376 
01377 struct curl_slist *Curl_cookie_list(struct Curl_easy *data)
01378 {
01379   struct curl_slist *list = NULL;
01380   struct curl_slist *beg;
01381   struct Cookie *c;
01382   char *line;
01383 
01384   if((data->cookies == NULL) ||
01385       (data->cookies->numcookies == 0))
01386     return NULL;
01387 
01388   for(c = data->cookies->cookies; c; c = c->next) {
01389     if(!c->domain)
01390       continue;
01391     line = get_netscape_format(c);
01392     if(!line) {
01393       curl_slist_free_all(list);
01394       return NULL;
01395     }
01396     beg = Curl_slist_append_nodup(list, line);
01397     if(!beg) {
01398       free(line);
01399       curl_slist_free_all(list);
01400       return NULL;
01401     }
01402     list = beg;
01403   }
01404 
01405   return list;
01406 }
01407 
01408 void Curl_flush_cookies(struct Curl_easy *data, int cleanup)
01409 {
01410   if(data->set.str[STRING_COOKIEJAR]) {
01411     if(data->change.cookielist) {
01412       /* If there is a list of cookie files to read, do it first so that
01413          we have all the told files read before we write the new jar.
01414          Curl_cookie_loadfiles() LOCKS and UNLOCKS the share itself! */
01415       Curl_cookie_loadfiles(data);
01416     }
01417 
01418     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
01419 
01420     /* if we have a destination file for all the cookies to get dumped to */
01421     if(cookie_output(data->cookies, data->set.str[STRING_COOKIEJAR]))
01422       infof(data, "WARNING: failed to save cookies in %s\n",
01423             data->set.str[STRING_COOKIEJAR]);
01424   }
01425   else {
01426     if(cleanup && data->change.cookielist) {
01427       /* since nothing is written, we can just free the list of cookie file
01428          names */
01429       curl_slist_free_all(data->change.cookielist); /* clean up list */
01430       data->change.cookielist = NULL;
01431     }
01432     Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
01433   }
01434 
01435   if(cleanup && (!data->share || (data->cookies != data->share->cookies))) {
01436     Curl_cookie_cleanup(data->cookies);
01437   }
01438   Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
01439 }
01440 
01441 #endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */


rc_visard_driver
Author(s): Heiko Hirschmueller , Christian Emmerich , Felix Ruess
autogenerated on Thu Jun 6 2019 20:43:02