source: EcnlProtoTool/trunk/curl-7.57.0/lib/cookie.c@ 331

Last change on this file since 331 was 331, checked in by coas-nagasima, 6 years ago

prototoolに関連するプロジェクトをnewlibからmuslを使うよう変更・更新
ntshellをnewlibの下位の実装から、muslのsyscallの実装に変更・更新
以下のOSSをアップデート
・mruby-1.3.0
・musl-1.1.18
・onigmo-6.1.3
・tcc-0.9.27
以下のOSSを追加
・openssl-1.1.0e
・curl-7.57.0
・zlib-1.2.11
以下のmrbgemsを追加
・iij/mruby-digest
・iij/mruby-env
・iij/mruby-errno
・iij/mruby-iijson
・iij/mruby-ipaddr
・iij/mruby-mock
・iij/mruby-require
・iij/mruby-tls-openssl

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