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