113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci/*** 2613498266Sopenharmony_ci 2713498266Sopenharmony_ci 2813498266Sopenharmony_ciRECEIVING COOKIE INFORMATION 2913498266Sopenharmony_ci============================ 3013498266Sopenharmony_ci 3113498266Sopenharmony_cistruct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 3213498266Sopenharmony_ci const char *file, struct CookieInfo *inc, bool newsession); 3313498266Sopenharmony_ci 3413498266Sopenharmony_ci Inits a cookie struct to store data in a local file. This is always 3513498266Sopenharmony_ci called before any cookies are set. 3613498266Sopenharmony_ci 3713498266Sopenharmony_cistruct Cookie *Curl_cookie_add(struct Curl_easy *data, 3813498266Sopenharmony_ci struct CookieInfo *c, bool httpheader, bool noexpire, 3913498266Sopenharmony_ci char *lineptr, const char *domain, const char *path, 4013498266Sopenharmony_ci bool secure); 4113498266Sopenharmony_ci 4213498266Sopenharmony_ci The 'lineptr' parameter is a full "Set-cookie:" line as 4313498266Sopenharmony_ci received from a server. 4413498266Sopenharmony_ci 4513498266Sopenharmony_ci The function need to replace previously stored lines that this new 4613498266Sopenharmony_ci line supersedes. 4713498266Sopenharmony_ci 4813498266Sopenharmony_ci It may remove lines that are expired. 4913498266Sopenharmony_ci 5013498266Sopenharmony_ci It should return an indication of success/error. 5113498266Sopenharmony_ci 5213498266Sopenharmony_ci 5313498266Sopenharmony_ciSENDING COOKIE INFORMATION 5413498266Sopenharmony_ci========================== 5513498266Sopenharmony_ci 5613498266Sopenharmony_cistruct Cookies *Curl_cookie_getlist(struct CookieInfo *cookie, 5713498266Sopenharmony_ci char *host, char *path, bool secure); 5813498266Sopenharmony_ci 5913498266Sopenharmony_ci For a given host and path, return a linked list of cookies that 6013498266Sopenharmony_ci the client should send to the server if used now. The secure 6113498266Sopenharmony_ci boolean informs the cookie if a secure connection is achieved or 6213498266Sopenharmony_ci not. 6313498266Sopenharmony_ci 6413498266Sopenharmony_ci It shall only return cookies that haven't expired. 6513498266Sopenharmony_ci 6613498266Sopenharmony_ci 6713498266Sopenharmony_ciExample set of cookies: 6813498266Sopenharmony_ci 6913498266Sopenharmony_ci Set-cookie: PRODUCTINFO=webxpress; domain=.fidelity.com; path=/; secure 7013498266Sopenharmony_ci Set-cookie: PERSONALIZE=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 7113498266Sopenharmony_ci domain=.fidelity.com; path=/ftgw; secure 7213498266Sopenharmony_ci Set-cookie: FidHist=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 7313498266Sopenharmony_ci domain=.fidelity.com; path=/; secure 7413498266Sopenharmony_ci Set-cookie: FidOrder=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 7513498266Sopenharmony_ci domain=.fidelity.com; path=/; secure 7613498266Sopenharmony_ci Set-cookie: DisPend=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 7713498266Sopenharmony_ci domain=.fidelity.com; path=/; secure 7813498266Sopenharmony_ci Set-cookie: FidDis=none;expires=Monday, 13-Jun-1988 03:04:55 GMT; 7913498266Sopenharmony_ci domain=.fidelity.com; path=/; secure 8013498266Sopenharmony_ci Set-cookie: 8113498266Sopenharmony_ci Session_Key@6791a9e0-901a-11d0-a1c8-9b012c88aa77=none;expires=Monday, 8213498266Sopenharmony_ci 13-Jun-1988 03:04:55 GMT; domain=.fidelity.com; path=/; secure 8313498266Sopenharmony_ci****/ 8413498266Sopenharmony_ci 8513498266Sopenharmony_ci 8613498266Sopenharmony_ci#include "curl_setup.h" 8713498266Sopenharmony_ci 8813498266Sopenharmony_ci#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_COOKIES) 8913498266Sopenharmony_ci 9013498266Sopenharmony_ci#include "urldata.h" 9113498266Sopenharmony_ci#include "cookie.h" 9213498266Sopenharmony_ci#include "psl.h" 9313498266Sopenharmony_ci#include "strtok.h" 9413498266Sopenharmony_ci#include "sendf.h" 9513498266Sopenharmony_ci#include "slist.h" 9613498266Sopenharmony_ci#include "share.h" 9713498266Sopenharmony_ci#include "strtoofft.h" 9813498266Sopenharmony_ci#include "strcase.h" 9913498266Sopenharmony_ci#include "curl_get_line.h" 10013498266Sopenharmony_ci#include "curl_memrchr.h" 10113498266Sopenharmony_ci#include "parsedate.h" 10213498266Sopenharmony_ci#include "rename.h" 10313498266Sopenharmony_ci#include "fopen.h" 10413498266Sopenharmony_ci#include "strdup.h" 10513498266Sopenharmony_ci 10613498266Sopenharmony_ci/* The last 3 #include files should be in this order */ 10713498266Sopenharmony_ci#include "curl_printf.h" 10813498266Sopenharmony_ci#include "curl_memory.h" 10913498266Sopenharmony_ci#include "memdebug.h" 11013498266Sopenharmony_ci 11113498266Sopenharmony_cistatic void strstore(char **str, const char *newstr, size_t len); 11213498266Sopenharmony_ci 11313498266Sopenharmony_cistatic void freecookie(struct Cookie *co) 11413498266Sopenharmony_ci{ 11513498266Sopenharmony_ci free(co->domain); 11613498266Sopenharmony_ci free(co->path); 11713498266Sopenharmony_ci free(co->spath); 11813498266Sopenharmony_ci free(co->name); 11913498266Sopenharmony_ci free(co->value); 12013498266Sopenharmony_ci free(co); 12113498266Sopenharmony_ci} 12213498266Sopenharmony_ci 12313498266Sopenharmony_cistatic bool cookie_tailmatch(const char *cookie_domain, 12413498266Sopenharmony_ci size_t cookie_domain_len, 12513498266Sopenharmony_ci const char *hostname) 12613498266Sopenharmony_ci{ 12713498266Sopenharmony_ci size_t hostname_len = strlen(hostname); 12813498266Sopenharmony_ci 12913498266Sopenharmony_ci if(hostname_len < cookie_domain_len) 13013498266Sopenharmony_ci return FALSE; 13113498266Sopenharmony_ci 13213498266Sopenharmony_ci if(!strncasecompare(cookie_domain, 13313498266Sopenharmony_ci hostname + hostname_len-cookie_domain_len, 13413498266Sopenharmony_ci cookie_domain_len)) 13513498266Sopenharmony_ci return FALSE; 13613498266Sopenharmony_ci 13713498266Sopenharmony_ci /* 13813498266Sopenharmony_ci * A lead char of cookie_domain is not '.'. 13913498266Sopenharmony_ci * RFC6265 4.1.2.3. The Domain Attribute says: 14013498266Sopenharmony_ci * For example, if the value of the Domain attribute is 14113498266Sopenharmony_ci * "example.com", the user agent will include the cookie in the Cookie 14213498266Sopenharmony_ci * header when making HTTP requests to example.com, www.example.com, and 14313498266Sopenharmony_ci * www.corp.example.com. 14413498266Sopenharmony_ci */ 14513498266Sopenharmony_ci if(hostname_len == cookie_domain_len) 14613498266Sopenharmony_ci return TRUE; 14713498266Sopenharmony_ci if('.' == *(hostname + hostname_len - cookie_domain_len - 1)) 14813498266Sopenharmony_ci return TRUE; 14913498266Sopenharmony_ci return FALSE; 15013498266Sopenharmony_ci} 15113498266Sopenharmony_ci 15213498266Sopenharmony_ci/* 15313498266Sopenharmony_ci * matching cookie path and url path 15413498266Sopenharmony_ci * RFC6265 5.1.4 Paths and Path-Match 15513498266Sopenharmony_ci */ 15613498266Sopenharmony_cistatic bool pathmatch(const char *cookie_path, const char *request_uri) 15713498266Sopenharmony_ci{ 15813498266Sopenharmony_ci size_t cookie_path_len; 15913498266Sopenharmony_ci size_t uri_path_len; 16013498266Sopenharmony_ci char *uri_path = NULL; 16113498266Sopenharmony_ci char *pos; 16213498266Sopenharmony_ci bool ret = FALSE; 16313498266Sopenharmony_ci 16413498266Sopenharmony_ci /* cookie_path must not have last '/' separator. ex: /sample */ 16513498266Sopenharmony_ci cookie_path_len = strlen(cookie_path); 16613498266Sopenharmony_ci if(1 == cookie_path_len) { 16713498266Sopenharmony_ci /* cookie_path must be '/' */ 16813498266Sopenharmony_ci return TRUE; 16913498266Sopenharmony_ci } 17013498266Sopenharmony_ci 17113498266Sopenharmony_ci uri_path = strdup(request_uri); 17213498266Sopenharmony_ci if(!uri_path) 17313498266Sopenharmony_ci return FALSE; 17413498266Sopenharmony_ci pos = strchr(uri_path, '?'); 17513498266Sopenharmony_ci if(pos) 17613498266Sopenharmony_ci *pos = 0x0; 17713498266Sopenharmony_ci 17813498266Sopenharmony_ci /* #-fragments are already cut off! */ 17913498266Sopenharmony_ci if(0 == strlen(uri_path) || uri_path[0] != '/') { 18013498266Sopenharmony_ci strstore(&uri_path, "/", 1); 18113498266Sopenharmony_ci if(!uri_path) 18213498266Sopenharmony_ci return FALSE; 18313498266Sopenharmony_ci } 18413498266Sopenharmony_ci 18513498266Sopenharmony_ci /* 18613498266Sopenharmony_ci * here, RFC6265 5.1.4 says 18713498266Sopenharmony_ci * 4. Output the characters of the uri-path from the first character up 18813498266Sopenharmony_ci * to, but not including, the right-most %x2F ("/"). 18913498266Sopenharmony_ci * but URL path /hoge?fuga=xxx means /hoge/index.cgi?fuga=xxx in some site 19013498266Sopenharmony_ci * without redirect. 19113498266Sopenharmony_ci * Ignore this algorithm because /hoge is uri path for this case 19213498266Sopenharmony_ci * (uri path is not /). 19313498266Sopenharmony_ci */ 19413498266Sopenharmony_ci 19513498266Sopenharmony_ci uri_path_len = strlen(uri_path); 19613498266Sopenharmony_ci 19713498266Sopenharmony_ci if(uri_path_len < cookie_path_len) { 19813498266Sopenharmony_ci ret = FALSE; 19913498266Sopenharmony_ci goto pathmatched; 20013498266Sopenharmony_ci } 20113498266Sopenharmony_ci 20213498266Sopenharmony_ci /* not using checkprefix() because matching should be case-sensitive */ 20313498266Sopenharmony_ci if(strncmp(cookie_path, uri_path, cookie_path_len)) { 20413498266Sopenharmony_ci ret = FALSE; 20513498266Sopenharmony_ci goto pathmatched; 20613498266Sopenharmony_ci } 20713498266Sopenharmony_ci 20813498266Sopenharmony_ci /* The cookie-path and the uri-path are identical. */ 20913498266Sopenharmony_ci if(cookie_path_len == uri_path_len) { 21013498266Sopenharmony_ci ret = TRUE; 21113498266Sopenharmony_ci goto pathmatched; 21213498266Sopenharmony_ci } 21313498266Sopenharmony_ci 21413498266Sopenharmony_ci /* here, cookie_path_len < uri_path_len */ 21513498266Sopenharmony_ci if(uri_path[cookie_path_len] == '/') { 21613498266Sopenharmony_ci ret = TRUE; 21713498266Sopenharmony_ci goto pathmatched; 21813498266Sopenharmony_ci } 21913498266Sopenharmony_ci 22013498266Sopenharmony_ci ret = FALSE; 22113498266Sopenharmony_ci 22213498266Sopenharmony_cipathmatched: 22313498266Sopenharmony_ci free(uri_path); 22413498266Sopenharmony_ci return ret; 22513498266Sopenharmony_ci} 22613498266Sopenharmony_ci 22713498266Sopenharmony_ci/* 22813498266Sopenharmony_ci * Return the top-level domain, for optimal hashing. 22913498266Sopenharmony_ci */ 23013498266Sopenharmony_cistatic const char *get_top_domain(const char * const domain, size_t *outlen) 23113498266Sopenharmony_ci{ 23213498266Sopenharmony_ci size_t len = 0; 23313498266Sopenharmony_ci const char *first = NULL, *last; 23413498266Sopenharmony_ci 23513498266Sopenharmony_ci if(domain) { 23613498266Sopenharmony_ci len = strlen(domain); 23713498266Sopenharmony_ci last = memrchr(domain, '.', len); 23813498266Sopenharmony_ci if(last) { 23913498266Sopenharmony_ci first = memrchr(domain, '.', (last - domain)); 24013498266Sopenharmony_ci if(first) 24113498266Sopenharmony_ci len -= (++first - domain); 24213498266Sopenharmony_ci } 24313498266Sopenharmony_ci } 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci if(outlen) 24613498266Sopenharmony_ci *outlen = len; 24713498266Sopenharmony_ci 24813498266Sopenharmony_ci return first? first: domain; 24913498266Sopenharmony_ci} 25013498266Sopenharmony_ci 25113498266Sopenharmony_ci/* Avoid C1001, an "internal error" with MSVC14 */ 25213498266Sopenharmony_ci#if defined(_MSC_VER) && (_MSC_VER == 1900) 25313498266Sopenharmony_ci#pragma optimize("", off) 25413498266Sopenharmony_ci#endif 25513498266Sopenharmony_ci 25613498266Sopenharmony_ci/* 25713498266Sopenharmony_ci * A case-insensitive hash for the cookie domains. 25813498266Sopenharmony_ci */ 25913498266Sopenharmony_cistatic size_t cookie_hash_domain(const char *domain, const size_t len) 26013498266Sopenharmony_ci{ 26113498266Sopenharmony_ci const char *end = domain + len; 26213498266Sopenharmony_ci size_t h = 5381; 26313498266Sopenharmony_ci 26413498266Sopenharmony_ci while(domain < end) { 26513498266Sopenharmony_ci h += h << 5; 26613498266Sopenharmony_ci h ^= Curl_raw_toupper(*domain++); 26713498266Sopenharmony_ci } 26813498266Sopenharmony_ci 26913498266Sopenharmony_ci return (h % COOKIE_HASH_SIZE); 27013498266Sopenharmony_ci} 27113498266Sopenharmony_ci 27213498266Sopenharmony_ci#if defined(_MSC_VER) && (_MSC_VER == 1900) 27313498266Sopenharmony_ci#pragma optimize("", on) 27413498266Sopenharmony_ci#endif 27513498266Sopenharmony_ci 27613498266Sopenharmony_ci/* 27713498266Sopenharmony_ci * Hash this domain. 27813498266Sopenharmony_ci */ 27913498266Sopenharmony_cistatic size_t cookiehash(const char * const domain) 28013498266Sopenharmony_ci{ 28113498266Sopenharmony_ci const char *top; 28213498266Sopenharmony_ci size_t len; 28313498266Sopenharmony_ci 28413498266Sopenharmony_ci if(!domain || Curl_host_is_ipnum(domain)) 28513498266Sopenharmony_ci return 0; 28613498266Sopenharmony_ci 28713498266Sopenharmony_ci top = get_top_domain(domain, &len); 28813498266Sopenharmony_ci return cookie_hash_domain(top, len); 28913498266Sopenharmony_ci} 29013498266Sopenharmony_ci 29113498266Sopenharmony_ci/* 29213498266Sopenharmony_ci * cookie path sanitize 29313498266Sopenharmony_ci */ 29413498266Sopenharmony_cistatic char *sanitize_cookie_path(const char *cookie_path) 29513498266Sopenharmony_ci{ 29613498266Sopenharmony_ci size_t len; 29713498266Sopenharmony_ci char *new_path = strdup(cookie_path); 29813498266Sopenharmony_ci if(!new_path) 29913498266Sopenharmony_ci return NULL; 30013498266Sopenharmony_ci 30113498266Sopenharmony_ci /* some stupid site sends path attribute with '"'. */ 30213498266Sopenharmony_ci len = strlen(new_path); 30313498266Sopenharmony_ci if(new_path[0] == '\"') { 30413498266Sopenharmony_ci memmove(new_path, new_path + 1, len); 30513498266Sopenharmony_ci len--; 30613498266Sopenharmony_ci } 30713498266Sopenharmony_ci if(len && (new_path[len - 1] == '\"')) { 30813498266Sopenharmony_ci new_path[--len] = 0x0; 30913498266Sopenharmony_ci } 31013498266Sopenharmony_ci 31113498266Sopenharmony_ci /* RFC6265 5.2.4 The Path Attribute */ 31213498266Sopenharmony_ci if(new_path[0] != '/') { 31313498266Sopenharmony_ci /* Let cookie-path be the default-path. */ 31413498266Sopenharmony_ci strstore(&new_path, "/", 1); 31513498266Sopenharmony_ci return new_path; 31613498266Sopenharmony_ci } 31713498266Sopenharmony_ci 31813498266Sopenharmony_ci /* convert /hoge/ to /hoge */ 31913498266Sopenharmony_ci if(len && new_path[len - 1] == '/') { 32013498266Sopenharmony_ci new_path[len - 1] = 0x0; 32113498266Sopenharmony_ci } 32213498266Sopenharmony_ci 32313498266Sopenharmony_ci return new_path; 32413498266Sopenharmony_ci} 32513498266Sopenharmony_ci 32613498266Sopenharmony_ci/* 32713498266Sopenharmony_ci * Load cookies from all given cookie files (CURLOPT_COOKIEFILE). 32813498266Sopenharmony_ci * 32913498266Sopenharmony_ci * NOTE: OOM or cookie parsing failures are ignored. 33013498266Sopenharmony_ci */ 33113498266Sopenharmony_civoid Curl_cookie_loadfiles(struct Curl_easy *data) 33213498266Sopenharmony_ci{ 33313498266Sopenharmony_ci struct curl_slist *list = data->state.cookielist; 33413498266Sopenharmony_ci if(list) { 33513498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 33613498266Sopenharmony_ci while(list) { 33713498266Sopenharmony_ci struct CookieInfo *newcookies = 33813498266Sopenharmony_ci Curl_cookie_init(data, list->data, data->cookies, 33913498266Sopenharmony_ci data->set.cookiesession); 34013498266Sopenharmony_ci if(!newcookies) 34113498266Sopenharmony_ci /* 34213498266Sopenharmony_ci * Failure may be due to OOM or a bad cookie; both are ignored 34313498266Sopenharmony_ci * but only the first should be 34413498266Sopenharmony_ci */ 34513498266Sopenharmony_ci infof(data, "ignoring failed cookie_init for %s", list->data); 34613498266Sopenharmony_ci else 34713498266Sopenharmony_ci data->cookies = newcookies; 34813498266Sopenharmony_ci list = list->next; 34913498266Sopenharmony_ci } 35013498266Sopenharmony_ci Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 35113498266Sopenharmony_ci } 35213498266Sopenharmony_ci} 35313498266Sopenharmony_ci 35413498266Sopenharmony_ci/* 35513498266Sopenharmony_ci * strstore 35613498266Sopenharmony_ci * 35713498266Sopenharmony_ci * A thin wrapper around strdup which ensures that any memory allocated at 35813498266Sopenharmony_ci * *str will be freed before the string allocated by strdup is stored there. 35913498266Sopenharmony_ci * The intended usecase is repeated assignments to the same variable during 36013498266Sopenharmony_ci * parsing in a last-wins scenario. The caller is responsible for checking 36113498266Sopenharmony_ci * for OOM errors. 36213498266Sopenharmony_ci */ 36313498266Sopenharmony_cistatic void strstore(char **str, const char *newstr, size_t len) 36413498266Sopenharmony_ci{ 36513498266Sopenharmony_ci DEBUGASSERT(newstr); 36613498266Sopenharmony_ci DEBUGASSERT(str); 36713498266Sopenharmony_ci free(*str); 36813498266Sopenharmony_ci *str = Curl_memdup0(newstr, len); 36913498266Sopenharmony_ci} 37013498266Sopenharmony_ci 37113498266Sopenharmony_ci/* 37213498266Sopenharmony_ci * remove_expired 37313498266Sopenharmony_ci * 37413498266Sopenharmony_ci * Remove expired cookies from the hash by inspecting the expires timestamp on 37513498266Sopenharmony_ci * each cookie in the hash, freeing and deleting any where the timestamp is in 37613498266Sopenharmony_ci * the past. If the cookiejar has recorded the next timestamp at which one or 37713498266Sopenharmony_ci * more cookies expire, then processing will exit early in case this timestamp 37813498266Sopenharmony_ci * is in the future. 37913498266Sopenharmony_ci */ 38013498266Sopenharmony_cistatic void remove_expired(struct CookieInfo *cookies) 38113498266Sopenharmony_ci{ 38213498266Sopenharmony_ci struct Cookie *co, *nx; 38313498266Sopenharmony_ci curl_off_t now = (curl_off_t)time(NULL); 38413498266Sopenharmony_ci unsigned int i; 38513498266Sopenharmony_ci 38613498266Sopenharmony_ci /* 38713498266Sopenharmony_ci * If the earliest expiration timestamp in the jar is in the future we can 38813498266Sopenharmony_ci * skip scanning the whole jar and instead exit early as there won't be any 38913498266Sopenharmony_ci * cookies to evict. If we need to evict however, reset the next_expiration 39013498266Sopenharmony_ci * counter in order to track the next one. In case the recorded first 39113498266Sopenharmony_ci * expiration is the max offset, then perform the safe fallback of checking 39213498266Sopenharmony_ci * all cookies. 39313498266Sopenharmony_ci */ 39413498266Sopenharmony_ci if(now < cookies->next_expiration && 39513498266Sopenharmony_ci cookies->next_expiration != CURL_OFF_T_MAX) 39613498266Sopenharmony_ci return; 39713498266Sopenharmony_ci else 39813498266Sopenharmony_ci cookies->next_expiration = CURL_OFF_T_MAX; 39913498266Sopenharmony_ci 40013498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) { 40113498266Sopenharmony_ci struct Cookie *pv = NULL; 40213498266Sopenharmony_ci co = cookies->cookies[i]; 40313498266Sopenharmony_ci while(co) { 40413498266Sopenharmony_ci nx = co->next; 40513498266Sopenharmony_ci if(co->expires && co->expires < now) { 40613498266Sopenharmony_ci if(!pv) { 40713498266Sopenharmony_ci cookies->cookies[i] = co->next; 40813498266Sopenharmony_ci } 40913498266Sopenharmony_ci else { 41013498266Sopenharmony_ci pv->next = co->next; 41113498266Sopenharmony_ci } 41213498266Sopenharmony_ci cookies->numcookies--; 41313498266Sopenharmony_ci freecookie(co); 41413498266Sopenharmony_ci } 41513498266Sopenharmony_ci else { 41613498266Sopenharmony_ci /* 41713498266Sopenharmony_ci * If this cookie has an expiration timestamp earlier than what we've 41813498266Sopenharmony_ci * seen so far then record it for the next round of expirations. 41913498266Sopenharmony_ci */ 42013498266Sopenharmony_ci if(co->expires && co->expires < cookies->next_expiration) 42113498266Sopenharmony_ci cookies->next_expiration = co->expires; 42213498266Sopenharmony_ci pv = co; 42313498266Sopenharmony_ci } 42413498266Sopenharmony_ci co = nx; 42513498266Sopenharmony_ci } 42613498266Sopenharmony_ci } 42713498266Sopenharmony_ci} 42813498266Sopenharmony_ci 42913498266Sopenharmony_ci/* Make sure domain contains a dot or is localhost. */ 43013498266Sopenharmony_cistatic bool bad_domain(const char *domain, size_t len) 43113498266Sopenharmony_ci{ 43213498266Sopenharmony_ci if((len == 9) && strncasecompare(domain, "localhost", 9)) 43313498266Sopenharmony_ci return FALSE; 43413498266Sopenharmony_ci else { 43513498266Sopenharmony_ci /* there must be a dot present, but that dot must not be a trailing dot */ 43613498266Sopenharmony_ci char *dot = memchr(domain, '.', len); 43713498266Sopenharmony_ci if(dot) { 43813498266Sopenharmony_ci size_t i = dot - domain; 43913498266Sopenharmony_ci if((len - i) > 1) 44013498266Sopenharmony_ci /* the dot is not the last byte */ 44113498266Sopenharmony_ci return FALSE; 44213498266Sopenharmony_ci } 44313498266Sopenharmony_ci } 44413498266Sopenharmony_ci return TRUE; 44513498266Sopenharmony_ci} 44613498266Sopenharmony_ci 44713498266Sopenharmony_ci/* 44813498266Sopenharmony_ci RFC 6265 section 4.1.1 says a server should accept this range: 44913498266Sopenharmony_ci 45013498266Sopenharmony_ci cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E 45113498266Sopenharmony_ci 45213498266Sopenharmony_ci But Firefox and Chrome as of June 2022 accept space, comma and double-quotes 45313498266Sopenharmony_ci fine. The prime reason for filtering out control bytes is that some HTTP 45413498266Sopenharmony_ci servers return 400 for requests that contain such. 45513498266Sopenharmony_ci*/ 45613498266Sopenharmony_cistatic int invalid_octets(const char *p) 45713498266Sopenharmony_ci{ 45813498266Sopenharmony_ci /* Reject all bytes \x01 - \x1f (*except* \x09, TAB) + \x7f */ 45913498266Sopenharmony_ci static const char badoctets[] = { 46013498266Sopenharmony_ci "\x01\x02\x03\x04\x05\x06\x07\x08\x0a" 46113498266Sopenharmony_ci "\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14" 46213498266Sopenharmony_ci "\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x7f" 46313498266Sopenharmony_ci }; 46413498266Sopenharmony_ci size_t len; 46513498266Sopenharmony_ci /* scan for all the octets that are *not* in cookie-octet */ 46613498266Sopenharmony_ci len = strcspn(p, badoctets); 46713498266Sopenharmony_ci return (p[len] != '\0'); 46813498266Sopenharmony_ci} 46913498266Sopenharmony_ci 47013498266Sopenharmony_ci/* 47113498266Sopenharmony_ci * Curl_cookie_add 47213498266Sopenharmony_ci * 47313498266Sopenharmony_ci * Add a single cookie line to the cookie keeping object. Be aware that 47413498266Sopenharmony_ci * sometimes we get an IP-only host name, and that might also be a numerical 47513498266Sopenharmony_ci * IPv6 address. 47613498266Sopenharmony_ci * 47713498266Sopenharmony_ci * Returns NULL on out of memory or invalid cookie. This is suboptimal, 47813498266Sopenharmony_ci * as they should be treated separately. 47913498266Sopenharmony_ci */ 48013498266Sopenharmony_cistruct Cookie * 48113498266Sopenharmony_ciCurl_cookie_add(struct Curl_easy *data, 48213498266Sopenharmony_ci struct CookieInfo *c, 48313498266Sopenharmony_ci bool httpheader, /* TRUE if HTTP header-style line */ 48413498266Sopenharmony_ci bool noexpire, /* if TRUE, skip remove_expired() */ 48513498266Sopenharmony_ci const char *lineptr, /* first character of the line */ 48613498266Sopenharmony_ci const char *domain, /* default domain */ 48713498266Sopenharmony_ci const char *path, /* full path used when this cookie is set, 48813498266Sopenharmony_ci used to get default path for the cookie 48913498266Sopenharmony_ci unless set */ 49013498266Sopenharmony_ci bool secure) /* TRUE if connection is over secure origin */ 49113498266Sopenharmony_ci{ 49213498266Sopenharmony_ci struct Cookie *clist; 49313498266Sopenharmony_ci struct Cookie *co; 49413498266Sopenharmony_ci struct Cookie *lastc = NULL; 49513498266Sopenharmony_ci struct Cookie *replace_co = NULL; 49613498266Sopenharmony_ci struct Cookie *replace_clist = NULL; 49713498266Sopenharmony_ci time_t now = time(NULL); 49813498266Sopenharmony_ci bool replace_old = FALSE; 49913498266Sopenharmony_ci bool badcookie = FALSE; /* cookies are good by default. mmmmm yummy */ 50013498266Sopenharmony_ci size_t myhash; 50113498266Sopenharmony_ci 50213498266Sopenharmony_ci DEBUGASSERT(data); 50313498266Sopenharmony_ci DEBUGASSERT(MAX_SET_COOKIE_AMOUNT <= 255); /* counter is an unsigned char */ 50413498266Sopenharmony_ci if(data->req.setcookies >= MAX_SET_COOKIE_AMOUNT) 50513498266Sopenharmony_ci return NULL; 50613498266Sopenharmony_ci 50713498266Sopenharmony_ci /* First, alloc and init a new struct for it */ 50813498266Sopenharmony_ci co = calloc(1, sizeof(struct Cookie)); 50913498266Sopenharmony_ci if(!co) 51013498266Sopenharmony_ci return NULL; /* bail out if we're this low on memory */ 51113498266Sopenharmony_ci 51213498266Sopenharmony_ci if(httpheader) { 51313498266Sopenharmony_ci /* This line was read off an HTTP-header */ 51413498266Sopenharmony_ci const char *ptr; 51513498266Sopenharmony_ci 51613498266Sopenharmony_ci size_t linelength = strlen(lineptr); 51713498266Sopenharmony_ci if(linelength > MAX_COOKIE_LINE) { 51813498266Sopenharmony_ci /* discard overly long lines at once */ 51913498266Sopenharmony_ci free(co); 52013498266Sopenharmony_ci return NULL; 52113498266Sopenharmony_ci } 52213498266Sopenharmony_ci 52313498266Sopenharmony_ci ptr = lineptr; 52413498266Sopenharmony_ci do { 52513498266Sopenharmony_ci size_t vlen; 52613498266Sopenharmony_ci size_t nlen; 52713498266Sopenharmony_ci 52813498266Sopenharmony_ci while(*ptr && ISBLANK(*ptr)) 52913498266Sopenharmony_ci ptr++; 53013498266Sopenharmony_ci 53113498266Sopenharmony_ci /* we have a <name>=<value> pair or a stand-alone word here */ 53213498266Sopenharmony_ci nlen = strcspn(ptr, ";\t\r\n="); 53313498266Sopenharmony_ci if(nlen) { 53413498266Sopenharmony_ci bool done = FALSE; 53513498266Sopenharmony_ci bool sep = FALSE; 53613498266Sopenharmony_ci const char *namep = ptr; 53713498266Sopenharmony_ci const char *valuep; 53813498266Sopenharmony_ci 53913498266Sopenharmony_ci ptr += nlen; 54013498266Sopenharmony_ci 54113498266Sopenharmony_ci /* trim trailing spaces and tabs after name */ 54213498266Sopenharmony_ci while(nlen && ISBLANK(namep[nlen - 1])) 54313498266Sopenharmony_ci nlen--; 54413498266Sopenharmony_ci 54513498266Sopenharmony_ci if(*ptr == '=') { 54613498266Sopenharmony_ci vlen = strcspn(++ptr, ";\r\n"); 54713498266Sopenharmony_ci valuep = ptr; 54813498266Sopenharmony_ci sep = TRUE; 54913498266Sopenharmony_ci ptr = &valuep[vlen]; 55013498266Sopenharmony_ci 55113498266Sopenharmony_ci /* Strip off trailing whitespace from the value */ 55213498266Sopenharmony_ci while(vlen && ISBLANK(valuep[vlen-1])) 55313498266Sopenharmony_ci vlen--; 55413498266Sopenharmony_ci 55513498266Sopenharmony_ci /* Skip leading whitespace from the value */ 55613498266Sopenharmony_ci while(vlen && ISBLANK(*valuep)) { 55713498266Sopenharmony_ci valuep++; 55813498266Sopenharmony_ci vlen--; 55913498266Sopenharmony_ci } 56013498266Sopenharmony_ci 56113498266Sopenharmony_ci /* Reject cookies with a TAB inside the value */ 56213498266Sopenharmony_ci if(memchr(valuep, '\t', vlen)) { 56313498266Sopenharmony_ci freecookie(co); 56413498266Sopenharmony_ci infof(data, "cookie contains TAB, dropping"); 56513498266Sopenharmony_ci return NULL; 56613498266Sopenharmony_ci } 56713498266Sopenharmony_ci } 56813498266Sopenharmony_ci else { 56913498266Sopenharmony_ci valuep = NULL; 57013498266Sopenharmony_ci vlen = 0; 57113498266Sopenharmony_ci } 57213498266Sopenharmony_ci 57313498266Sopenharmony_ci /* 57413498266Sopenharmony_ci * Check for too long individual name or contents, or too long 57513498266Sopenharmony_ci * combination of name + contents. Chrome and Firefox support 4095 or 57613498266Sopenharmony_ci * 4096 bytes combo 57713498266Sopenharmony_ci */ 57813498266Sopenharmony_ci if(nlen >= (MAX_NAME-1) || vlen >= (MAX_NAME-1) || 57913498266Sopenharmony_ci ((nlen + vlen) > MAX_NAME)) { 58013498266Sopenharmony_ci freecookie(co); 58113498266Sopenharmony_ci infof(data, "oversized cookie dropped, name/val %zu + %zu bytes", 58213498266Sopenharmony_ci nlen, vlen); 58313498266Sopenharmony_ci return NULL; 58413498266Sopenharmony_ci } 58513498266Sopenharmony_ci 58613498266Sopenharmony_ci /* 58713498266Sopenharmony_ci * Check if we have a reserved prefix set before anything else, as we 58813498266Sopenharmony_ci * otherwise have to test for the prefix in both the cookie name and 58913498266Sopenharmony_ci * "the rest". Prefixes must start with '__' and end with a '-', so 59013498266Sopenharmony_ci * only test for names where that can possibly be true. 59113498266Sopenharmony_ci */ 59213498266Sopenharmony_ci if(nlen >= 7 && namep[0] == '_' && namep[1] == '_') { 59313498266Sopenharmony_ci if(strncasecompare("__Secure-", namep, 9)) 59413498266Sopenharmony_ci co->prefix |= COOKIE_PREFIX__SECURE; 59513498266Sopenharmony_ci else if(strncasecompare("__Host-", namep, 7)) 59613498266Sopenharmony_ci co->prefix |= COOKIE_PREFIX__HOST; 59713498266Sopenharmony_ci } 59813498266Sopenharmony_ci 59913498266Sopenharmony_ci /* 60013498266Sopenharmony_ci * Use strstore() below to properly deal with received cookie 60113498266Sopenharmony_ci * headers that have the same string property set more than once, 60213498266Sopenharmony_ci * and then we use the last one. 60313498266Sopenharmony_ci */ 60413498266Sopenharmony_ci 60513498266Sopenharmony_ci if(!co->name) { 60613498266Sopenharmony_ci /* The very first name/value pair is the actual cookie name */ 60713498266Sopenharmony_ci if(!sep) { 60813498266Sopenharmony_ci /* Bad name/value pair. */ 60913498266Sopenharmony_ci badcookie = TRUE; 61013498266Sopenharmony_ci break; 61113498266Sopenharmony_ci } 61213498266Sopenharmony_ci strstore(&co->name, namep, nlen); 61313498266Sopenharmony_ci strstore(&co->value, valuep, vlen); 61413498266Sopenharmony_ci done = TRUE; 61513498266Sopenharmony_ci if(!co->name || !co->value) { 61613498266Sopenharmony_ci badcookie = TRUE; 61713498266Sopenharmony_ci break; 61813498266Sopenharmony_ci } 61913498266Sopenharmony_ci if(invalid_octets(co->value) || invalid_octets(co->name)) { 62013498266Sopenharmony_ci infof(data, "invalid octets in name/value, cookie dropped"); 62113498266Sopenharmony_ci badcookie = TRUE; 62213498266Sopenharmony_ci break; 62313498266Sopenharmony_ci } 62413498266Sopenharmony_ci } 62513498266Sopenharmony_ci else if(!vlen) { 62613498266Sopenharmony_ci /* 62713498266Sopenharmony_ci * this was a "<name>=" with no content, and we must allow 62813498266Sopenharmony_ci * 'secure' and 'httponly' specified this weirdly 62913498266Sopenharmony_ci */ 63013498266Sopenharmony_ci done = TRUE; 63113498266Sopenharmony_ci /* 63213498266Sopenharmony_ci * secure cookies are only allowed to be set when the connection is 63313498266Sopenharmony_ci * using a secure protocol, or when the cookie is being set by 63413498266Sopenharmony_ci * reading from file 63513498266Sopenharmony_ci */ 63613498266Sopenharmony_ci if((nlen == 6) && strncasecompare("secure", namep, 6)) { 63713498266Sopenharmony_ci if(secure || !c->running) { 63813498266Sopenharmony_ci co->secure = TRUE; 63913498266Sopenharmony_ci } 64013498266Sopenharmony_ci else { 64113498266Sopenharmony_ci badcookie = TRUE; 64213498266Sopenharmony_ci break; 64313498266Sopenharmony_ci } 64413498266Sopenharmony_ci } 64513498266Sopenharmony_ci else if((nlen == 8) && strncasecompare("httponly", namep, 8)) 64613498266Sopenharmony_ci co->httponly = TRUE; 64713498266Sopenharmony_ci else if(sep) 64813498266Sopenharmony_ci /* there was a '=' so we're not done parsing this field */ 64913498266Sopenharmony_ci done = FALSE; 65013498266Sopenharmony_ci } 65113498266Sopenharmony_ci if(done) 65213498266Sopenharmony_ci ; 65313498266Sopenharmony_ci else if((nlen == 4) && strncasecompare("path", namep, 4)) { 65413498266Sopenharmony_ci strstore(&co->path, valuep, vlen); 65513498266Sopenharmony_ci if(!co->path) { 65613498266Sopenharmony_ci badcookie = TRUE; /* out of memory bad */ 65713498266Sopenharmony_ci break; 65813498266Sopenharmony_ci } 65913498266Sopenharmony_ci free(co->spath); /* if this is set again */ 66013498266Sopenharmony_ci co->spath = sanitize_cookie_path(co->path); 66113498266Sopenharmony_ci if(!co->spath) { 66213498266Sopenharmony_ci badcookie = TRUE; /* out of memory bad */ 66313498266Sopenharmony_ci break; 66413498266Sopenharmony_ci } 66513498266Sopenharmony_ci } 66613498266Sopenharmony_ci else if((nlen == 6) && 66713498266Sopenharmony_ci strncasecompare("domain", namep, 6) && vlen) { 66813498266Sopenharmony_ci bool is_ip; 66913498266Sopenharmony_ci 67013498266Sopenharmony_ci /* 67113498266Sopenharmony_ci * Now, we make sure that our host is within the given domain, or 67213498266Sopenharmony_ci * the given domain is not valid and thus cannot be set. 67313498266Sopenharmony_ci */ 67413498266Sopenharmony_ci 67513498266Sopenharmony_ci if('.' == valuep[0]) { 67613498266Sopenharmony_ci valuep++; /* ignore preceding dot */ 67713498266Sopenharmony_ci vlen--; 67813498266Sopenharmony_ci } 67913498266Sopenharmony_ci 68013498266Sopenharmony_ci#ifndef USE_LIBPSL 68113498266Sopenharmony_ci /* 68213498266Sopenharmony_ci * Without PSL we don't know when the incoming cookie is set on a 68313498266Sopenharmony_ci * TLD or otherwise "protected" suffix. To reduce risk, we require a 68413498266Sopenharmony_ci * dot OR the exact host name being "localhost". 68513498266Sopenharmony_ci */ 68613498266Sopenharmony_ci if(bad_domain(valuep, vlen)) 68713498266Sopenharmony_ci domain = ":"; 68813498266Sopenharmony_ci#endif 68913498266Sopenharmony_ci 69013498266Sopenharmony_ci is_ip = Curl_host_is_ipnum(domain ? domain : valuep); 69113498266Sopenharmony_ci 69213498266Sopenharmony_ci if(!domain 69313498266Sopenharmony_ci || (is_ip && !strncmp(valuep, domain, vlen) && 69413498266Sopenharmony_ci (vlen == strlen(domain))) 69513498266Sopenharmony_ci || (!is_ip && cookie_tailmatch(valuep, vlen, domain))) { 69613498266Sopenharmony_ci strstore(&co->domain, valuep, vlen); 69713498266Sopenharmony_ci if(!co->domain) { 69813498266Sopenharmony_ci badcookie = TRUE; 69913498266Sopenharmony_ci break; 70013498266Sopenharmony_ci } 70113498266Sopenharmony_ci if(!is_ip) 70213498266Sopenharmony_ci co->tailmatch = TRUE; /* we always do that if the domain name was 70313498266Sopenharmony_ci given */ 70413498266Sopenharmony_ci } 70513498266Sopenharmony_ci else { 70613498266Sopenharmony_ci /* 70713498266Sopenharmony_ci * We did not get a tailmatch and then the attempted set domain is 70813498266Sopenharmony_ci * not a domain to which the current host belongs. Mark as bad. 70913498266Sopenharmony_ci */ 71013498266Sopenharmony_ci badcookie = TRUE; 71113498266Sopenharmony_ci infof(data, "skipped cookie with bad tailmatch domain: %s", 71213498266Sopenharmony_ci valuep); 71313498266Sopenharmony_ci } 71413498266Sopenharmony_ci } 71513498266Sopenharmony_ci else if((nlen == 7) && strncasecompare("version", namep, 7)) { 71613498266Sopenharmony_ci /* just ignore */ 71713498266Sopenharmony_ci } 71813498266Sopenharmony_ci else if((nlen == 7) && strncasecompare("max-age", namep, 7)) { 71913498266Sopenharmony_ci /* 72013498266Sopenharmony_ci * Defined in RFC2109: 72113498266Sopenharmony_ci * 72213498266Sopenharmony_ci * Optional. The Max-Age attribute defines the lifetime of the 72313498266Sopenharmony_ci * cookie, in seconds. The delta-seconds value is a decimal non- 72413498266Sopenharmony_ci * negative integer. After delta-seconds seconds elapse, the 72513498266Sopenharmony_ci * client should discard the cookie. A value of zero means the 72613498266Sopenharmony_ci * cookie should be discarded immediately. 72713498266Sopenharmony_ci */ 72813498266Sopenharmony_ci CURLofft offt; 72913498266Sopenharmony_ci const char *maxage = valuep; 73013498266Sopenharmony_ci offt = curlx_strtoofft((*maxage == '\"')? 73113498266Sopenharmony_ci &maxage[1]:&maxage[0], NULL, 10, 73213498266Sopenharmony_ci &co->expires); 73313498266Sopenharmony_ci switch(offt) { 73413498266Sopenharmony_ci case CURL_OFFT_FLOW: 73513498266Sopenharmony_ci /* overflow, used max value */ 73613498266Sopenharmony_ci co->expires = CURL_OFF_T_MAX; 73713498266Sopenharmony_ci break; 73813498266Sopenharmony_ci case CURL_OFFT_INVAL: 73913498266Sopenharmony_ci /* negative or otherwise bad, expire */ 74013498266Sopenharmony_ci co->expires = 1; 74113498266Sopenharmony_ci break; 74213498266Sopenharmony_ci case CURL_OFFT_OK: 74313498266Sopenharmony_ci if(!co->expires) 74413498266Sopenharmony_ci /* already expired */ 74513498266Sopenharmony_ci co->expires = 1; 74613498266Sopenharmony_ci else if(CURL_OFF_T_MAX - now < co->expires) 74713498266Sopenharmony_ci /* would overflow */ 74813498266Sopenharmony_ci co->expires = CURL_OFF_T_MAX; 74913498266Sopenharmony_ci else 75013498266Sopenharmony_ci co->expires += now; 75113498266Sopenharmony_ci break; 75213498266Sopenharmony_ci } 75313498266Sopenharmony_ci } 75413498266Sopenharmony_ci else if((nlen == 7) && strncasecompare("expires", namep, 7)) { 75513498266Sopenharmony_ci char date[128]; 75613498266Sopenharmony_ci if(!co->expires && (vlen < sizeof(date))) { 75713498266Sopenharmony_ci /* copy the date so that it can be null terminated */ 75813498266Sopenharmony_ci memcpy(date, valuep, vlen); 75913498266Sopenharmony_ci date[vlen] = 0; 76013498266Sopenharmony_ci /* 76113498266Sopenharmony_ci * Let max-age have priority. 76213498266Sopenharmony_ci * 76313498266Sopenharmony_ci * If the date cannot get parsed for whatever reason, the cookie 76413498266Sopenharmony_ci * will be treated as a session cookie 76513498266Sopenharmony_ci */ 76613498266Sopenharmony_ci co->expires = Curl_getdate_capped(date); 76713498266Sopenharmony_ci 76813498266Sopenharmony_ci /* 76913498266Sopenharmony_ci * Session cookies have expires set to 0 so if we get that back 77013498266Sopenharmony_ci * from the date parser let's add a second to make it a 77113498266Sopenharmony_ci * non-session cookie 77213498266Sopenharmony_ci */ 77313498266Sopenharmony_ci if(co->expires == 0) 77413498266Sopenharmony_ci co->expires = 1; 77513498266Sopenharmony_ci else if(co->expires < 0) 77613498266Sopenharmony_ci co->expires = 0; 77713498266Sopenharmony_ci } 77813498266Sopenharmony_ci } 77913498266Sopenharmony_ci 78013498266Sopenharmony_ci /* 78113498266Sopenharmony_ci * Else, this is the second (or more) name we don't know about! 78213498266Sopenharmony_ci */ 78313498266Sopenharmony_ci } 78413498266Sopenharmony_ci else { 78513498266Sopenharmony_ci /* this is an "illegal" <what>=<this> pair */ 78613498266Sopenharmony_ci } 78713498266Sopenharmony_ci 78813498266Sopenharmony_ci while(*ptr && ISBLANK(*ptr)) 78913498266Sopenharmony_ci ptr++; 79013498266Sopenharmony_ci if(*ptr == ';') 79113498266Sopenharmony_ci ptr++; 79213498266Sopenharmony_ci else 79313498266Sopenharmony_ci break; 79413498266Sopenharmony_ci } while(1); 79513498266Sopenharmony_ci 79613498266Sopenharmony_ci if(!badcookie && !co->domain) { 79713498266Sopenharmony_ci if(domain) { 79813498266Sopenharmony_ci /* no domain was given in the header line, set the default */ 79913498266Sopenharmony_ci co->domain = strdup(domain); 80013498266Sopenharmony_ci if(!co->domain) 80113498266Sopenharmony_ci badcookie = TRUE; 80213498266Sopenharmony_ci } 80313498266Sopenharmony_ci } 80413498266Sopenharmony_ci 80513498266Sopenharmony_ci if(!badcookie && !co->path && path) { 80613498266Sopenharmony_ci /* 80713498266Sopenharmony_ci * No path was given in the header line, set the default. Note that the 80813498266Sopenharmony_ci * passed-in path to this function MAY have a '?' and following part that 80913498266Sopenharmony_ci * MUST NOT be stored as part of the path. 81013498266Sopenharmony_ci */ 81113498266Sopenharmony_ci char *queryp = strchr(path, '?'); 81213498266Sopenharmony_ci 81313498266Sopenharmony_ci /* 81413498266Sopenharmony_ci * queryp is where the interesting part of the path ends, so now we 81513498266Sopenharmony_ci * want to the find the last 81613498266Sopenharmony_ci */ 81713498266Sopenharmony_ci char *endslash; 81813498266Sopenharmony_ci if(!queryp) 81913498266Sopenharmony_ci endslash = strrchr(path, '/'); 82013498266Sopenharmony_ci else 82113498266Sopenharmony_ci endslash = memrchr(path, '/', (queryp - path)); 82213498266Sopenharmony_ci if(endslash) { 82313498266Sopenharmony_ci size_t pathlen = (endslash-path + 1); /* include end slash */ 82413498266Sopenharmony_ci co->path = Curl_memdup0(path, pathlen); 82513498266Sopenharmony_ci if(co->path) { 82613498266Sopenharmony_ci co->spath = sanitize_cookie_path(co->path); 82713498266Sopenharmony_ci if(!co->spath) 82813498266Sopenharmony_ci badcookie = TRUE; /* out of memory bad */ 82913498266Sopenharmony_ci } 83013498266Sopenharmony_ci else 83113498266Sopenharmony_ci badcookie = TRUE; 83213498266Sopenharmony_ci } 83313498266Sopenharmony_ci } 83413498266Sopenharmony_ci 83513498266Sopenharmony_ci /* 83613498266Sopenharmony_ci * If we didn't get a cookie name, or a bad one, the this is an illegal 83713498266Sopenharmony_ci * line so bail out. 83813498266Sopenharmony_ci */ 83913498266Sopenharmony_ci if(badcookie || !co->name) { 84013498266Sopenharmony_ci freecookie(co); 84113498266Sopenharmony_ci return NULL; 84213498266Sopenharmony_ci } 84313498266Sopenharmony_ci data->req.setcookies++; 84413498266Sopenharmony_ci } 84513498266Sopenharmony_ci else { 84613498266Sopenharmony_ci /* 84713498266Sopenharmony_ci * This line is NOT an HTTP header style line, we do offer support for 84813498266Sopenharmony_ci * reading the odd netscape cookies-file format here 84913498266Sopenharmony_ci */ 85013498266Sopenharmony_ci char *ptr; 85113498266Sopenharmony_ci char *firstptr; 85213498266Sopenharmony_ci char *tok_buf = NULL; 85313498266Sopenharmony_ci int fields; 85413498266Sopenharmony_ci 85513498266Sopenharmony_ci /* 85613498266Sopenharmony_ci * IE introduced HTTP-only cookies to prevent XSS attacks. Cookies marked 85713498266Sopenharmony_ci * with httpOnly after the domain name are not accessible from javascripts, 85813498266Sopenharmony_ci * but since curl does not operate at javascript level, we include them 85913498266Sopenharmony_ci * anyway. In Firefox's cookie files, these lines are preceded with 86013498266Sopenharmony_ci * #HttpOnly_ and then everything is as usual, so we skip 10 characters of 86113498266Sopenharmony_ci * the line.. 86213498266Sopenharmony_ci */ 86313498266Sopenharmony_ci if(strncmp(lineptr, "#HttpOnly_", 10) == 0) { 86413498266Sopenharmony_ci lineptr += 10; 86513498266Sopenharmony_ci co->httponly = TRUE; 86613498266Sopenharmony_ci } 86713498266Sopenharmony_ci 86813498266Sopenharmony_ci if(lineptr[0]=='#') { 86913498266Sopenharmony_ci /* don't even try the comments */ 87013498266Sopenharmony_ci free(co); 87113498266Sopenharmony_ci return NULL; 87213498266Sopenharmony_ci } 87313498266Sopenharmony_ci /* strip off the possible end-of-line characters */ 87413498266Sopenharmony_ci ptr = strchr(lineptr, '\r'); 87513498266Sopenharmony_ci if(ptr) 87613498266Sopenharmony_ci *ptr = 0; /* clear it */ 87713498266Sopenharmony_ci ptr = strchr(lineptr, '\n'); 87813498266Sopenharmony_ci if(ptr) 87913498266Sopenharmony_ci *ptr = 0; /* clear it */ 88013498266Sopenharmony_ci 88113498266Sopenharmony_ci firstptr = strtok_r((char *)lineptr, "\t", &tok_buf); /* tokenize on TAB */ 88213498266Sopenharmony_ci 88313498266Sopenharmony_ci /* 88413498266Sopenharmony_ci * Now loop through the fields and init the struct we already have 88513498266Sopenharmony_ci * allocated 88613498266Sopenharmony_ci */ 88713498266Sopenharmony_ci for(ptr = firstptr, fields = 0; ptr && !badcookie; 88813498266Sopenharmony_ci ptr = strtok_r(NULL, "\t", &tok_buf), fields++) { 88913498266Sopenharmony_ci switch(fields) { 89013498266Sopenharmony_ci case 0: 89113498266Sopenharmony_ci if(ptr[0]=='.') /* skip preceding dots */ 89213498266Sopenharmony_ci ptr++; 89313498266Sopenharmony_ci co->domain = strdup(ptr); 89413498266Sopenharmony_ci if(!co->domain) 89513498266Sopenharmony_ci badcookie = TRUE; 89613498266Sopenharmony_ci break; 89713498266Sopenharmony_ci case 1: 89813498266Sopenharmony_ci /* 89913498266Sopenharmony_ci * flag: A TRUE/FALSE value indicating if all machines within a given 90013498266Sopenharmony_ci * domain can access the variable. Set TRUE when the cookie says 90113498266Sopenharmony_ci * .domain.com and to false when the domain is complete www.domain.com 90213498266Sopenharmony_ci */ 90313498266Sopenharmony_ci co->tailmatch = strcasecompare(ptr, "TRUE")?TRUE:FALSE; 90413498266Sopenharmony_ci break; 90513498266Sopenharmony_ci case 2: 90613498266Sopenharmony_ci /* The file format allows the path field to remain not filled in */ 90713498266Sopenharmony_ci if(strcmp("TRUE", ptr) && strcmp("FALSE", ptr)) { 90813498266Sopenharmony_ci /* only if the path doesn't look like a boolean option! */ 90913498266Sopenharmony_ci co->path = strdup(ptr); 91013498266Sopenharmony_ci if(!co->path) 91113498266Sopenharmony_ci badcookie = TRUE; 91213498266Sopenharmony_ci else { 91313498266Sopenharmony_ci co->spath = sanitize_cookie_path(co->path); 91413498266Sopenharmony_ci if(!co->spath) { 91513498266Sopenharmony_ci badcookie = TRUE; /* out of memory bad */ 91613498266Sopenharmony_ci } 91713498266Sopenharmony_ci } 91813498266Sopenharmony_ci break; 91913498266Sopenharmony_ci } 92013498266Sopenharmony_ci /* this doesn't look like a path, make one up! */ 92113498266Sopenharmony_ci co->path = strdup("/"); 92213498266Sopenharmony_ci if(!co->path) 92313498266Sopenharmony_ci badcookie = TRUE; 92413498266Sopenharmony_ci co->spath = strdup("/"); 92513498266Sopenharmony_ci if(!co->spath) 92613498266Sopenharmony_ci badcookie = TRUE; 92713498266Sopenharmony_ci fields++; /* add a field and fall down to secure */ 92813498266Sopenharmony_ci FALLTHROUGH(); 92913498266Sopenharmony_ci case 3: 93013498266Sopenharmony_ci co->secure = FALSE; 93113498266Sopenharmony_ci if(strcasecompare(ptr, "TRUE")) { 93213498266Sopenharmony_ci if(secure || c->running) 93313498266Sopenharmony_ci co->secure = TRUE; 93413498266Sopenharmony_ci else 93513498266Sopenharmony_ci badcookie = TRUE; 93613498266Sopenharmony_ci } 93713498266Sopenharmony_ci break; 93813498266Sopenharmony_ci case 4: 93913498266Sopenharmony_ci if(curlx_strtoofft(ptr, NULL, 10, &co->expires)) 94013498266Sopenharmony_ci badcookie = TRUE; 94113498266Sopenharmony_ci break; 94213498266Sopenharmony_ci case 5: 94313498266Sopenharmony_ci co->name = strdup(ptr); 94413498266Sopenharmony_ci if(!co->name) 94513498266Sopenharmony_ci badcookie = TRUE; 94613498266Sopenharmony_ci else { 94713498266Sopenharmony_ci /* For Netscape file format cookies we check prefix on the name */ 94813498266Sopenharmony_ci if(strncasecompare("__Secure-", co->name, 9)) 94913498266Sopenharmony_ci co->prefix |= COOKIE_PREFIX__SECURE; 95013498266Sopenharmony_ci else if(strncasecompare("__Host-", co->name, 7)) 95113498266Sopenharmony_ci co->prefix |= COOKIE_PREFIX__HOST; 95213498266Sopenharmony_ci } 95313498266Sopenharmony_ci break; 95413498266Sopenharmony_ci case 6: 95513498266Sopenharmony_ci co->value = strdup(ptr); 95613498266Sopenharmony_ci if(!co->value) 95713498266Sopenharmony_ci badcookie = TRUE; 95813498266Sopenharmony_ci break; 95913498266Sopenharmony_ci } 96013498266Sopenharmony_ci } 96113498266Sopenharmony_ci if(6 == fields) { 96213498266Sopenharmony_ci /* we got a cookie with blank contents, fix it */ 96313498266Sopenharmony_ci co->value = strdup(""); 96413498266Sopenharmony_ci if(!co->value) 96513498266Sopenharmony_ci badcookie = TRUE; 96613498266Sopenharmony_ci else 96713498266Sopenharmony_ci fields++; 96813498266Sopenharmony_ci } 96913498266Sopenharmony_ci 97013498266Sopenharmony_ci if(!badcookie && (7 != fields)) 97113498266Sopenharmony_ci /* we did not find the sufficient number of fields */ 97213498266Sopenharmony_ci badcookie = TRUE; 97313498266Sopenharmony_ci 97413498266Sopenharmony_ci if(badcookie) { 97513498266Sopenharmony_ci freecookie(co); 97613498266Sopenharmony_ci return NULL; 97713498266Sopenharmony_ci } 97813498266Sopenharmony_ci 97913498266Sopenharmony_ci } 98013498266Sopenharmony_ci 98113498266Sopenharmony_ci if(co->prefix & COOKIE_PREFIX__SECURE) { 98213498266Sopenharmony_ci /* The __Secure- prefix only requires that the cookie be set secure */ 98313498266Sopenharmony_ci if(!co->secure) { 98413498266Sopenharmony_ci freecookie(co); 98513498266Sopenharmony_ci return NULL; 98613498266Sopenharmony_ci } 98713498266Sopenharmony_ci } 98813498266Sopenharmony_ci if(co->prefix & COOKIE_PREFIX__HOST) { 98913498266Sopenharmony_ci /* 99013498266Sopenharmony_ci * The __Host- prefix requires the cookie to be secure, have a "/" path 99113498266Sopenharmony_ci * and not have a domain set. 99213498266Sopenharmony_ci */ 99313498266Sopenharmony_ci if(co->secure && co->path && strcmp(co->path, "/") == 0 && !co->tailmatch) 99413498266Sopenharmony_ci ; 99513498266Sopenharmony_ci else { 99613498266Sopenharmony_ci freecookie(co); 99713498266Sopenharmony_ci return NULL; 99813498266Sopenharmony_ci } 99913498266Sopenharmony_ci } 100013498266Sopenharmony_ci 100113498266Sopenharmony_ci if(!c->running && /* read from a file */ 100213498266Sopenharmony_ci c->newsession && /* clean session cookies */ 100313498266Sopenharmony_ci !co->expires) { /* this is a session cookie since it doesn't expire! */ 100413498266Sopenharmony_ci freecookie(co); 100513498266Sopenharmony_ci return NULL; 100613498266Sopenharmony_ci } 100713498266Sopenharmony_ci 100813498266Sopenharmony_ci co->livecookie = c->running; 100913498266Sopenharmony_ci co->creationtime = ++c->lastct; 101013498266Sopenharmony_ci 101113498266Sopenharmony_ci /* 101213498266Sopenharmony_ci * Now we have parsed the incoming line, we must now check if this supersedes 101313498266Sopenharmony_ci * an already existing cookie, which it may if the previous have the same 101413498266Sopenharmony_ci * domain and path as this. 101513498266Sopenharmony_ci */ 101613498266Sopenharmony_ci 101713498266Sopenharmony_ci /* at first, remove expired cookies */ 101813498266Sopenharmony_ci if(!noexpire) 101913498266Sopenharmony_ci remove_expired(c); 102013498266Sopenharmony_ci 102113498266Sopenharmony_ci#ifdef USE_LIBPSL 102213498266Sopenharmony_ci /* 102313498266Sopenharmony_ci * Check if the domain is a Public Suffix and if yes, ignore the cookie. We 102413498266Sopenharmony_ci * must also check that the data handle isn't NULL since the psl code will 102513498266Sopenharmony_ci * dereference it. 102613498266Sopenharmony_ci */ 102713498266Sopenharmony_ci if(data && (domain && co->domain && !Curl_host_is_ipnum(co->domain))) { 102813498266Sopenharmony_ci bool acceptable = FALSE; 102913498266Sopenharmony_ci char lcase[256]; 103013498266Sopenharmony_ci char lcookie[256]; 103113498266Sopenharmony_ci size_t dlen = strlen(domain); 103213498266Sopenharmony_ci size_t clen = strlen(co->domain); 103313498266Sopenharmony_ci if((dlen < sizeof(lcase)) && (clen < sizeof(lcookie))) { 103413498266Sopenharmony_ci const psl_ctx_t *psl = Curl_psl_use(data); 103513498266Sopenharmony_ci if(psl) { 103613498266Sopenharmony_ci /* the PSL check requires lowercase domain name and pattern */ 103713498266Sopenharmony_ci Curl_strntolower(lcase, domain, dlen + 1); 103813498266Sopenharmony_ci Curl_strntolower(lcookie, co->domain, clen + 1); 103913498266Sopenharmony_ci acceptable = psl_is_cookie_domain_acceptable(psl, lcase, lcookie); 104013498266Sopenharmony_ci Curl_psl_release(data); 104113498266Sopenharmony_ci } 104213498266Sopenharmony_ci else 104313498266Sopenharmony_ci acceptable = !bad_domain(domain, strlen(domain)); 104413498266Sopenharmony_ci } 104513498266Sopenharmony_ci 104613498266Sopenharmony_ci if(!acceptable) { 104713498266Sopenharmony_ci infof(data, "cookie '%s' dropped, domain '%s' must not " 104813498266Sopenharmony_ci "set cookies for '%s'", co->name, domain, co->domain); 104913498266Sopenharmony_ci freecookie(co); 105013498266Sopenharmony_ci return NULL; 105113498266Sopenharmony_ci } 105213498266Sopenharmony_ci } 105313498266Sopenharmony_ci#endif 105413498266Sopenharmony_ci 105513498266Sopenharmony_ci /* A non-secure cookie may not overlay an existing secure cookie. */ 105613498266Sopenharmony_ci myhash = cookiehash(co->domain); 105713498266Sopenharmony_ci clist = c->cookies[myhash]; 105813498266Sopenharmony_ci while(clist) { 105913498266Sopenharmony_ci if(strcasecompare(clist->name, co->name)) { 106013498266Sopenharmony_ci /* the names are identical */ 106113498266Sopenharmony_ci bool matching_domains = FALSE; 106213498266Sopenharmony_ci 106313498266Sopenharmony_ci if(clist->domain && co->domain) { 106413498266Sopenharmony_ci if(strcasecompare(clist->domain, co->domain)) 106513498266Sopenharmony_ci /* The domains are identical */ 106613498266Sopenharmony_ci matching_domains = TRUE; 106713498266Sopenharmony_ci } 106813498266Sopenharmony_ci else if(!clist->domain && !co->domain) 106913498266Sopenharmony_ci matching_domains = TRUE; 107013498266Sopenharmony_ci 107113498266Sopenharmony_ci if(matching_domains && /* the domains were identical */ 107213498266Sopenharmony_ci clist->spath && co->spath && /* both have paths */ 107313498266Sopenharmony_ci clist->secure && !co->secure && !secure) { 107413498266Sopenharmony_ci size_t cllen; 107513498266Sopenharmony_ci const char *sep; 107613498266Sopenharmony_ci 107713498266Sopenharmony_ci /* 107813498266Sopenharmony_ci * A non-secure cookie may not overlay an existing secure cookie. 107913498266Sopenharmony_ci * For an existing cookie "a" with path "/login", refuse a new 108013498266Sopenharmony_ci * cookie "a" with for example path "/login/en", while the path 108113498266Sopenharmony_ci * "/loginhelper" is ok. 108213498266Sopenharmony_ci */ 108313498266Sopenharmony_ci 108413498266Sopenharmony_ci sep = strchr(clist->spath + 1, '/'); 108513498266Sopenharmony_ci 108613498266Sopenharmony_ci if(sep) 108713498266Sopenharmony_ci cllen = sep - clist->spath; 108813498266Sopenharmony_ci else 108913498266Sopenharmony_ci cllen = strlen(clist->spath); 109013498266Sopenharmony_ci 109113498266Sopenharmony_ci if(strncasecompare(clist->spath, co->spath, cllen)) { 109213498266Sopenharmony_ci infof(data, "cookie '%s' for domain '%s' dropped, would " 109313498266Sopenharmony_ci "overlay an existing cookie", co->name, co->domain); 109413498266Sopenharmony_ci freecookie(co); 109513498266Sopenharmony_ci return NULL; 109613498266Sopenharmony_ci } 109713498266Sopenharmony_ci } 109813498266Sopenharmony_ci } 109913498266Sopenharmony_ci 110013498266Sopenharmony_ci if(!replace_co && strcasecompare(clist->name, co->name)) { 110113498266Sopenharmony_ci /* the names are identical */ 110213498266Sopenharmony_ci 110313498266Sopenharmony_ci if(clist->domain && co->domain) { 110413498266Sopenharmony_ci if(strcasecompare(clist->domain, co->domain) && 110513498266Sopenharmony_ci (clist->tailmatch == co->tailmatch)) 110613498266Sopenharmony_ci /* The domains are identical */ 110713498266Sopenharmony_ci replace_old = TRUE; 110813498266Sopenharmony_ci } 110913498266Sopenharmony_ci else if(!clist->domain && !co->domain) 111013498266Sopenharmony_ci replace_old = TRUE; 111113498266Sopenharmony_ci 111213498266Sopenharmony_ci if(replace_old) { 111313498266Sopenharmony_ci /* the domains were identical */ 111413498266Sopenharmony_ci 111513498266Sopenharmony_ci if(clist->spath && co->spath && 111613498266Sopenharmony_ci !strcasecompare(clist->spath, co->spath)) 111713498266Sopenharmony_ci replace_old = FALSE; 111813498266Sopenharmony_ci else if(!clist->spath != !co->spath) 111913498266Sopenharmony_ci replace_old = FALSE; 112013498266Sopenharmony_ci } 112113498266Sopenharmony_ci 112213498266Sopenharmony_ci if(replace_old && !co->livecookie && clist->livecookie) { 112313498266Sopenharmony_ci /* 112413498266Sopenharmony_ci * Both cookies matched fine, except that the already present cookie is 112513498266Sopenharmony_ci * "live", which means it was set from a header, while the new one was 112613498266Sopenharmony_ci * read from a file and thus isn't "live". "live" cookies are preferred 112713498266Sopenharmony_ci * so the new cookie is freed. 112813498266Sopenharmony_ci */ 112913498266Sopenharmony_ci freecookie(co); 113013498266Sopenharmony_ci return NULL; 113113498266Sopenharmony_ci } 113213498266Sopenharmony_ci if(replace_old) { 113313498266Sopenharmony_ci replace_co = co; 113413498266Sopenharmony_ci replace_clist = clist; 113513498266Sopenharmony_ci } 113613498266Sopenharmony_ci } 113713498266Sopenharmony_ci lastc = clist; 113813498266Sopenharmony_ci clist = clist->next; 113913498266Sopenharmony_ci } 114013498266Sopenharmony_ci if(replace_co) { 114113498266Sopenharmony_ci co = replace_co; 114213498266Sopenharmony_ci clist = replace_clist; 114313498266Sopenharmony_ci co->next = clist->next; /* get the next-pointer first */ 114413498266Sopenharmony_ci 114513498266Sopenharmony_ci /* when replacing, creationtime is kept from old */ 114613498266Sopenharmony_ci co->creationtime = clist->creationtime; 114713498266Sopenharmony_ci 114813498266Sopenharmony_ci /* then free all the old pointers */ 114913498266Sopenharmony_ci free(clist->name); 115013498266Sopenharmony_ci free(clist->value); 115113498266Sopenharmony_ci free(clist->domain); 115213498266Sopenharmony_ci free(clist->path); 115313498266Sopenharmony_ci free(clist->spath); 115413498266Sopenharmony_ci 115513498266Sopenharmony_ci *clist = *co; /* then store all the new data */ 115613498266Sopenharmony_ci 115713498266Sopenharmony_ci free(co); /* free the newly allocated memory */ 115813498266Sopenharmony_ci co = clist; 115913498266Sopenharmony_ci } 116013498266Sopenharmony_ci 116113498266Sopenharmony_ci if(c->running) 116213498266Sopenharmony_ci /* Only show this when NOT reading the cookies from a file */ 116313498266Sopenharmony_ci infof(data, "%s cookie %s=\"%s\" for domain %s, path %s, " 116413498266Sopenharmony_ci "expire %" CURL_FORMAT_CURL_OFF_T, 116513498266Sopenharmony_ci replace_old?"Replaced":"Added", co->name, co->value, 116613498266Sopenharmony_ci co->domain, co->path, co->expires); 116713498266Sopenharmony_ci 116813498266Sopenharmony_ci if(!replace_old) { 116913498266Sopenharmony_ci /* then make the last item point on this new one */ 117013498266Sopenharmony_ci if(lastc) 117113498266Sopenharmony_ci lastc->next = co; 117213498266Sopenharmony_ci else 117313498266Sopenharmony_ci c->cookies[myhash] = co; 117413498266Sopenharmony_ci c->numcookies++; /* one more cookie in the jar */ 117513498266Sopenharmony_ci } 117613498266Sopenharmony_ci 117713498266Sopenharmony_ci /* 117813498266Sopenharmony_ci * Now that we've added a new cookie to the jar, update the expiration 117913498266Sopenharmony_ci * tracker in case it is the next one to expire. 118013498266Sopenharmony_ci */ 118113498266Sopenharmony_ci if(co->expires && (co->expires < c->next_expiration)) 118213498266Sopenharmony_ci c->next_expiration = co->expires; 118313498266Sopenharmony_ci 118413498266Sopenharmony_ci return co; 118513498266Sopenharmony_ci} 118613498266Sopenharmony_ci 118713498266Sopenharmony_ci 118813498266Sopenharmony_ci/* 118913498266Sopenharmony_ci * Curl_cookie_init() 119013498266Sopenharmony_ci * 119113498266Sopenharmony_ci * Inits a cookie struct to read data from a local file. This is always 119213498266Sopenharmony_ci * called before any cookies are set. File may be NULL in which case only the 119313498266Sopenharmony_ci * struct is initialized. Is file is "-" then STDIN is read. 119413498266Sopenharmony_ci * 119513498266Sopenharmony_ci * If 'newsession' is TRUE, discard all "session cookies" on read from file. 119613498266Sopenharmony_ci * 119713498266Sopenharmony_ci * Note that 'data' might be called as NULL pointer. If data is NULL, 'file' 119813498266Sopenharmony_ci * will be ignored. 119913498266Sopenharmony_ci * 120013498266Sopenharmony_ci * Returns NULL on out of memory. Invalid cookies are ignored. 120113498266Sopenharmony_ci */ 120213498266Sopenharmony_cistruct CookieInfo *Curl_cookie_init(struct Curl_easy *data, 120313498266Sopenharmony_ci const char *file, 120413498266Sopenharmony_ci struct CookieInfo *inc, 120513498266Sopenharmony_ci bool newsession) 120613498266Sopenharmony_ci{ 120713498266Sopenharmony_ci struct CookieInfo *c; 120813498266Sopenharmony_ci char *line = NULL; 120913498266Sopenharmony_ci FILE *handle = NULL; 121013498266Sopenharmony_ci 121113498266Sopenharmony_ci if(!inc) { 121213498266Sopenharmony_ci /* we didn't get a struct, create one */ 121313498266Sopenharmony_ci c = calloc(1, sizeof(struct CookieInfo)); 121413498266Sopenharmony_ci if(!c) 121513498266Sopenharmony_ci return NULL; /* failed to get memory */ 121613498266Sopenharmony_ci /* 121713498266Sopenharmony_ci * Initialize the next_expiration time to signal that we don't have enough 121813498266Sopenharmony_ci * information yet. 121913498266Sopenharmony_ci */ 122013498266Sopenharmony_ci c->next_expiration = CURL_OFF_T_MAX; 122113498266Sopenharmony_ci } 122213498266Sopenharmony_ci else { 122313498266Sopenharmony_ci /* we got an already existing one, use that */ 122413498266Sopenharmony_ci c = inc; 122513498266Sopenharmony_ci } 122613498266Sopenharmony_ci c->newsession = newsession; /* new session? */ 122713498266Sopenharmony_ci 122813498266Sopenharmony_ci if(data) { 122913498266Sopenharmony_ci FILE *fp = NULL; 123013498266Sopenharmony_ci if(file && *file) { 123113498266Sopenharmony_ci if(!strcmp(file, "-")) 123213498266Sopenharmony_ci fp = stdin; 123313498266Sopenharmony_ci else { 123413498266Sopenharmony_ci fp = fopen(file, "rb"); 123513498266Sopenharmony_ci if(!fp) 123613498266Sopenharmony_ci infof(data, "WARNING: failed to open cookie file \"%s\"", file); 123713498266Sopenharmony_ci else 123813498266Sopenharmony_ci handle = fp; 123913498266Sopenharmony_ci } 124013498266Sopenharmony_ci } 124113498266Sopenharmony_ci 124213498266Sopenharmony_ci c->running = FALSE; /* this is not running, this is init */ 124313498266Sopenharmony_ci if(fp) { 124413498266Sopenharmony_ci 124513498266Sopenharmony_ci line = malloc(MAX_COOKIE_LINE); 124613498266Sopenharmony_ci if(!line) 124713498266Sopenharmony_ci goto fail; 124813498266Sopenharmony_ci while(Curl_get_line(line, MAX_COOKIE_LINE, fp)) { 124913498266Sopenharmony_ci char *lineptr = line; 125013498266Sopenharmony_ci bool headerline = FALSE; 125113498266Sopenharmony_ci if(checkprefix("Set-Cookie:", line)) { 125213498266Sopenharmony_ci /* This is a cookie line, get it! */ 125313498266Sopenharmony_ci lineptr = &line[11]; 125413498266Sopenharmony_ci headerline = TRUE; 125513498266Sopenharmony_ci while(*lineptr && ISBLANK(*lineptr)) 125613498266Sopenharmony_ci lineptr++; 125713498266Sopenharmony_ci } 125813498266Sopenharmony_ci 125913498266Sopenharmony_ci Curl_cookie_add(data, c, headerline, TRUE, lineptr, NULL, NULL, TRUE); 126013498266Sopenharmony_ci } 126113498266Sopenharmony_ci free(line); /* free the line buffer */ 126213498266Sopenharmony_ci 126313498266Sopenharmony_ci /* 126413498266Sopenharmony_ci * Remove expired cookies from the hash. We must make sure to run this 126513498266Sopenharmony_ci * after reading the file, and not on every cookie. 126613498266Sopenharmony_ci */ 126713498266Sopenharmony_ci remove_expired(c); 126813498266Sopenharmony_ci 126913498266Sopenharmony_ci if(handle) 127013498266Sopenharmony_ci fclose(handle); 127113498266Sopenharmony_ci } 127213498266Sopenharmony_ci data->state.cookie_engine = TRUE; 127313498266Sopenharmony_ci } 127413498266Sopenharmony_ci c->running = TRUE; /* now, we're running */ 127513498266Sopenharmony_ci 127613498266Sopenharmony_ci return c; 127713498266Sopenharmony_ci 127813498266Sopenharmony_cifail: 127913498266Sopenharmony_ci free(line); 128013498266Sopenharmony_ci /* 128113498266Sopenharmony_ci * Only clean up if we allocated it here, as the original could still be in 128213498266Sopenharmony_ci * use by a share handle. 128313498266Sopenharmony_ci */ 128413498266Sopenharmony_ci if(!inc) 128513498266Sopenharmony_ci Curl_cookie_cleanup(c); 128613498266Sopenharmony_ci if(handle) 128713498266Sopenharmony_ci fclose(handle); 128813498266Sopenharmony_ci return NULL; /* out of memory */ 128913498266Sopenharmony_ci} 129013498266Sopenharmony_ci 129113498266Sopenharmony_ci/* 129213498266Sopenharmony_ci * cookie_sort 129313498266Sopenharmony_ci * 129413498266Sopenharmony_ci * Helper function to sort cookies such that the longest path gets before the 129513498266Sopenharmony_ci * shorter path. Path, domain and name lengths are considered in that order, 129613498266Sopenharmony_ci * with the creationtime as the tiebreaker. The creationtime is guaranteed to 129713498266Sopenharmony_ci * be unique per cookie, so we know we will get an ordering at that point. 129813498266Sopenharmony_ci */ 129913498266Sopenharmony_cistatic int cookie_sort(const void *p1, const void *p2) 130013498266Sopenharmony_ci{ 130113498266Sopenharmony_ci struct Cookie *c1 = *(struct Cookie **)p1; 130213498266Sopenharmony_ci struct Cookie *c2 = *(struct Cookie **)p2; 130313498266Sopenharmony_ci size_t l1, l2; 130413498266Sopenharmony_ci 130513498266Sopenharmony_ci /* 1 - compare cookie path lengths */ 130613498266Sopenharmony_ci l1 = c1->path ? strlen(c1->path) : 0; 130713498266Sopenharmony_ci l2 = c2->path ? strlen(c2->path) : 0; 130813498266Sopenharmony_ci 130913498266Sopenharmony_ci if(l1 != l2) 131013498266Sopenharmony_ci return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 131113498266Sopenharmony_ci 131213498266Sopenharmony_ci /* 2 - compare cookie domain lengths */ 131313498266Sopenharmony_ci l1 = c1->domain ? strlen(c1->domain) : 0; 131413498266Sopenharmony_ci l2 = c2->domain ? strlen(c2->domain) : 0; 131513498266Sopenharmony_ci 131613498266Sopenharmony_ci if(l1 != l2) 131713498266Sopenharmony_ci return (l2 > l1) ? 1 : -1 ; /* avoid size_t <=> int conversions */ 131813498266Sopenharmony_ci 131913498266Sopenharmony_ci /* 3 - compare cookie name lengths */ 132013498266Sopenharmony_ci l1 = c1->name ? strlen(c1->name) : 0; 132113498266Sopenharmony_ci l2 = c2->name ? strlen(c2->name) : 0; 132213498266Sopenharmony_ci 132313498266Sopenharmony_ci if(l1 != l2) 132413498266Sopenharmony_ci return (l2 > l1) ? 1 : -1; 132513498266Sopenharmony_ci 132613498266Sopenharmony_ci /* 4 - compare cookie creation time */ 132713498266Sopenharmony_ci return (c2->creationtime > c1->creationtime) ? 1 : -1; 132813498266Sopenharmony_ci} 132913498266Sopenharmony_ci 133013498266Sopenharmony_ci/* 133113498266Sopenharmony_ci * cookie_sort_ct 133213498266Sopenharmony_ci * 133313498266Sopenharmony_ci * Helper function to sort cookies according to creation time. 133413498266Sopenharmony_ci */ 133513498266Sopenharmony_cistatic int cookie_sort_ct(const void *p1, const void *p2) 133613498266Sopenharmony_ci{ 133713498266Sopenharmony_ci struct Cookie *c1 = *(struct Cookie **)p1; 133813498266Sopenharmony_ci struct Cookie *c2 = *(struct Cookie **)p2; 133913498266Sopenharmony_ci 134013498266Sopenharmony_ci return (c2->creationtime > c1->creationtime) ? 1 : -1; 134113498266Sopenharmony_ci} 134213498266Sopenharmony_ci 134313498266Sopenharmony_ci#define CLONE(field) \ 134413498266Sopenharmony_ci do { \ 134513498266Sopenharmony_ci if(src->field) { \ 134613498266Sopenharmony_ci d->field = strdup(src->field); \ 134713498266Sopenharmony_ci if(!d->field) \ 134813498266Sopenharmony_ci goto fail; \ 134913498266Sopenharmony_ci } \ 135013498266Sopenharmony_ci } while(0) 135113498266Sopenharmony_ci 135213498266Sopenharmony_cistatic struct Cookie *dup_cookie(struct Cookie *src) 135313498266Sopenharmony_ci{ 135413498266Sopenharmony_ci struct Cookie *d = calloc(1, sizeof(struct Cookie)); 135513498266Sopenharmony_ci if(d) { 135613498266Sopenharmony_ci CLONE(domain); 135713498266Sopenharmony_ci CLONE(path); 135813498266Sopenharmony_ci CLONE(spath); 135913498266Sopenharmony_ci CLONE(name); 136013498266Sopenharmony_ci CLONE(value); 136113498266Sopenharmony_ci d->expires = src->expires; 136213498266Sopenharmony_ci d->tailmatch = src->tailmatch; 136313498266Sopenharmony_ci d->secure = src->secure; 136413498266Sopenharmony_ci d->livecookie = src->livecookie; 136513498266Sopenharmony_ci d->httponly = src->httponly; 136613498266Sopenharmony_ci d->creationtime = src->creationtime; 136713498266Sopenharmony_ci } 136813498266Sopenharmony_ci return d; 136913498266Sopenharmony_ci 137013498266Sopenharmony_cifail: 137113498266Sopenharmony_ci freecookie(d); 137213498266Sopenharmony_ci return NULL; 137313498266Sopenharmony_ci} 137413498266Sopenharmony_ci 137513498266Sopenharmony_ci/* 137613498266Sopenharmony_ci * Curl_cookie_getlist 137713498266Sopenharmony_ci * 137813498266Sopenharmony_ci * For a given host and path, return a linked list of cookies that the client 137913498266Sopenharmony_ci * should send to the server if used now. The secure boolean informs the cookie 138013498266Sopenharmony_ci * if a secure connection is achieved or not. 138113498266Sopenharmony_ci * 138213498266Sopenharmony_ci * It shall only return cookies that haven't expired. 138313498266Sopenharmony_ci */ 138413498266Sopenharmony_cistruct Cookie *Curl_cookie_getlist(struct Curl_easy *data, 138513498266Sopenharmony_ci struct CookieInfo *c, 138613498266Sopenharmony_ci const char *host, const char *path, 138713498266Sopenharmony_ci bool secure) 138813498266Sopenharmony_ci{ 138913498266Sopenharmony_ci struct Cookie *newco; 139013498266Sopenharmony_ci struct Cookie *co; 139113498266Sopenharmony_ci struct Cookie *mainco = NULL; 139213498266Sopenharmony_ci size_t matches = 0; 139313498266Sopenharmony_ci bool is_ip; 139413498266Sopenharmony_ci const size_t myhash = cookiehash(host); 139513498266Sopenharmony_ci 139613498266Sopenharmony_ci if(!c || !c->cookies[myhash]) 139713498266Sopenharmony_ci return NULL; /* no cookie struct or no cookies in the struct */ 139813498266Sopenharmony_ci 139913498266Sopenharmony_ci /* at first, remove expired cookies */ 140013498266Sopenharmony_ci remove_expired(c); 140113498266Sopenharmony_ci 140213498266Sopenharmony_ci /* check if host is an IP(v4|v6) address */ 140313498266Sopenharmony_ci is_ip = Curl_host_is_ipnum(host); 140413498266Sopenharmony_ci 140513498266Sopenharmony_ci co = c->cookies[myhash]; 140613498266Sopenharmony_ci 140713498266Sopenharmony_ci while(co) { 140813498266Sopenharmony_ci /* if the cookie requires we're secure we must only continue if we are! */ 140913498266Sopenharmony_ci if(co->secure?secure:TRUE) { 141013498266Sopenharmony_ci 141113498266Sopenharmony_ci /* now check if the domain is correct */ 141213498266Sopenharmony_ci if(!co->domain || 141313498266Sopenharmony_ci (co->tailmatch && !is_ip && 141413498266Sopenharmony_ci cookie_tailmatch(co->domain, strlen(co->domain), host)) || 141513498266Sopenharmony_ci ((!co->tailmatch || is_ip) && strcasecompare(host, co->domain)) ) { 141613498266Sopenharmony_ci /* 141713498266Sopenharmony_ci * the right part of the host matches the domain stuff in the 141813498266Sopenharmony_ci * cookie data 141913498266Sopenharmony_ci */ 142013498266Sopenharmony_ci 142113498266Sopenharmony_ci /* 142213498266Sopenharmony_ci * now check the left part of the path with the cookies path 142313498266Sopenharmony_ci * requirement 142413498266Sopenharmony_ci */ 142513498266Sopenharmony_ci if(!co->spath || pathmatch(co->spath, path) ) { 142613498266Sopenharmony_ci 142713498266Sopenharmony_ci /* 142813498266Sopenharmony_ci * and now, we know this is a match and we should create an 142913498266Sopenharmony_ci * entry for the return-linked-list 143013498266Sopenharmony_ci */ 143113498266Sopenharmony_ci 143213498266Sopenharmony_ci newco = dup_cookie(co); 143313498266Sopenharmony_ci if(newco) { 143413498266Sopenharmony_ci /* then modify our next */ 143513498266Sopenharmony_ci newco->next = mainco; 143613498266Sopenharmony_ci 143713498266Sopenharmony_ci /* point the main to us */ 143813498266Sopenharmony_ci mainco = newco; 143913498266Sopenharmony_ci 144013498266Sopenharmony_ci matches++; 144113498266Sopenharmony_ci if(matches >= MAX_COOKIE_SEND_AMOUNT) { 144213498266Sopenharmony_ci infof(data, "Included max number of cookies (%zu) in request!", 144313498266Sopenharmony_ci matches); 144413498266Sopenharmony_ci break; 144513498266Sopenharmony_ci } 144613498266Sopenharmony_ci } 144713498266Sopenharmony_ci else 144813498266Sopenharmony_ci goto fail; 144913498266Sopenharmony_ci } 145013498266Sopenharmony_ci } 145113498266Sopenharmony_ci } 145213498266Sopenharmony_ci co = co->next; 145313498266Sopenharmony_ci } 145413498266Sopenharmony_ci 145513498266Sopenharmony_ci if(matches) { 145613498266Sopenharmony_ci /* 145713498266Sopenharmony_ci * Now we need to make sure that if there is a name appearing more than 145813498266Sopenharmony_ci * once, the longest specified path version comes first. To make this 145913498266Sopenharmony_ci * the swiftest way, we just sort them all based on path length. 146013498266Sopenharmony_ci */ 146113498266Sopenharmony_ci struct Cookie **array; 146213498266Sopenharmony_ci size_t i; 146313498266Sopenharmony_ci 146413498266Sopenharmony_ci /* alloc an array and store all cookie pointers */ 146513498266Sopenharmony_ci array = malloc(sizeof(struct Cookie *) * matches); 146613498266Sopenharmony_ci if(!array) 146713498266Sopenharmony_ci goto fail; 146813498266Sopenharmony_ci 146913498266Sopenharmony_ci co = mainco; 147013498266Sopenharmony_ci 147113498266Sopenharmony_ci for(i = 0; co; co = co->next) 147213498266Sopenharmony_ci array[i++] = co; 147313498266Sopenharmony_ci 147413498266Sopenharmony_ci /* now sort the cookie pointers in path length order */ 147513498266Sopenharmony_ci qsort(array, matches, sizeof(struct Cookie *), cookie_sort); 147613498266Sopenharmony_ci 147713498266Sopenharmony_ci /* remake the linked list order according to the new order */ 147813498266Sopenharmony_ci 147913498266Sopenharmony_ci mainco = array[0]; /* start here */ 148013498266Sopenharmony_ci for(i = 0; i<matches-1; i++) 148113498266Sopenharmony_ci array[i]->next = array[i + 1]; 148213498266Sopenharmony_ci array[matches-1]->next = NULL; /* terminate the list */ 148313498266Sopenharmony_ci 148413498266Sopenharmony_ci free(array); /* remove the temporary data again */ 148513498266Sopenharmony_ci } 148613498266Sopenharmony_ci 148713498266Sopenharmony_ci return mainco; /* return the new list */ 148813498266Sopenharmony_ci 148913498266Sopenharmony_cifail: 149013498266Sopenharmony_ci /* failure, clear up the allocated chain and return NULL */ 149113498266Sopenharmony_ci Curl_cookie_freelist(mainco); 149213498266Sopenharmony_ci return NULL; 149313498266Sopenharmony_ci} 149413498266Sopenharmony_ci 149513498266Sopenharmony_ci/* 149613498266Sopenharmony_ci * Curl_cookie_clearall 149713498266Sopenharmony_ci * 149813498266Sopenharmony_ci * Clear all existing cookies and reset the counter. 149913498266Sopenharmony_ci */ 150013498266Sopenharmony_civoid Curl_cookie_clearall(struct CookieInfo *cookies) 150113498266Sopenharmony_ci{ 150213498266Sopenharmony_ci if(cookies) { 150313498266Sopenharmony_ci unsigned int i; 150413498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) { 150513498266Sopenharmony_ci Curl_cookie_freelist(cookies->cookies[i]); 150613498266Sopenharmony_ci cookies->cookies[i] = NULL; 150713498266Sopenharmony_ci } 150813498266Sopenharmony_ci cookies->numcookies = 0; 150913498266Sopenharmony_ci } 151013498266Sopenharmony_ci} 151113498266Sopenharmony_ci 151213498266Sopenharmony_ci/* 151313498266Sopenharmony_ci * Curl_cookie_freelist 151413498266Sopenharmony_ci * 151513498266Sopenharmony_ci * Free a list of cookies previously returned by Curl_cookie_getlist(); 151613498266Sopenharmony_ci */ 151713498266Sopenharmony_civoid Curl_cookie_freelist(struct Cookie *co) 151813498266Sopenharmony_ci{ 151913498266Sopenharmony_ci struct Cookie *next; 152013498266Sopenharmony_ci while(co) { 152113498266Sopenharmony_ci next = co->next; 152213498266Sopenharmony_ci freecookie(co); 152313498266Sopenharmony_ci co = next; 152413498266Sopenharmony_ci } 152513498266Sopenharmony_ci} 152613498266Sopenharmony_ci 152713498266Sopenharmony_ci/* 152813498266Sopenharmony_ci * Curl_cookie_clearsess 152913498266Sopenharmony_ci * 153013498266Sopenharmony_ci * Free all session cookies in the cookies list. 153113498266Sopenharmony_ci */ 153213498266Sopenharmony_civoid Curl_cookie_clearsess(struct CookieInfo *cookies) 153313498266Sopenharmony_ci{ 153413498266Sopenharmony_ci struct Cookie *first, *curr, *next, *prev = NULL; 153513498266Sopenharmony_ci unsigned int i; 153613498266Sopenharmony_ci 153713498266Sopenharmony_ci if(!cookies) 153813498266Sopenharmony_ci return; 153913498266Sopenharmony_ci 154013498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) { 154113498266Sopenharmony_ci if(!cookies->cookies[i]) 154213498266Sopenharmony_ci continue; 154313498266Sopenharmony_ci 154413498266Sopenharmony_ci first = curr = prev = cookies->cookies[i]; 154513498266Sopenharmony_ci 154613498266Sopenharmony_ci for(; curr; curr = next) { 154713498266Sopenharmony_ci next = curr->next; 154813498266Sopenharmony_ci if(!curr->expires) { 154913498266Sopenharmony_ci if(first == curr) 155013498266Sopenharmony_ci first = next; 155113498266Sopenharmony_ci 155213498266Sopenharmony_ci if(prev == curr) 155313498266Sopenharmony_ci prev = next; 155413498266Sopenharmony_ci else 155513498266Sopenharmony_ci prev->next = next; 155613498266Sopenharmony_ci 155713498266Sopenharmony_ci freecookie(curr); 155813498266Sopenharmony_ci cookies->numcookies--; 155913498266Sopenharmony_ci } 156013498266Sopenharmony_ci else 156113498266Sopenharmony_ci prev = curr; 156213498266Sopenharmony_ci } 156313498266Sopenharmony_ci 156413498266Sopenharmony_ci cookies->cookies[i] = first; 156513498266Sopenharmony_ci } 156613498266Sopenharmony_ci} 156713498266Sopenharmony_ci 156813498266Sopenharmony_ci/* 156913498266Sopenharmony_ci * Curl_cookie_cleanup() 157013498266Sopenharmony_ci * 157113498266Sopenharmony_ci * Free a "cookie object" previous created with Curl_cookie_init(). 157213498266Sopenharmony_ci */ 157313498266Sopenharmony_civoid Curl_cookie_cleanup(struct CookieInfo *c) 157413498266Sopenharmony_ci{ 157513498266Sopenharmony_ci if(c) { 157613498266Sopenharmony_ci unsigned int i; 157713498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) 157813498266Sopenharmony_ci Curl_cookie_freelist(c->cookies[i]); 157913498266Sopenharmony_ci free(c); /* free the base struct as well */ 158013498266Sopenharmony_ci } 158113498266Sopenharmony_ci} 158213498266Sopenharmony_ci 158313498266Sopenharmony_ci/* 158413498266Sopenharmony_ci * get_netscape_format() 158513498266Sopenharmony_ci * 158613498266Sopenharmony_ci * Formats a string for Netscape output file, w/o a newline at the end. 158713498266Sopenharmony_ci * Function returns a char * to a formatted line. The caller is responsible 158813498266Sopenharmony_ci * for freeing the returned pointer. 158913498266Sopenharmony_ci */ 159013498266Sopenharmony_cistatic char *get_netscape_format(const struct Cookie *co) 159113498266Sopenharmony_ci{ 159213498266Sopenharmony_ci return aprintf( 159313498266Sopenharmony_ci "%s" /* httponly preamble */ 159413498266Sopenharmony_ci "%s%s\t" /* domain */ 159513498266Sopenharmony_ci "%s\t" /* tailmatch */ 159613498266Sopenharmony_ci "%s\t" /* path */ 159713498266Sopenharmony_ci "%s\t" /* secure */ 159813498266Sopenharmony_ci "%" CURL_FORMAT_CURL_OFF_T "\t" /* expires */ 159913498266Sopenharmony_ci "%s\t" /* name */ 160013498266Sopenharmony_ci "%s", /* value */ 160113498266Sopenharmony_ci co->httponly?"#HttpOnly_":"", 160213498266Sopenharmony_ci /* 160313498266Sopenharmony_ci * Make sure all domains are prefixed with a dot if they allow 160413498266Sopenharmony_ci * tailmatching. This is Mozilla-style. 160513498266Sopenharmony_ci */ 160613498266Sopenharmony_ci (co->tailmatch && co->domain && co->domain[0] != '.')? ".":"", 160713498266Sopenharmony_ci co->domain?co->domain:"unknown", 160813498266Sopenharmony_ci co->tailmatch?"TRUE":"FALSE", 160913498266Sopenharmony_ci co->path?co->path:"/", 161013498266Sopenharmony_ci co->secure?"TRUE":"FALSE", 161113498266Sopenharmony_ci co->expires, 161213498266Sopenharmony_ci co->name, 161313498266Sopenharmony_ci co->value?co->value:""); 161413498266Sopenharmony_ci} 161513498266Sopenharmony_ci 161613498266Sopenharmony_ci/* 161713498266Sopenharmony_ci * cookie_output() 161813498266Sopenharmony_ci * 161913498266Sopenharmony_ci * Writes all internally known cookies to the specified file. Specify 162013498266Sopenharmony_ci * "-" as file name to write to stdout. 162113498266Sopenharmony_ci * 162213498266Sopenharmony_ci * The function returns non-zero on write failure. 162313498266Sopenharmony_ci */ 162413498266Sopenharmony_cistatic CURLcode cookie_output(struct Curl_easy *data, 162513498266Sopenharmony_ci struct CookieInfo *c, const char *filename) 162613498266Sopenharmony_ci{ 162713498266Sopenharmony_ci struct Cookie *co; 162813498266Sopenharmony_ci FILE *out = NULL; 162913498266Sopenharmony_ci bool use_stdout = FALSE; 163013498266Sopenharmony_ci char *tempstore = NULL; 163113498266Sopenharmony_ci CURLcode error = CURLE_OK; 163213498266Sopenharmony_ci 163313498266Sopenharmony_ci if(!c) 163413498266Sopenharmony_ci /* no cookie engine alive */ 163513498266Sopenharmony_ci return CURLE_OK; 163613498266Sopenharmony_ci 163713498266Sopenharmony_ci /* at first, remove expired cookies */ 163813498266Sopenharmony_ci remove_expired(c); 163913498266Sopenharmony_ci 164013498266Sopenharmony_ci if(!strcmp("-", filename)) { 164113498266Sopenharmony_ci /* use stdout */ 164213498266Sopenharmony_ci out = stdout; 164313498266Sopenharmony_ci use_stdout = TRUE; 164413498266Sopenharmony_ci } 164513498266Sopenharmony_ci else { 164613498266Sopenharmony_ci error = Curl_fopen(data, filename, &out, &tempstore); 164713498266Sopenharmony_ci if(error) 164813498266Sopenharmony_ci goto error; 164913498266Sopenharmony_ci } 165013498266Sopenharmony_ci 165113498266Sopenharmony_ci fputs("# Netscape HTTP Cookie File\n" 165213498266Sopenharmony_ci "# https://curl.se/docs/http-cookies.html\n" 165313498266Sopenharmony_ci "# This file was generated by libcurl! Edit at your own risk.\n\n", 165413498266Sopenharmony_ci out); 165513498266Sopenharmony_ci 165613498266Sopenharmony_ci if(c->numcookies) { 165713498266Sopenharmony_ci unsigned int i; 165813498266Sopenharmony_ci size_t nvalid = 0; 165913498266Sopenharmony_ci struct Cookie **array; 166013498266Sopenharmony_ci 166113498266Sopenharmony_ci array = calloc(1, sizeof(struct Cookie *) * c->numcookies); 166213498266Sopenharmony_ci if(!array) { 166313498266Sopenharmony_ci error = CURLE_OUT_OF_MEMORY; 166413498266Sopenharmony_ci goto error; 166513498266Sopenharmony_ci } 166613498266Sopenharmony_ci 166713498266Sopenharmony_ci /* only sort the cookies with a domain property */ 166813498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) { 166913498266Sopenharmony_ci for(co = c->cookies[i]; co; co = co->next) { 167013498266Sopenharmony_ci if(!co->domain) 167113498266Sopenharmony_ci continue; 167213498266Sopenharmony_ci array[nvalid++] = co; 167313498266Sopenharmony_ci } 167413498266Sopenharmony_ci } 167513498266Sopenharmony_ci 167613498266Sopenharmony_ci qsort(array, nvalid, sizeof(struct Cookie *), cookie_sort_ct); 167713498266Sopenharmony_ci 167813498266Sopenharmony_ci for(i = 0; i < nvalid; i++) { 167913498266Sopenharmony_ci char *format_ptr = get_netscape_format(array[i]); 168013498266Sopenharmony_ci if(!format_ptr) { 168113498266Sopenharmony_ci free(array); 168213498266Sopenharmony_ci error = CURLE_OUT_OF_MEMORY; 168313498266Sopenharmony_ci goto error; 168413498266Sopenharmony_ci } 168513498266Sopenharmony_ci fprintf(out, "%s\n", format_ptr); 168613498266Sopenharmony_ci free(format_ptr); 168713498266Sopenharmony_ci } 168813498266Sopenharmony_ci 168913498266Sopenharmony_ci free(array); 169013498266Sopenharmony_ci } 169113498266Sopenharmony_ci 169213498266Sopenharmony_ci if(!use_stdout) { 169313498266Sopenharmony_ci fclose(out); 169413498266Sopenharmony_ci out = NULL; 169513498266Sopenharmony_ci if(tempstore && Curl_rename(tempstore, filename)) { 169613498266Sopenharmony_ci unlink(tempstore); 169713498266Sopenharmony_ci error = CURLE_WRITE_ERROR; 169813498266Sopenharmony_ci goto error; 169913498266Sopenharmony_ci } 170013498266Sopenharmony_ci } 170113498266Sopenharmony_ci 170213498266Sopenharmony_ci /* 170313498266Sopenharmony_ci * If we reach here we have successfully written a cookie file so there is 170413498266Sopenharmony_ci * no need to inspect the error, any error case should have jumped into the 170513498266Sopenharmony_ci * error block below. 170613498266Sopenharmony_ci */ 170713498266Sopenharmony_ci free(tempstore); 170813498266Sopenharmony_ci return CURLE_OK; 170913498266Sopenharmony_ci 171013498266Sopenharmony_cierror: 171113498266Sopenharmony_ci if(out && !use_stdout) 171213498266Sopenharmony_ci fclose(out); 171313498266Sopenharmony_ci free(tempstore); 171413498266Sopenharmony_ci return error; 171513498266Sopenharmony_ci} 171613498266Sopenharmony_ci 171713498266Sopenharmony_cistatic struct curl_slist *cookie_list(struct Curl_easy *data) 171813498266Sopenharmony_ci{ 171913498266Sopenharmony_ci struct curl_slist *list = NULL; 172013498266Sopenharmony_ci struct curl_slist *beg; 172113498266Sopenharmony_ci struct Cookie *c; 172213498266Sopenharmony_ci char *line; 172313498266Sopenharmony_ci unsigned int i; 172413498266Sopenharmony_ci 172513498266Sopenharmony_ci if(!data->cookies || (data->cookies->numcookies == 0)) 172613498266Sopenharmony_ci return NULL; 172713498266Sopenharmony_ci 172813498266Sopenharmony_ci for(i = 0; i < COOKIE_HASH_SIZE; i++) { 172913498266Sopenharmony_ci for(c = data->cookies->cookies[i]; c; c = c->next) { 173013498266Sopenharmony_ci if(!c->domain) 173113498266Sopenharmony_ci continue; 173213498266Sopenharmony_ci line = get_netscape_format(c); 173313498266Sopenharmony_ci if(!line) { 173413498266Sopenharmony_ci curl_slist_free_all(list); 173513498266Sopenharmony_ci return NULL; 173613498266Sopenharmony_ci } 173713498266Sopenharmony_ci beg = Curl_slist_append_nodup(list, line); 173813498266Sopenharmony_ci if(!beg) { 173913498266Sopenharmony_ci free(line); 174013498266Sopenharmony_ci curl_slist_free_all(list); 174113498266Sopenharmony_ci return NULL; 174213498266Sopenharmony_ci } 174313498266Sopenharmony_ci list = beg; 174413498266Sopenharmony_ci } 174513498266Sopenharmony_ci } 174613498266Sopenharmony_ci 174713498266Sopenharmony_ci return list; 174813498266Sopenharmony_ci} 174913498266Sopenharmony_ci 175013498266Sopenharmony_cistruct curl_slist *Curl_cookie_list(struct Curl_easy *data) 175113498266Sopenharmony_ci{ 175213498266Sopenharmony_ci struct curl_slist *list; 175313498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 175413498266Sopenharmony_ci list = cookie_list(data); 175513498266Sopenharmony_ci Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 175613498266Sopenharmony_ci return list; 175713498266Sopenharmony_ci} 175813498266Sopenharmony_ci 175913498266Sopenharmony_civoid Curl_flush_cookies(struct Curl_easy *data, bool cleanup) 176013498266Sopenharmony_ci{ 176113498266Sopenharmony_ci CURLcode res; 176213498266Sopenharmony_ci 176313498266Sopenharmony_ci if(data->set.str[STRING_COOKIEJAR]) { 176413498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 176513498266Sopenharmony_ci 176613498266Sopenharmony_ci /* if we have a destination file for all the cookies to get dumped to */ 176713498266Sopenharmony_ci res = cookie_output(data, data->cookies, data->set.str[STRING_COOKIEJAR]); 176813498266Sopenharmony_ci if(res) 176913498266Sopenharmony_ci infof(data, "WARNING: failed to save cookies in %s: %s", 177013498266Sopenharmony_ci data->set.str[STRING_COOKIEJAR], curl_easy_strerror(res)); 177113498266Sopenharmony_ci } 177213498266Sopenharmony_ci else { 177313498266Sopenharmony_ci Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); 177413498266Sopenharmony_ci } 177513498266Sopenharmony_ci 177613498266Sopenharmony_ci if(cleanup && (!data->share || (data->cookies != data->share->cookies))) { 177713498266Sopenharmony_ci Curl_cookie_cleanup(data->cookies); 177813498266Sopenharmony_ci data->cookies = NULL; 177913498266Sopenharmony_ci } 178013498266Sopenharmony_ci Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); 178113498266Sopenharmony_ci} 178213498266Sopenharmony_ci 178313498266Sopenharmony_ci#endif /* CURL_DISABLE_HTTP || CURL_DISABLE_COOKIES */ 1784