]> sjero.net Git - wget/blob - src/cookies.c
[svn] Improve documentation of cookie code.
[wget] / src / cookies.c
1 /* Support for cookies.
2    Copyright (C) 2001, 2002 Free Software Foundation, Inc.
3
4 This file is part of GNU Wget.
5
6 GNU Wget is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at
9 your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Wget; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 In addition, as a special exception, the Free Software Foundation
21 gives permission to link the code of its release of Wget with the
22 OpenSSL project's "OpenSSL" library (or with modified versions of it
23 that use the same license as the "OpenSSL" library), and distribute
24 the linked executables.  You must obey the GNU General Public License
25 in all respects for all of the code used other than "OpenSSL".  If you
26 modify this file, you may extend this exception to your version of the
27 file, but you are not obligated to do so.  If you do not wish to do
28 so, delete this exception statement from your version.  */
29
30 /* Written by Hrvoje Niksic.  Parts are loosely inspired by cookie
31    code submitted by Tomasz Wegrzanowski.
32
33    Ideas for future work:
34
35    * Implement limits on cookie-related sizes, such as max. cookie
36      size, max. number of cookies, etc.
37
38    * Add more "cookie jar" methods, such as methods to iterate over
39      stored cookies, to clear temporary cookies, to perform
40      intelligent auto-saving, etc.
41
42    * Support `Set-Cookie2' and `Cookie2' headers?  Does anyone really
43      use them?  */
44
45 #include <config.h>
46
47 #include <stdio.h>
48 #ifdef HAVE_STRING_H
49 # include <string.h>
50 #else
51 # include <strings.h>
52 #endif
53 #include <stdlib.h>
54 #include <assert.h>
55 #include <errno.h>
56
57 #include "wget.h"
58 #include "utils.h"
59 #include "hash.h"
60 #include "cookies.h"
61
62 /* This should *really* be in a .h file!  */
63 time_t http_atotm PARAMS ((const char *));
64 \f
65 /* Declarations of `struct cookie' and the most basic functions. */
66
67 /* Cookie jar serves as cookie storage and a means of retrieving
68    cookies efficiently.  All cookies with the same domain are stored
69    in a linked list called "chain".  A cookie chain can be reached by
70    looking up the domain in the cookie jar's chains_by_domain table.
71
72    For example, to reach all the cookies under google.com, one must
73    execute hash_table_get(jar->chains_by_domain, "google.com").  Of
74    course, when sending a cookie to `www.google.com', one must search
75    for cookies that belong to either `www.google.com' or `google.com'
76    -- but the point is that the code doesn't need to go through *all*
77    the cookies.  */
78
79 struct cookie_jar {
80   /* Mapping between domains and their corresponding cookies.  */
81   struct hash_table *chains_by_domain;
82
83   int cookie_count;             /* number of cookies in the jar. */
84 };
85
86 /* Value set by entry point functions, so that the low-level
87    routines don't need to call time() all the time.  */
88 time_t cookies_now;
89
90 struct cookie_jar *
91 cookie_jar_new (void)
92 {
93   struct cookie_jar *jar = xmalloc (sizeof (struct cookie_jar));
94   jar->chains_by_domain = make_nocase_string_hash_table (0);
95   jar->cookie_count = 0;
96   return jar;
97 }
98
99 struct cookie {
100   char *domain;                 /* domain of the cookie */
101   int port;                     /* port number */
102   char *path;                   /* path prefix of the cookie */
103
104   int secure;                   /* whether cookie should be
105                                    transmitted over non-https
106                                    connections. */
107   int domain_exact;             /* whether DOMAIN must match as a
108                                    whole. */
109
110   int permanent;                /* whether the cookie should outlive
111                                    the session */
112   time_t expiry_time;           /* time when the cookie expires */
113
114   int discard_requested;        /* whether cookie was created to
115                                    request discarding another
116                                    cookie */
117
118   char *attr;                   /* cookie attribute name */
119   char *value;                  /* cookie attribute value */
120
121   struct cookie_jar *jar;       /* pointer back to the cookie jar, for
122                                    convenience. */
123   struct cookie *next;          /* used for chaining of cookies in the
124                                    same domain. */
125 };
126
127 #define PORT_ANY (-1)
128 #define COOKIE_EXPIRED_P(c) ((c)->expiry_time != 0 && (c)->expiry_time < cookies_now)
129
130 /* Allocate and return a new, empty cookie structure. */
131
132 static struct cookie *
133 cookie_new (void)
134 {
135   struct cookie *cookie = xmalloc (sizeof (struct cookie));
136   memset (cookie, '\0', sizeof (struct cookie));
137
138   /* Both cookie->permanent and cookie->expiry_time are now 0.  By
139      default, we assume that the cookie is non-permanent and valid
140      until the end of the session.  */
141
142   cookie->port = PORT_ANY;
143   return cookie;
144 }
145
146 /* Deallocate COOKIE and its components. */
147
148 static void
149 delete_cookie (struct cookie *cookie)
150 {
151   FREE_MAYBE (cookie->domain);
152   FREE_MAYBE (cookie->path);
153   FREE_MAYBE (cookie->attr);
154   FREE_MAYBE (cookie->value);
155   xfree (cookie);
156 }
157 \f
158 /* Functions for storing cookies.
159
160    All cookies can be reached beginning with jar->chains_by_domain.
161    The key in that table is the domain name, and the value is a linked
162    list of all cookies from that domain.  Every new cookie is placed
163    on the head of the list.  */
164
165 /* Find and return a cookie in JAR whose domain, path, and attribute
166    name correspond to COOKIE.  If found, PREVPTR will point to the
167    location of the cookie previous in chain, or NULL if the found
168    cookie is the head of a chain.
169
170    If no matching cookie is found, return NULL. */
171
172 static struct cookie *
173 find_matching_cookie (struct cookie_jar *jar, struct cookie *cookie,
174                       struct cookie **prevptr)
175 {
176   struct cookie *chain, *prev;
177
178   chain = hash_table_get (jar->chains_by_domain, cookie->domain);
179   if (!chain)
180     goto nomatch;
181
182   prev = NULL;
183   for (; chain; prev = chain, chain = chain->next)
184     if (0 == strcmp (cookie->path, chain->path)
185         && 0 == strcmp (cookie->attr, chain->attr)
186         && cookie->port == chain->port)
187       {
188         *prevptr = prev;
189         return chain;
190       }
191
192  nomatch:
193   *prevptr = NULL;
194   return NULL;
195 }
196
197 /* Store COOKIE to the jar.
198
199    This is done by placing COOKIE at the head of its chain.  However,
200    if COOKIE matches a cookie already in memory, as determined by
201    find_matching_cookie, the old cookie is unlinked and destroyed.
202
203    The key of each chain's hash table entry is allocated only the
204    first time; next hash_table_put's reuse the same key.  */
205
206 static void
207 store_cookie (struct cookie_jar *jar, struct cookie *cookie)
208 {
209   struct cookie *chain_head;
210   char *chain_key;
211
212   if (hash_table_get_pair (jar->chains_by_domain, cookie->domain,
213                            &chain_key, &chain_head))
214     {
215       /* A chain of cookies in this domain already exists.  Check for
216          duplicates -- if an extant cookie exactly matches our domain,
217          port, path, and name, replace it.  */
218       struct cookie *prev;
219       struct cookie *victim = find_matching_cookie (jar, cookie, &prev);
220
221       if (victim)
222         {
223           /* Remove VICTIM from the chain.  COOKIE will be placed at
224              the head. */
225           if (prev)
226             {
227               prev->next = victim->next;
228               cookie->next = chain_head;
229             }
230           else
231             {
232               /* prev is NULL; apparently VICTIM was at the head of
233                  the chain.  This place will be taken by COOKIE, so
234                  all we need to do is:  */
235               cookie->next = victim->next;
236             }
237           delete_cookie (victim);
238           --jar->cookie_count;
239           DEBUGP (("Deleted old cookie (to be replaced.)\n"));
240         }
241       else
242         cookie->next = chain_head;
243     }
244   else
245     {
246       /* We are now creating the chain.  Use a copy of cookie->domain
247          as the key for the life-time of the chain.  Using
248          cookie->domain would be unsafe because the life-time of the
249          chain may exceed the life-time of the cookie.  (Cookies may
250          be deleted from the chain by this very function.)  */
251       cookie->next = NULL;
252       chain_key = xstrdup (cookie->domain);
253     }
254
255   hash_table_put (jar->chains_by_domain, chain_key, cookie);
256   ++jar->cookie_count;
257
258   DEBUGP (("\nStored cookie %s %d%s %s %s %d %s %s %s\n",
259            cookie->domain, cookie->port,
260            cookie->port == PORT_ANY ? " (ANY)" : "",
261            cookie->path,
262            cookie->permanent ? "permanent" : "nonpermanent",
263            cookie->secure,
264            cookie->expiry_time
265            ? asctime (localtime (&cookie->expiry_time)) : "<undefined>",
266            cookie->attr, cookie->value));
267 }
268
269 /* Discard a cookie matching COOKIE's domain, port, path, and
270    attribute name.  This gets called when we encounter a cookie whose
271    expiry date is in the past, or whose max-age is set to 0.  The
272    former corresponds to netscape cookie spec, while the latter is
273    specified by rfc2109.  */
274
275 static void
276 discard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie)
277 {
278   struct cookie *prev, *victim;
279
280   if (!hash_table_count (jar->chains_by_domain))
281     /* No elements == nothing to discard. */
282     return;
283
284   victim = find_matching_cookie (jar, cookie, &prev);
285   if (victim)
286     {
287       if (prev)
288         /* Simply unchain the victim. */
289         prev->next = victim->next;
290       else
291         {
292           /* VICTIM was head of its chain.  We need to place a new
293              cookie at the head.  */
294           char *chain_key = NULL;
295           int res;
296
297           res = hash_table_get_pair (jar->chains_by_domain, victim->domain,
298                                      &chain_key, NULL);
299           assert (res != 0);
300           if (!victim->next)
301             {
302               /* VICTIM was the only cookie in the chain.  Destroy the
303                  chain and deallocate the chain key.  */
304               hash_table_remove (jar->chains_by_domain, victim->domain);
305               xfree (chain_key);
306             }
307           else
308             hash_table_put (jar->chains_by_domain, chain_key, victim->next);
309         }
310       delete_cookie (victim);
311       DEBUGP (("Discarded old cookie.\n"));
312     }
313 }
314 \f
315 /* Functions for parsing the `Set-Cookie' header, and creating new
316    cookies from the wire.  */
317
318 #define NAME_IS(string_literal)                                 \
319   BOUNDED_EQUAL_NO_CASE (name_b, name_e, string_literal)
320
321 #define VALUE_EXISTS (value_b && value_e)
322
323 #define VALUE_NON_EMPTY (VALUE_EXISTS && (value_b != value_e))
324
325 /* Update the appropriate cookie field.  [name_b, name_e) are expected
326    to delimit the attribute name, while [value_b, value_e) (optional)
327    should delimit the attribute value.
328
329    When called the first time, it will set the cookie's attribute name
330    and value.  After that, it will check the attribute name for
331    special fields such as `domain', `path', etc.  Where appropriate,
332    it will parse the values of the fields it recognizes and fill the
333    corresponding fields in COOKIE.
334
335    Returns 1 on success.  Returns zero in case a syntax error is
336    found; such a cookie should be discarded.  */
337
338 static int
339 update_cookie_field (struct cookie *cookie,
340                      const char *name_b, const char *name_e,
341                      const char *value_b, const char *value_e)
342 {
343   assert (name_b != NULL && name_e != NULL);
344
345   if (!cookie->attr)
346     {
347       if (!VALUE_EXISTS)
348         return 0;
349       cookie->attr = strdupdelim (name_b, name_e);
350       cookie->value = strdupdelim (value_b, value_e);
351       return 1;
352     }
353
354   if (NAME_IS ("domain"))
355     {
356       if (!VALUE_NON_EMPTY)
357         return 0;
358       FREE_MAYBE (cookie->domain);
359       /* Strictly speaking, we should set cookie->domain_exact if the
360          domain doesn't begin with a dot.  But many sites set the
361          domain to "foo.com" and expect "subhost.foo.com" to get the
362          cookie, and it apparently works.  */
363       if (*value_b == '.')
364         ++value_b;
365       cookie->domain = strdupdelim (value_b, value_e);
366       return 1;
367     }
368   else if (NAME_IS ("path"))
369     {
370       if (!VALUE_NON_EMPTY)
371         return 0;
372       FREE_MAYBE (cookie->path);
373       cookie->path = strdupdelim (value_b, value_e);
374       return 1;
375     }
376   else if (NAME_IS ("expires"))
377     {
378       char *value_copy;
379       time_t expires;
380
381       if (!VALUE_NON_EMPTY)
382         return 0;
383       BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);
384
385       expires = http_atotm (value_copy);
386       if (expires != -1)
387         {
388           cookie->permanent = 1;
389           cookie->expiry_time = (time_t)expires;
390         }
391       else
392         /* Error in expiration spec.  Assume default (cookie valid for
393            this session.)  */
394         ;
395
396       /* According to netscape's specification, expiry time in the
397          past means that discarding of a matching cookie is
398          requested.  */
399       if (cookie->expiry_time < cookies_now)
400         cookie->discard_requested = 1;
401
402       return 1;
403     }
404   else if (NAME_IS ("max-age"))
405     {
406       double maxage = -1;
407       char *value_copy;
408
409       if (!VALUE_NON_EMPTY)
410         return 0;
411       BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);
412
413       sscanf (value_copy, "%lf", &maxage);
414       if (maxage == -1)
415         /* something went wrong. */
416         return 0;
417       cookie->permanent = 1;
418       cookie->expiry_time = cookies_now + maxage;
419
420       /* According to rfc2109, a cookie with max-age of 0 means that
421          discarding of a matching cookie is requested.  */
422       if (maxage == 0)
423         cookie->discard_requested = 1;
424
425       return 1;
426     }
427   else if (NAME_IS ("secure"))
428     {
429       /* ignore value completely */
430       cookie->secure = 1;
431       return 1;
432     }
433   else
434     /* Unrecognized attribute; ignore it. */
435     return 1;
436 }
437
438 #undef NAME_IS
439
440 /* Returns non-zero for characters that are legal in the name of an
441    attribute.  This used to allow only alphanumerics, '-', and '_',
442    but we need to be more lenient because a number of sites wants to
443    use weirder attribute names.  rfc2965 "informally specifies"
444    attribute name (token) as "a sequence of non-special, non-white
445    space characters".  So we allow everything except the stuff we know
446    could harm us.  */
447
448 #define ATTR_NAME_CHAR(c) ((c) > 32 && (c) < 127        \
449                            && (c) != '"' && (c) != '='  \
450                            && (c) != ';' && (c) != ',')
451
452 /* Parse the contents of the `Set-Cookie' header.  The header looks
453    like this:
454
455    name1=value1; name2=value2; ...
456
457    Trailing semicolon is optional; spaces are allowed between all
458    tokens.  Additionally, values may be quoted.
459
460    A new cookie is returned upon success, NULL otherwise.  The
461    specified CALLBACK function (normally `update_cookie_field' is used
462    to update the fields of the newly created cookie structure.  */
463
464 static struct cookie *
465 parse_set_cookies (const char *sc,
466                    int (*callback) (struct cookie *,
467                                     const char *, const char *,
468                                     const char *, const char *),
469                    int silent)
470 {
471   struct cookie *cookie = cookie_new ();
472
473   /* #### Hand-written DFAs are no fun to debug.  We'de be better off
474      to rewrite this as an inline parser.  */
475
476   enum { S_START, S_NAME, S_NAME_POST,
477          S_VALUE_PRE, S_VALUE, S_QUOTED_VALUE, S_VALUE_TRAILSPACE,
478          S_ATTR_ACTION, S_DONE, S_ERROR
479   } state = S_START;
480
481   const char *p = sc;
482   char c;
483
484   const char *name_b  = NULL, *name_e  = NULL;
485   const char *value_b = NULL, *value_e = NULL;
486
487   c = *p;
488
489   while (state != S_DONE && state != S_ERROR)
490     {
491       switch (state)
492         {
493         case S_START:
494           if (!c)
495             state = S_DONE;
496           else if (ISSPACE (c))
497             /* Strip all whitespace preceding the name. */
498             c = *++p;
499           else if (ATTR_NAME_CHAR (c))
500             {
501               name_b = p;
502               state = S_NAME;
503             }
504           else
505             /* empty attr name not allowed */
506             state = S_ERROR;
507           break;
508         case S_NAME:
509           if (!c || c == ';' || c == '=' || ISSPACE (c))
510             {
511               name_e = p;
512               state = S_NAME_POST;
513             }
514           else if (ATTR_NAME_CHAR (c))
515             c = *++p;
516           else
517             state = S_ERROR;
518           break;
519         case S_NAME_POST:
520           if (!c || c == ';')
521             {
522               value_b = value_e = NULL;
523               if (c == ';')
524                 c = *++p;
525               state = S_ATTR_ACTION;
526             }
527           else if (c == '=')
528             {
529               c = *++p;
530               state = S_VALUE_PRE;
531             }
532           else if (ISSPACE (c))
533             /* Ignore space and keep the state. */
534             c = *++p;
535           else
536             state = S_ERROR;
537           break;
538         case S_VALUE_PRE:
539           if (!c || c == ';')
540             {
541               value_b = value_e = p;
542               if (c == ';')
543                 c = *++p;
544               state = S_ATTR_ACTION;
545             }
546           else if (c == '"')
547             {
548               c = *++p;
549               value_b = p;
550               state = S_QUOTED_VALUE;
551             }
552           else if (ISSPACE (c))
553             c = *++p;
554           else
555             {
556               value_b = p;
557               value_e = NULL;
558               state = S_VALUE;
559             }
560           break;
561         case S_VALUE:
562           if (!c || c == ';' || ISSPACE (c))
563             {
564               value_e = p;
565               state = S_VALUE_TRAILSPACE;
566             }
567           else
568             {
569               value_e = NULL;   /* no trailing space */
570               c = *++p;
571             }
572           break;
573         case S_QUOTED_VALUE:
574           if (c == '"')
575             {
576               value_e = p;
577               c = *++p;
578               state = S_VALUE_TRAILSPACE;
579             }
580           else if (!c)
581             state = S_ERROR;
582           else
583             c = *++p;
584           break;
585         case S_VALUE_TRAILSPACE:
586           if (c == ';')
587             {
588               c = *++p;
589               state = S_ATTR_ACTION;
590             }
591           else if (!c)
592             state = S_ATTR_ACTION;
593           else if (ISSPACE (c))
594             c = *++p;
595           else
596             state = S_VALUE;
597           break;
598         case S_ATTR_ACTION:
599           {
600             int legal = callback (cookie, name_b, name_e, value_b, value_e);
601             if (!legal)
602               {
603                 if (!silent)
604                   {
605                     char *name;
606                     BOUNDED_TO_ALLOCA (name_b, name_e, name);
607                     logprintf (LOG_NOTQUIET,
608                                _("Error in Set-Cookie, field `%s'"), name);
609                   }
610                 state = S_ERROR;
611                 break;
612               }
613             state = S_START;
614           }
615           break;
616         case S_DONE:
617         case S_ERROR:
618           /* handled by loop condition */
619           break;
620         }
621     }
622   if (state == S_DONE)
623     return cookie;
624
625   delete_cookie (cookie);
626   if (state != S_ERROR)
627     abort ();
628
629   if (!silent)
630     logprintf (LOG_NOTQUIET,
631                _("Syntax error in Set-Cookie: %s at position %d.\n"),
632                sc, p - sc);
633   return NULL;
634 }
635 \f
636 /* Sanity checks.  These are important, otherwise it is possible for
637    mailcious attackers to destroy important cookie information and/or
638    violate your privacy.  */
639
640
641 #define REQUIRE_DIGITS(p) do {                  \
642   if (!ISDIGIT (*p))                            \
643     return 0;                                   \
644   for (++p; ISDIGIT (*p); p++)                  \
645     ;                                           \
646 } while (0)
647
648 #define REQUIRE_DOT(p) do {                     \
649   if (*p++ != '.')                              \
650     return 0;                                   \
651 } while (0)
652
653 /* Check whether ADDR matches <digits>.<digits>.<digits>.<digits>.
654
655   We don't want to call network functions like inet_addr() because all
656   we need is a check, preferrably one that is small, fast, and
657   well-defined.  */
658
659 static int
660 numeric_address_p (const char *addr)
661 {
662   const char *p = addr;
663
664   REQUIRE_DIGITS (p);           /* A */
665   REQUIRE_DOT (p);              /* . */
666   REQUIRE_DIGITS (p);           /* B */
667   REQUIRE_DOT (p);              /* . */
668   REQUIRE_DIGITS (p);           /* C */
669   REQUIRE_DOT (p);              /* . */
670   REQUIRE_DIGITS (p);           /* D */
671
672   if (*p != '\0')
673     return 0;
674   return 1;
675 }
676
677 /* Check whether COOKIE_DOMAIN is an appropriate domain for HOST.
678    Originally I tried to make the check compliant with rfc2109, but
679    the sites deviated too often, so I had to fall back to "tail
680    matching", as defined by the original Netscape's cookie spec.  */
681
682 static int
683 check_domain_match (const char *cookie_domain, const char *host)
684 {
685   DEBUGP (("cdm: 1"));
686
687   /* Numeric address requires exact match.  It also requires HOST to
688      be an IP address.  */
689   if (numeric_address_p (cookie_domain))
690     return 0 == strcmp (cookie_domain, host);
691
692   DEBUGP ((" 2"));
693
694   /* For the sake of efficiency, check for exact match first. */
695   if (0 == strcasecmp (cookie_domain, host))
696     return 1;
697
698   DEBUGP ((" 3"));
699
700   /* HOST must match the tail of cookie_domain. */
701   if (!match_tail (host, cookie_domain, 1))
702     return 0;
703
704   /* We know that COOKIE_DOMAIN is a subset of HOST; however, we must
705      make sure that somebody is not trying to set the cookie for a
706      subdomain shared by many entities.  For example, "company.co.uk"
707      must not be allowed to set a cookie for ".co.uk".  On the other
708      hand, "sso.redhat.de" should be able to set a cookie for
709      ".redhat.de".
710
711      The only marginally sane way to handle this I can think of is to
712      reject on the basis of the length of the second-level domain name
713      (but when the top-level domain is unknown), with the assumption
714      that those of three or less characters could be reserved.  For
715      example:
716
717           .co.org -> works because the TLD is known
718            .co.uk -> doesn't work because "co" is only two chars long
719           .com.au -> doesn't work because "com" is only 3 chars long
720           .cnn.uk -> doesn't work because "cnn" is also only 3 chars long (ugh)
721           .cnn.de -> doesn't work for the same reason (ugh!!)
722          .abcd.de -> works because "abcd" is 4 chars long
723       .img.cnn.de -> works because it's not trying to set the 2nd level domain
724        .cnn.co.uk -> works for the same reason
725
726     That should prevent misuse, while allowing reasonable usage.  If
727     someone knows of a better way to handle this, please let me
728     know.  */
729   {
730     const char *p = cookie_domain;
731     int dccount = 1;            /* number of domain components */
732     int ldcl  = 0;              /* last domain component length */
733     int nldcl = 0;              /* next to last domain component length */
734     int out;
735     if (*p == '.')
736       /* Ignore leading period in this calculation. */
737       ++p;
738     DEBUGP ((" 4"));
739     for (out = 0; !out; p++)
740       switch (*p)
741         {
742         case '\0':
743           out = 1;
744           break;
745         case '.':
746           if (ldcl == 0)
747             /* Empty domain component found -- the domain is invalid. */
748             return 0;
749           if (*(p + 1) == '\0')
750             {
751               /* Tolerate trailing '.' by not treating the domain as
752                  one ending with an empty domain component.  */
753               out = 1;
754               break;
755             }
756           nldcl = ldcl;
757           ldcl  = 0;
758           ++dccount;
759           break;
760         default:
761           ++ldcl;
762         }
763
764     DEBUGP ((" 5"));
765
766     if (dccount < 2)
767       return 0;
768
769     DEBUGP ((" 6"));
770
771     if (dccount == 2)
772       {
773         int i;
774         int known_toplevel = 0;
775         static char *known_toplevel_domains[] = {
776           ".com", ".edu", ".net", ".org", ".gov", ".mil", ".int"
777         };
778         for (i = 0; i < countof (known_toplevel_domains); i++)
779           if (match_tail (cookie_domain, known_toplevel_domains[i], 1))
780             {
781               known_toplevel = 1;
782               break;
783             }
784         if (!known_toplevel && nldcl <= 3)
785           return 0;
786       }
787   }
788
789   DEBUGP ((" 7"));
790
791   /* Don't allow the host "foobar.com" to set a cookie for domain
792      "bar.com".  */
793   if (*cookie_domain != '.')
794     {
795       int dlen = strlen (cookie_domain);
796       int hlen = strlen (host);
797       /* cookie host:    hostname.foobar.com */
798       /* desired domain:             bar.com */
799       /* '.' must be here in host-> ^        */
800       if (hlen > dlen && host[hlen - dlen - 1] != '.')
801         return 0;
802     }
803
804   DEBUGP ((" 8"));
805
806   return 1;
807 }
808
809 static int path_matches PARAMS ((const char *, const char *));
810
811 /* Check whether PATH begins with COOKIE_PATH. */
812
813 static int
814 check_path_match (const char *cookie_path, const char *path)
815 {
816   return path_matches (path, cookie_path);
817 }
818 \f
819 /* Process the HTTP `Set-Cookie' header.  This results in storing the
820    cookie or discarding a matching one, or ignoring it completely, all
821    depending on the contents.  */
822
823 void
824 cookie_jar_process_set_cookie (struct cookie_jar *jar,
825                                const char *host, int port,
826                                const char *path, const char *set_cookie)
827 {
828   struct cookie *cookie;
829   cookies_now = time (NULL);
830
831   cookie = parse_set_cookies (set_cookie, update_cookie_field, 0);
832   if (!cookie)
833     goto out;
834
835   /* Sanitize parts of cookie. */
836
837   if (!cookie->domain)
838     {
839     copy_domain:
840       cookie->domain = xstrdup (host);
841       cookie->port = port;
842     }
843   else
844     {
845       if (!check_domain_match (cookie->domain, host))
846         {
847           logprintf (LOG_NOTQUIET,
848                      "Cookie coming from %s attempted to set domain to %s\n",
849                      host, cookie->domain);
850           xfree (cookie->domain);
851           goto copy_domain;
852         }
853     }
854
855   if (!cookie->path)
856     cookie->path = xstrdup (path);
857   else
858     {
859       if (!check_path_match (cookie->path, path))
860         {
861           DEBUGP (("Attempt to fake the path: %s, %s\n",
862                    cookie->path, path));
863           goto out;
864         }
865     }
866
867   if (cookie->discard_requested)
868     {
869       discard_matching_cookie (jar, cookie);
870       goto out;
871     }
872
873   store_cookie (jar, cookie);
874   return;
875
876  out:
877   if (cookie)
878     delete_cookie (cookie);
879 }
880 \f
881 /* Support for sending out cookies in HTTP requests, based on
882    previously stored cookies.  Entry point is
883    `build_cookies_request'.  */
884
885 /* Find the cookie chains whose domains match HOST and store them to
886    DEST.
887
888    A cookie chain is the head of a list of cookies that belong to a
889    host/domain.  Given HOST "img.search.xemacs.org", this function
890    will return the chains for "img.search.xemacs.org",
891    "search.xemacs.org", and "xemacs.org" -- those of them that exist
892    (if any), that is.
893
894    DEST should be large enough to accept (in the worst case) as many
895    elements as there are domain components of HOST.  */
896
897 static int
898 find_chains_of_host (struct cookie_jar *jar, const char *host,
899                      struct cookie *dest[])
900 {
901   int dest_count = 0;
902   int passes, passcnt;
903
904   /* Bail out quickly if there are no cookies in the jar.  */
905   if (!hash_table_count (jar->chains_by_domain))
906     return 0;
907
908   if (numeric_address_p (host))
909     /* If host is an IP address, only check for the exact match. */
910     passes = 1;
911   else
912     /* Otherwise, check all the subdomains except the top-level (last)
913        one.  As a domain with N components has N-1 dots, the number of
914        passes equals the number of dots.  */
915     passes = count_char (host, '.');
916
917   passcnt = 0;
918
919   /* Find chains that match HOST, starting with exact match and
920      progressing to less specific domains.  For instance, given HOST
921      fly.srk.fer.hr, first look for fly.srk.fer.hr's chain, then
922      srk.fer.hr's, then fer.hr's.  */
923   while (1)
924     {
925       struct cookie *chain = hash_table_get (jar->chains_by_domain, host);
926       if (chain)
927         dest[dest_count++] = chain;
928       if (++passcnt >= passes)
929         break;
930       host = strchr (host, '.') + 1;
931     }
932
933   return dest_count;
934 }
935
936 /* If FULL_PATH begins with PREFIX, return the length of PREFIX, zero
937    otherwise.  */
938
939 static int
940 path_matches (const char *full_path, const char *prefix)
941 {
942   int len;
943
944   if (*prefix != '/')
945     /* Wget's HTTP paths do not begin with '/' (the URL code treats it
946        as a mere separator, inspired by rfc1808), but the '/' is
947        assumed when matching against the cookie stuff.  */
948     return 0;
949
950   ++prefix;
951   len = strlen (prefix);
952
953   if (0 != strncmp (full_path, prefix, len))
954     /* FULL_PATH doesn't begin with PREFIX. */
955     return 0;
956
957   /* Length of PREFIX determines the quality of the match. */
958   return len + 1;
959 }
960
961 /* Return non-zero iff COOKIE matches the provided parameters of the
962    URL being downloaded: HOST, PORT, PATH, and SECFLAG.
963
964    If PATH_GOODNESS is non-NULL, store the "path goodness" value
965    there.  That value is a measure of how closely COOKIE matches PATH,
966    used for ordering cookies.  */
967
968 static int
969 cookie_matches_url (const struct cookie *cookie,
970                     const char *host, int port, const char *path,
971                     int secflag, int *path_goodness)
972 {
973   int pg;
974
975   if (COOKIE_EXPIRED_P (cookie))
976     /* Ignore stale cookies.  Don't bother unchaining the cookie at
977        this point -- Wget is a relatively short-lived application, and
978        stale cookies will not be saved by `save_cookies'.  On the
979        other hand, this function should be as efficient as
980        possible.  */
981     return 0;
982
983   if (cookie->secure && !secflag)
984     /* Don't transmit secure cookies over insecure connections.  */
985     return 0;
986   if (cookie->port != PORT_ANY && cookie->port != port)
987     return 0;
988
989   /* If exact domain match is required, verify that cookie's domain is
990      equal to HOST.  If not, assume success on the grounds of the
991      cookie's chain having been found by find_chains_of_host.  */
992   if (cookie->domain_exact
993       && 0 != strcasecmp (host, cookie->domain))
994     return 0;
995
996   pg = path_matches (path, cookie->path);
997   if (!pg)
998     return 0;
999
1000   if (path_goodness)
1001     /* If the caller requested path_goodness, we return it.  This is
1002        an optimization, so that the caller doesn't need to call
1003        path_matches() again.  */
1004     *path_goodness = pg;
1005   return 1;
1006 }
1007
1008 /* A structure that points to a cookie, along with the additional
1009    information about the cookie's "goodness".  This allows us to sort
1010    the cookies when returning them to the server, as required by the
1011    spec.  */
1012
1013 struct weighed_cookie {
1014   struct cookie *cookie;
1015   int domain_goodness;
1016   int path_goodness;
1017 };
1018
1019 /* Comparator used for uniquifying the list. */
1020
1021 static int
1022 equality_comparator (const void *p1, const void *p2)
1023 {
1024   struct weighed_cookie *wc1 = (struct weighed_cookie *)p1;
1025   struct weighed_cookie *wc2 = (struct weighed_cookie *)p2;
1026
1027   int namecmp  = strcmp (wc1->cookie->attr, wc2->cookie->attr);
1028   int valuecmp = strcmp (wc1->cookie->value, wc2->cookie->value);
1029
1030   /* We only really care whether both name and value are equal.  We
1031      return them in this order only for consistency...  */
1032   return namecmp ? namecmp : valuecmp;
1033 }
1034
1035 /* Eliminate duplicate cookies.  "Duplicate cookies" are any two
1036    cookies with the same attr name and value.  Whenever a duplicate
1037    pair is found, one of the cookies is removed.  */
1038
1039 static int
1040 eliminate_dups (struct weighed_cookie *outgoing, int count)
1041 {
1042   struct weighed_cookie *h;     /* hare */
1043   struct weighed_cookie *t;     /* tortoise */
1044   struct weighed_cookie *end = outgoing + count;
1045
1046   /* We deploy a simple uniquify algorithm: first sort the array
1047      according to our sort criteria, then copy it to itself, comparing
1048      each cookie to its neighbor and ignoring the duplicates.  */
1049
1050   qsort (outgoing, count, sizeof (struct weighed_cookie), equality_comparator);
1051
1052   /* "Hare" runs through all the entries in the array, followed by
1053      "tortoise".  If a duplicate is found, the hare skips it.
1054      Non-duplicate entries are copied to the tortoise ptr.  */
1055
1056   for (h = t = outgoing; h < end; h++)
1057     {
1058       if (h != end - 1)
1059         {
1060           struct cookie *c0 = h[0].cookie;
1061           struct cookie *c1 = h[1].cookie;
1062           if (!strcmp (c0->attr, c1->attr) && !strcmp (c0->value, c1->value))
1063             continue;           /* ignore the duplicate */
1064         }
1065
1066       /* If the hare has advanced past the tortoise (because of
1067          previous dups), make sure the values get copied.  Otherwise,
1068          no copying is necessary.  */
1069       if (h != t)
1070         *t++ = *h;
1071       else
1072         t++;
1073     }
1074   return t - outgoing;
1075 }
1076
1077 /* Comparator used for sorting by quality. */
1078
1079 static int
1080 goodness_comparator (const void *p1, const void *p2)
1081 {
1082   struct weighed_cookie *wc1 = (struct weighed_cookie *)p1;
1083   struct weighed_cookie *wc2 = (struct weighed_cookie *)p2;
1084
1085   /* Subtractions take `wc2' as the first argument becauase we want a
1086      sort in *decreasing* order of goodness.  */
1087   int dgdiff = wc2->domain_goodness - wc1->domain_goodness;
1088   int pgdiff = wc2->path_goodness - wc1->path_goodness;
1089
1090   /* Sort by domain goodness; if these are the same, sort by path
1091      goodness.  (The sorting order isn't really specified; maybe it
1092      should be the other way around.)  */
1093   return dgdiff ? dgdiff : pgdiff;
1094 }
1095
1096 /* Generate a `Cookie' header for a request that goes to HOST:PORT and
1097    requests PATH from the server.  The resulting string is allocated
1098    with `malloc', and the caller is responsible for freeing it.  If no
1099    cookies pertain to this request, i.e. no cookie header should be
1100    generated, NULL is returned.  */
1101
1102 char *
1103 cookie_jar_generate_cookie_header (struct cookie_jar *jar, const char *host,
1104                                    int port, const char *path,
1105                                    int connection_secure_p)
1106 {
1107   struct cookie **chains;
1108   int chain_count;
1109
1110   struct cookie *cookie;
1111   struct weighed_cookie *outgoing;
1112   int count, i, ocnt;
1113   char *result;
1114   int result_size, pos;
1115
1116   /* First, find the cookie chains whose domains match HOST. */
1117
1118   /* Allocate room for find_chains_of_host to write to.  The number of
1119      chains can at most equal the number of subdomains, hence
1120      1+<number of dots>.  */
1121   chains = alloca_array (struct cookie *, 1 + count_char (host, '.'));
1122   chain_count = find_chains_of_host (jar, host, chains);
1123
1124   /* No cookies for this host. */
1125   if (!chain_count)
1126     return NULL;
1127
1128   cookies_now = time (NULL);
1129
1130   /* Now extract from the chains those cookies that match our host
1131      (for domain_exact cookies), port (for cookies with port other
1132      than PORT_ANY), etc.  See matching_cookie for details.  */
1133
1134   /* Count the number of matching cookies. */
1135   count = 0;
1136   for (i = 0; i < chain_count; i++)
1137     for (cookie = chains[i]; cookie; cookie = cookie->next)
1138       if (cookie_matches_url (cookie, host, port, path, connection_secure_p,
1139                               NULL))
1140         ++count;
1141   if (!count)
1142     return NULL;                /* no cookies matched */
1143
1144   /* Allocate the array. */
1145   outgoing = alloca_array (struct weighed_cookie, count);
1146
1147   /* Fill the array with all the matching cookies from the chains that
1148      match HOST. */
1149   ocnt = 0;
1150   for (i = 0; i < chain_count; i++)
1151     for (cookie = chains[i]; cookie; cookie = cookie->next)
1152       {
1153         int pg;
1154         if (!cookie_matches_url (cookie, host, port, path,
1155                                  connection_secure_p, &pg))
1156           continue;
1157         outgoing[ocnt].cookie = cookie;
1158         outgoing[ocnt].domain_goodness = strlen (cookie->domain);
1159         outgoing[ocnt].path_goodness   = pg;
1160         ++ocnt;
1161       }
1162   assert (ocnt == count);
1163
1164   /* Eliminate duplicate cookies; that is, those whose name and value
1165      are the same.  */
1166   count = eliminate_dups (outgoing, count);
1167
1168   /* Sort the array so that best-matching domains come first, and
1169      that, within one domain, best-matching paths come first. */
1170   qsort (outgoing, count, sizeof (struct weighed_cookie), goodness_comparator);
1171
1172   /* Count the space the name=value pairs will take. */
1173   result_size = 0;
1174   for (i = 0; i < count; i++)
1175     {
1176       struct cookie *c = outgoing[i].cookie;
1177       /* name=value */
1178       result_size += strlen (c->attr) + 1 + strlen (c->value);
1179     }
1180
1181   /* Allocate output buffer:
1182      "Cookie: "       -- 8
1183      name=value pairs -- result_size
1184      "; " separators  -- (count - 1) * 2
1185      \r\n line ending -- 2
1186      \0 terminator    -- 1 */
1187   result_size = 8 + result_size + (count - 1) * 2 + 2 + 1;
1188   result = xmalloc (result_size);
1189   pos = 0;
1190   strcpy (result, "Cookie: ");
1191   pos += 8;
1192   for (i = 0; i < count; i++)
1193     {
1194       struct cookie *c = outgoing[i].cookie;
1195       int namlen = strlen (c->attr);
1196       int vallen = strlen (c->value);
1197
1198       memcpy (result + pos, c->attr, namlen);
1199       pos += namlen;
1200       result[pos++] = '=';
1201       memcpy (result + pos, c->value, vallen);
1202       pos += vallen;
1203       if (i < count - 1)
1204         {
1205           result[pos++] = ';';
1206           result[pos++] = ' ';
1207         }
1208     }
1209   result[pos++] = '\r';
1210   result[pos++] = '\n';
1211   result[pos++] = '\0';
1212   assert (pos == result_size);
1213   return result;
1214 }
1215 \f
1216 /* Support for loading and saving cookies.  The format used for
1217    loading and saving should be the format of the `cookies.txt' file
1218    used by Netscape and Mozilla, at least the Unix versions.
1219    (Apparently IE can export cookies in that format as well.)  The
1220    format goes like this:
1221
1222        DOMAIN DOMAIN-FLAG PATH SECURE-FLAG TIMESTAMP ATTR-NAME ATTR-VALUE
1223
1224      DOMAIN      -- cookie domain, optionally followed by :PORT
1225      DOMAIN-FLAG -- whether all hosts in the domain match
1226      PATH        -- cookie path
1227      SECURE-FLAG -- whether cookie requires secure connection
1228      TIMESTAMP   -- expiry timestamp, number of seconds since epoch
1229      ATTR-NAME   -- name of the cookie attribute
1230      ATTR-VALUE  -- value of the cookie attribute (empty if absent)
1231
1232    The fields are separated by TABs.  All fields are mandatory, except
1233    for ATTR-VALUE.  The `-FLAG' fields are boolean, their legal values
1234    being "TRUE" and "FALSE'.  Empty lines, lines consisting of
1235    whitespace only, and comment lines (beginning with # optionally
1236    preceded by whitespace) are ignored.
1237
1238    Example line from cookies.txt (split in two lines for readability):
1239
1240        .google.com      TRUE    /       FALSE   2147368447      \
1241        PREF     ID=34bb47565bbcd47b:LD=en:NR=20:TM=985172580:LM=985739012
1242
1243 */
1244
1245 /* If the region [B, E) ends with :<digits>, parse the number, return
1246    it, and store new boundary (location of the `:') to DOMAIN_E_PTR.
1247    If port is not specified, return 0.  */
1248
1249 static int
1250 domain_port (const char *domain_b, const char *domain_e,
1251              const char **domain_e_ptr)
1252 {
1253   int port = 0;
1254   const char *p;
1255   const char *colon = memchr (domain_b, ':', domain_e - domain_b);
1256   if (!colon)
1257     return 0;
1258   for (p = colon + 1; p < domain_e && ISDIGIT (*p); p++)
1259     port = 10 * port + (*p - '0');
1260   if (p < domain_e)
1261     /* Garbage following port number. */
1262     return 0;
1263   *domain_e_ptr = colon;
1264   return port;
1265 }
1266
1267 #define GET_WORD(p, b, e) do {                  \
1268   b = p;                                        \
1269   while (*p && *p != '\t')                      \
1270     ++p;                                        \
1271   e = p;                                        \
1272   if (b == e || !*p)                            \
1273     goto next;                                  \
1274   ++p;                                          \
1275 } while (0)
1276
1277 /* Load cookies from FILE.  */
1278
1279 void
1280 cookie_jar_load (struct cookie_jar *jar, const char *file)
1281 {
1282   char *line;
1283   FILE *fp = fopen (file, "r");
1284   if (!fp)
1285     {
1286       logprintf (LOG_NOTQUIET, "Cannot open cookies file `%s': %s\n",
1287                  file, strerror (errno));
1288       return;
1289     }
1290   cookies_now = time (NULL);
1291
1292   for (; ((line = read_whole_line (fp)) != NULL); xfree (line))
1293     {
1294       struct cookie *cookie;
1295       char *p = line;
1296
1297       double expiry;
1298       int port;
1299
1300       char *domain_b  = NULL, *domain_e  = NULL;
1301       char *domflag_b = NULL, *domflag_e = NULL;
1302       char *path_b    = NULL, *path_e    = NULL;
1303       char *secure_b  = NULL, *secure_e  = NULL;
1304       char *expires_b = NULL, *expires_e = NULL;
1305       char *name_b    = NULL, *name_e    = NULL;
1306       char *value_b   = NULL, *value_e   = NULL;
1307
1308       /* Skip leading white-space. */
1309       while (*p && ISSPACE (*p))
1310         ++p;
1311       /* Ignore empty lines.  */
1312       if (!*p || *p == '#')
1313         continue;
1314
1315       GET_WORD (p, domain_b,  domain_e);
1316       GET_WORD (p, domflag_b, domflag_e);
1317       GET_WORD (p, path_b,    path_e);
1318       GET_WORD (p, secure_b,  secure_e);
1319       GET_WORD (p, expires_b, expires_e);
1320       GET_WORD (p, name_b,    name_e);
1321
1322       /* Don't use GET_WORD for value because it ends with newline,
1323          not TAB.  */
1324       value_b = p;
1325       value_e = p + strlen (p);
1326       if (value_e > value_b && value_e[-1] == '\n')
1327         --value_e;
1328       if (value_e > value_b && value_e[-1] == '\r')
1329         --value_e;
1330       /* Empty values are legal (I think), so don't bother checking. */
1331
1332       cookie = cookie_new ();
1333
1334       cookie->attr    = strdupdelim (name_b, name_e);
1335       cookie->value   = strdupdelim (value_b, value_e);
1336       cookie->path    = strdupdelim (path_b, path_e);
1337       cookie->secure  = BOUNDED_EQUAL (secure_b, secure_e, "TRUE");
1338
1339       /* Curl source says, quoting Andre Garcia: "flag: A TRUE/FALSE
1340          value indicating if all machines within a given domain can
1341          access the variable.  This value is set automatically by the
1342          browser, depending on the value set for the domain."  */
1343       cookie->domain_exact = !BOUNDED_EQUAL (domflag_b, domflag_e, "TRUE");
1344
1345       /* DOMAIN needs special treatment because we might need to
1346          extract the port.  */
1347       port = domain_port (domain_b, domain_e, (const char **)&domain_e);
1348       if (port)
1349         cookie->port = port;
1350
1351       if (*domain_b == '.')
1352         ++domain_b;             /* remove leading dot internally */
1353       cookie->domain  = strdupdelim (domain_b, domain_e);
1354
1355       /* safe default in case EXPIRES field is garbled. */
1356       expiry = (double)cookies_now - 1;
1357
1358       /* I don't like changing the line, but it's safe here.  (line is
1359          malloced.)  */
1360       *expires_e = '\0';
1361       sscanf (expires_b, "%lf", &expiry);
1362       if (expiry < cookies_now)
1363         /* ignore stale cookie. */
1364         goto abort;
1365       cookie->expiry_time = expiry;
1366
1367       /* If the cookie has survived being saved into an external file,
1368          it is obviously permanent.  */
1369       cookie->permanent = 1;
1370
1371       store_cookie (jar, cookie);
1372
1373     next:
1374       continue;
1375
1376     abort:
1377       delete_cookie (cookie);
1378     }
1379   fclose (fp);
1380 }
1381
1382 /* Mapper for save_cookies callable by hash_table_map.  VALUE points
1383    to the head in a chain of cookies.  The function prints the entire
1384    chain.  */
1385
1386 static int
1387 save_cookies_mapper (void *key, void *value, void *arg)
1388 {
1389   FILE *fp = (FILE *)arg;
1390   char *domain = (char *)key;
1391   struct cookie *cookie = (struct cookie *)value;
1392   for (; cookie; cookie = cookie->next)
1393     {
1394       if (!cookie->permanent)
1395         continue;
1396       if (COOKIE_EXPIRED_P (cookie))
1397         continue;
1398       if (!cookie->domain_exact)
1399         fputc ('.', fp);
1400       fputs (domain, fp);
1401       if (cookie->port != PORT_ANY)
1402         fprintf (fp, ":%d", cookie->port);
1403       fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n",
1404                cookie->domain_exact ? "FALSE" : "TRUE",
1405                cookie->path, cookie->secure ? "TRUE" : "FALSE",
1406                (double)cookie->expiry_time,
1407                cookie->attr, cookie->value);
1408       if (ferror (fp))
1409         return 1;               /* stop mapping */
1410     }
1411   return 0;
1412 }
1413
1414 /* Save cookies, in format described above, to FILE. */
1415
1416 void
1417 cookie_jar_save (struct cookie_jar *jar, const char *file)
1418 {
1419   FILE *fp;
1420
1421   DEBUGP (("Saving cookies to %s.\n", file));
1422
1423   cookies_now = time (NULL);
1424
1425   fp = fopen (file, "w");
1426   if (!fp)
1427     {
1428       logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"),
1429                  file, strerror (errno));
1430       return;
1431     }
1432
1433   fputs ("# HTTP cookie file.\n", fp);
1434   fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (NULL));
1435   fputs ("# Edit at your own risk.\n\n", fp);
1436
1437   hash_table_map (jar->chains_by_domain, save_cookies_mapper, fp);
1438
1439   if (ferror (fp))
1440     logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"),
1441                file, strerror (errno));
1442
1443   if (fclose (fp) < 0)
1444     logprintf (LOG_NOTQUIET, _("Error closing `%s': %s\n"),
1445                file, strerror (errno));
1446
1447   DEBUGP (("Done saving cookies.\n"));
1448 }
1449 \f
1450 /* Destroy all the elements in the chain and unhook it from the cookie
1451    jar.  This is written in the form of a callback to hash_table_map
1452    and used by cookie_jar_delete to delete all the cookies in a
1453    jar.  */
1454
1455 static int
1456 nuke_cookie_chain (void *value, void *key, void *arg)
1457 {
1458   char *chain_key = (char *)value;
1459   struct cookie *chain = (struct cookie *)key;
1460   struct cookie_jar *jar = (struct cookie_jar *)arg;
1461
1462   /* Remove the chain from the table and free the key. */
1463   hash_table_remove (jar->chains_by_domain, chain_key);
1464   xfree (chain_key);
1465
1466   /* Then delete all the cookies in the chain. */
1467   while (chain)
1468     {
1469       struct cookie *next = chain->next;
1470       delete_cookie (chain);
1471       chain = next;
1472     }
1473
1474   /* Keep mapping. */
1475   return 0;
1476 }
1477
1478 /* Clean up cookie-related data. */
1479
1480 void
1481 cookie_jar_delete (struct cookie_jar *jar)
1482 {
1483   hash_table_map (jar->chains_by_domain, nuke_cookie_chain, jar);
1484   hash_table_destroy (jar->chains_by_domain);
1485   xfree (jar);
1486 }
1487 \f
1488 /* Test cases.  Currently this is only tests parse_set_cookies.  To
1489    use, recompile Wget with -DTEST_COOKIES and call test_cookies()
1490    from main.  */
1491
1492 #ifdef TEST_COOKIES
1493 int test_count;
1494 char *test_results[10];
1495
1496 static int test_parse_cookies_callback (struct cookie *ignored,
1497                                         const char *nb, const char *ne,
1498                                         const char *vb, const char *ve)
1499 {
1500   test_results[test_count++] = strdupdelim (nb, ne);
1501   test_results[test_count++] = strdupdelim (vb, ve);
1502   return 1;
1503 }
1504
1505 void
1506 test_cookies (void)
1507 {
1508   /* Tests expected to succeed: */
1509   static struct {
1510     char *data;
1511     char *results[10];
1512   } tests_succ[] = {
1513     { "", {NULL} },
1514     { "arg=value", {"arg", "value", NULL} },
1515     { "arg1=value1;arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} },
1516     { "arg1=value1; arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} },
1517     { "arg1=value1;  arg2=value2;", {"arg1", "value1", "arg2", "value2", NULL} },
1518     { "arg1=value1;  arg2=value2;  ", {"arg1", "value1", "arg2", "value2", NULL} },
1519     { "arg1=\"value1\"; arg2=\"\"", {"arg1", "value1", "arg2", "", NULL} },
1520     { "arg=", {"arg", "", NULL} },
1521     { "arg1=; arg2=", {"arg1", "", "arg2", "", NULL} },
1522     { "arg1 = ; arg2= ", {"arg1", "", "arg2", "", NULL} },
1523   };
1524
1525   /* Tests expected to fail: */
1526   static char *tests_fail[] = {
1527     ";",
1528     "arg=\"unterminated",
1529     "=empty-name",
1530     "arg1=;=another-empty-name",
1531   };
1532   int i;
1533
1534   for (i = 0; i < countof (tests_succ); i++)
1535     {
1536       int ind;
1537       char *data = tests_succ[i].data;
1538       char **expected = tests_succ[i].results;
1539       struct cookie *c;
1540
1541       test_count = 0;
1542       c = parse_set_cookies (data, test_parse_cookies_callback, 1);
1543       if (!c)
1544         {
1545           printf ("NULL cookie returned for valid data: %s\n", data);
1546           continue;
1547         }
1548
1549       for (ind = 0; ind < test_count; ind += 2)
1550         {
1551           if (!expected[ind])
1552             break;
1553           if (0 != strcmp (expected[ind], test_results[ind]))
1554             printf ("Invalid name %d for '%s' (expected '%s', got '%s')\n",
1555                     ind / 2 + 1, data, expected[ind], test_results[ind]);
1556           if (0 != strcmp (expected[ind + 1], test_results[ind + 1]))
1557             printf ("Invalid value %d for '%s' (expected '%s', got '%s')\n",
1558                     ind / 2 + 1, data, expected[ind + 1], test_results[ind + 1]);
1559         }
1560       if (ind < test_count || expected[ind])
1561         printf ("Unmatched number of results: %s\n", data);
1562     }
1563
1564   for (i = 0; i < countof (tests_fail); i++)
1565     {
1566       struct cookie *c;
1567       char *data = tests_fail[i];
1568       test_count = 0;
1569       c = parse_set_cookies (data, test_parse_cookies_callback, 1);
1570       if (c)
1571         printf ("Failed to report error on invalid data: %s\n", data);
1572     }
1573 }
1574 #endif /* TEST_COOKIES */