source: UsbWattMeter/trunk/curl-7.47.1/lib/cookie.c

Last change on this file was 167, checked in by coas-nagasima, 8 years ago

MIMEにSJISを設定

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