]> sjero.net Git - wget/blob - src/recur.c
[svn] Follow https links from http.
[wget] / src / recur.c
1 /* Handling of recursive HTTP retrieving.
2    Copyright (C) 1995, 1996, 1997, 2000, 2001 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
9 (at your option) any later version.
10
11 GNU Wget is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU 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 #include <config.h>
21
22 #include <stdio.h>
23 #include <stdlib.h>
24 #ifdef HAVE_STRING_H
25 # include <string.h>
26 #else
27 # include <strings.h>
28 #endif /* HAVE_STRING_H */
29 #ifdef HAVE_UNISTD_H
30 # include <unistd.h>
31 #endif /* HAVE_UNISTD_H */
32 #include <errno.h>
33 #include <assert.h>
34 #include <sys/types.h>
35
36 #include "wget.h"
37 #include "url.h"
38 #include "recur.h"
39 #include "utils.h"
40 #include "retr.h"
41 #include "ftp.h"
42 #include "fnmatch.h"
43 #include "host.h"
44 #include "hash.h"
45 #include "res.h"
46
47 #ifndef errno
48 extern int errno;
49 #endif
50
51 extern char *version_string;
52
53 static struct hash_table *dl_file_url_map;
54 static struct hash_table *dl_url_file_map;
55
56 /* List of HTML files downloaded in this Wget run, used for link
57    conversion after Wget is done.  The list and the set contain the
58    same information, except the list maintains the order.  Perhaps I
59    should get rid of the list, it's there for historical reasons.  */
60 static slist *downloaded_html_list;
61 static struct hash_table *downloaded_html_set;
62
63 static void register_delete_file PARAMS ((const char *));
64 \f
65 /* Functions for maintaining the URL queue.  */
66
67 struct queue_element {
68   const char *url;
69   const char *referer;
70   int depth;
71   struct queue_element *next;
72 };
73
74 struct url_queue {
75   struct queue_element *head;
76   struct queue_element *tail;
77   int count, maxcount;
78 };
79
80 /* Create a URL queue. */
81
82 static struct url_queue *
83 url_queue_new (void)
84 {
85   struct url_queue *queue = xmalloc (sizeof (*queue));
86   memset (queue, '\0', sizeof (*queue));
87   return queue;
88 }
89
90 /* Delete a URL queue. */
91
92 static void
93 url_queue_delete (struct url_queue *queue)
94 {
95   xfree (queue);
96 }
97
98 /* Enqueue a URL in the queue.  The queue is FIFO: the items will be
99    retrieved ("dequeued") from the queue in the order they were placed
100    into it.  */
101
102 static void
103 url_enqueue (struct url_queue *queue,
104              const char *url, const char *referer, int depth)
105 {
106   struct queue_element *qel = xmalloc (sizeof (*qel));
107   qel->url = url;
108   qel->referer = referer;
109   qel->depth = depth;
110   qel->next = NULL;
111
112   ++queue->count;
113   if (queue->count > queue->maxcount)
114     queue->maxcount = queue->count;
115
116   DEBUGP (("Enqueuing %s at depth %d\n", url, depth));
117   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
118
119   if (queue->tail)
120     queue->tail->next = qel;
121   queue->tail = qel;
122
123   if (!queue->head)
124     queue->head = queue->tail;
125 }
126
127 /* Take a URL out of the queue.  Return 1 if this operation succeeded,
128    or 0 if the queue is empty.  */
129
130 static int
131 url_dequeue (struct url_queue *queue,
132              const char **url, const char **referer, int *depth)
133 {
134   struct queue_element *qel = queue->head;
135
136   if (!qel)
137     return 0;
138
139   queue->head = queue->head->next;
140   if (!queue->head)
141     queue->tail = NULL;
142
143   *url = qel->url;
144   *referer = qel->referer;
145   *depth = qel->depth;
146
147   --queue->count;
148
149   DEBUGP (("Dequeuing %s at depth %d\n", qel->url, qel->depth));
150   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
151
152   xfree (qel);
153   return 1;
154 }
155 \f
156 static int download_child_p PARAMS ((const struct urlpos *, struct url *, int,
157                                      struct url *, struct hash_table *));
158 static int descend_redirect_p PARAMS ((const char *, const char *, int,
159                                        struct url *, struct hash_table *));
160
161
162 /* Retrieve a part of the web beginning with START_URL.  This used to
163    be called "recursive retrieval", because the old function was
164    recursive and implemented depth-first search.  retrieve_tree on the
165    other hand implements breadth-search traversal of the tree, which
166    results in much nicer ordering of downloads.
167
168    The algorithm this function uses is simple:
169
170    1. put START_URL in the queue.
171    2. while there are URLs in the queue:
172
173      3. get next URL from the queue.
174      4. download it.
175      5. if the URL is HTML and its depth does not exceed maximum depth,
176         get the list of URLs embedded therein.
177      6. for each of those URLs do the following:
178
179        7. if the URL is not one of those downloaded before, and if it
180           satisfies the criteria specified by the various command-line
181           options, add it to the queue. */
182
183 uerr_t
184 retrieve_tree (const char *start_url)
185 {
186   uerr_t status = RETROK;
187
188   /* The queue of URLs we need to load. */
189   struct url_queue *queue = url_queue_new ();
190
191   /* The URLs we do not wish to enqueue, because they are already in
192      the queue, but haven't been downloaded yet.  */
193   struct hash_table *blacklist = make_string_hash_table (0);
194
195   /* We'll need various components of this, so better get it over with
196      now. */
197   struct url *start_url_parsed = url_parse (start_url, NULL);
198
199   /* Enqueue the starting URL.  Use start_url_parsed->url rather than
200      just URL so we enqueue the canonical form of the URL.  */
201   url_enqueue (queue, xstrdup (start_url_parsed->url), NULL, 0);
202   string_set_add (blacklist, start_url_parsed->url);
203
204   while (1)
205     {
206       int descend = 0;
207       char *url, *referer, *file = NULL;
208       int depth;
209       boolean dash_p_leaf_HTML = FALSE;
210
211       if (downloaded_exceeds_quota ())
212         break;
213       if (status == FWRITEERR)
214         break;
215
216       /* Get the next URL from the queue... */
217
218       if (!url_dequeue (queue,
219                         (const char **)&url, (const char **)&referer,
220                         &depth))
221         break;
222
223       /* ...and download it.  Note that this download is in most cases
224          unconditional, as download_child_p already makes sure a file
225          doesn't get enqueued twice -- and yet this check is here, and
226          not in download_child_p.  This is so that if you run `wget -r
227          URL1 URL2', and a random URL is encountered once under URL1
228          and again under URL2, but at a different (possibly smaller)
229          depth, we want the URL's children to be taken into account
230          the second time.  */
231       if (dl_url_file_map && hash_table_contains (dl_url_file_map, url))
232         {
233           file = xstrdup (hash_table_get (dl_url_file_map, url));
234
235           DEBUGP (("Already downloaded \"%s\", reusing it from \"%s\".\n",
236                    url, file));
237
238           if (string_set_contains (downloaded_html_set, file))
239             descend = 1;
240         }
241       else
242         {
243           int dt = 0;
244           char *redirected = NULL;
245           int oldrec = opt.recursive;
246
247           opt.recursive = 0;
248           status = retrieve_url (url, &file, &redirected, referer, &dt);
249           opt.recursive = oldrec;
250
251           if (file && status == RETROK
252               && (dt & RETROKF) && (dt & TEXTHTML))
253             descend = 1;
254
255           if (redirected)
256             {
257               /* We have been redirected, possibly to another host, or
258                  different path, or wherever.  Check whether we really
259                  want to follow it.  */
260               if (descend)
261                 {
262                   if (!descend_redirect_p (redirected, url, depth,
263                                            start_url_parsed, blacklist))
264                     descend = 0;
265                   else
266                     /* Make sure that the old pre-redirect form gets
267                        blacklisted. */
268                     string_set_add (blacklist, url);
269                 }
270
271               xfree (url);
272               url = redirected;
273             }
274         }
275
276       if (descend
277           && depth >= opt.reclevel && opt.reclevel != INFINITE_RECURSION)
278         {
279           if (opt.page_requisites
280               && (depth == opt.reclevel || depth == opt.reclevel + 1))
281             {
282               /* When -p is specified, we are allowed to exceed the
283                  maximum depth, but only for the "inline" links,
284                  i.e. those that are needed to display the page.
285                  Originally this could exceed the depth at most by
286                  one, but we allow one more level so that the leaf
287                  pages that contain frames can be loaded
288                  correctly.  */
289               dash_p_leaf_HTML = TRUE;
290             }
291           else
292             {
293               /* Either -p wasn't specified or it was and we've
294                  already spent the two extra (pseudo-)levels that it
295                  affords us, so we need to bail out. */
296               DEBUGP (("Not descending further; at depth %d, max. %d.\n",
297                        depth, opt.reclevel));
298               descend = 0;
299             }
300         }
301
302       /* If the downloaded document was HTML, parse it and enqueue the
303          links it contains. */
304
305       if (descend)
306         {
307           int meta_disallow_follow = 0;
308           struct urlpos *children
309             = get_urls_html (file, url, &meta_disallow_follow);
310
311           if (opt.use_robots && meta_disallow_follow)
312             {
313               free_urlpos (children);
314               children = NULL;
315             }
316
317           if (children)
318             {
319               struct urlpos *child = children;
320               struct url *url_parsed = url_parsed = url_parse (url, NULL);
321               assert (url_parsed != NULL);
322
323               for (; child; child = child->next)
324                 {
325                   if (child->ignore_when_downloading)
326                     continue;
327                   if (dash_p_leaf_HTML && !child->link_inline_p)
328                     continue;
329                   if (download_child_p (child, url_parsed, depth, start_url_parsed,
330                                         blacklist))
331                     {
332                       url_enqueue (queue, xstrdup (child->url->url),
333                                    xstrdup (url), depth + 1);
334                       /* We blacklist the URL we have enqueued, because we
335                          don't want to enqueue (and hence download) the
336                          same URL twice.  */
337                       string_set_add (blacklist, child->url->url);
338                     }
339                 }
340
341               url_free (url_parsed);
342               free_urlpos (children);
343             }
344         }
345
346       if (opt.delete_after || (file && !acceptable (file)))
347         {
348           /* Either --delete-after was specified, or we loaded this
349              otherwise rejected (e.g. by -R) HTML file just so we
350              could harvest its hyperlinks -- in either case, delete
351              the local file. */
352           DEBUGP (("Removing file due to %s in recursive_retrieve():\n",
353                    opt.delete_after ? "--delete-after" :
354                    "recursive rejection criteria"));
355           logprintf (LOG_VERBOSE,
356                      (opt.delete_after
357                       ? _("Removing %s.\n")
358                       : _("Removing %s since it should be rejected.\n")),
359                      file);
360           if (unlink (file))
361             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
362           register_delete_file (file);
363         }
364
365       xfree (url);
366       FREE_MAYBE (referer);
367       FREE_MAYBE (file);
368     }
369
370   /* If anything is left of the queue due to a premature exit, free it
371      now.  */
372   {
373     char *d1, *d2;
374     int d3;
375     while (url_dequeue (queue, (const char **)&d1, (const char **)&d2, &d3))
376       {
377         xfree (d1);
378         FREE_MAYBE (d2);
379       }
380   }
381   url_queue_delete (queue);
382
383   if (start_url_parsed)
384     url_free (start_url_parsed);
385   string_set_free (blacklist);
386
387   if (downloaded_exceeds_quota ())
388     return QUOTEXC;
389   else if (status == FWRITEERR)
390     return FWRITEERR;
391   else
392     return RETROK;
393 }
394
395 /* Based on the context provided by retrieve_tree, decide whether a
396    URL is to be descended to.  This is only ever called from
397    retrieve_tree, but is in a separate function for clarity.
398
399    The most expensive checks (such as those for robots) are memoized
400    by storing these URLs to BLACKLIST.  This may or may not help.  It
401    will help if those URLs are encountered many times.  */
402
403 static int
404 download_child_p (const struct urlpos *upos, struct url *parent, int depth,
405                   struct url *start_url_parsed, struct hash_table *blacklist)
406 {
407   struct url *u = upos->url;
408   const char *url = u->url;
409
410   DEBUGP (("Deciding whether to enqueue \"%s\".\n", url));
411
412   if (string_set_contains (blacklist, url))
413     {
414       DEBUGP (("Already on the black list.\n"));
415       goto out;
416     }
417
418   /* Several things to check for:
419      1. if scheme is not http, and we don't load it
420      2. check for relative links (if relative_only is set)
421      3. check for domain
422      4. check for no-parent
423      5. check for excludes && includes
424      6. check for suffix
425      7. check for same host (if spanhost is unset), with possible
426      gethostbyname baggage
427      8. check for robots.txt
428
429      Addendum: If the URL is FTP, and it is to be loaded, only the
430      domain and suffix settings are "stronger".
431
432      Note that .html files will get loaded regardless of suffix rules
433      (but that is remedied later with unlink) unless the depth equals
434      the maximum depth.
435
436      More time- and memory- consuming tests should be put later on
437      the list.  */
438
439   /* 1. Schemes other than HTTP are normally not recursed into. */
440   if (u->scheme != SCHEME_HTTP
441 #ifdef HAVE_SSL
442                 && u->scheme != SCHEME_HTTPS
443 #endif
444       && !(u->scheme == SCHEME_FTP && opt.follow_ftp))
445     {
446       DEBUGP (("Not following non-HTTP schemes.\n"));
447       goto out;
448     }
449
450   /* 2. If it is an absolute link and they are not followed, throw it
451      out.  */
452   if (u->scheme == SCHEME_HTTP
453 #ifdef HAVE_SSL
454                   || u->scheme == SCHEME_HTTPS
455 #endif
456          )
457     if (opt.relative_only && !upos->link_relative_p)
458       {
459         DEBUGP (("It doesn't really look like a relative link.\n"));
460         goto out;
461       }
462
463   /* 3. If its domain is not to be accepted/looked-up, chuck it
464      out.  */
465   if (!accept_domain (u))
466     {
467       DEBUGP (("The domain was not accepted.\n"));
468       goto out;
469     }
470
471   /* 4. Check for parent directory.
472
473      If we descended to a different host or changed the scheme, ignore
474      opt.no_parent.  Also ignore it for documents needed to display
475      the parent page when in -p mode.  */
476   if (opt.no_parent
477       && u->scheme == start_url_parsed->scheme
478       && 0 == strcasecmp (u->host, start_url_parsed->host)
479       && u->port == start_url_parsed->port
480       && !(opt.page_requisites && upos->link_inline_p))
481     {
482       if (!frontcmp (start_url_parsed->dir, u->dir))
483         {
484           DEBUGP (("Going to \"%s\" would escape \"%s\" with no_parent on.\n",
485                    u->dir, start_url_parsed->dir));
486           goto out;
487         }
488     }
489
490   /* 5. If the file does not match the acceptance list, or is on the
491      rejection list, chuck it out.  The same goes for the directory
492      exclusion and inclusion lists.  */
493   if (opt.includes || opt.excludes)
494     {
495       if (!accdir (u->dir, ALLABS))
496         {
497           DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir));
498           goto out;
499         }
500     }
501
502   /* 6. */
503   {
504     char *suf;
505     /* Check for acceptance/rejection rules.  We ignore these rules
506        for HTML documents because they might lead to other files which
507        need to be downloaded.  Of course, we don't know which
508        documents are HTML before downloading them, so we guess.
509
510        A file is subject to acceptance/rejection rules if:
511
512        * u->file is not "" (i.e. it is not a directory)
513        and either:
514          + there is no file suffix,
515          + or there is a suffix, but is not "html" or "htm",
516          + both:
517            - recursion is not infinite,
518            - and we are at its very end. */
519
520     if (u->file[0] != '\0'
521         && ((suf = suffix (url)) == NULL
522             || (0 != strcmp (suf, "html") && 0 != strcmp (suf, "htm"))
523             || (opt.reclevel != INFINITE_RECURSION && depth >= opt.reclevel)))
524       {
525         if (!acceptable (u->file))
526           {
527             DEBUGP (("%s (%s) does not match acc/rej rules.\n",
528                      url, u->file));
529             goto out;
530           }
531       }
532   }
533
534   /* 7. */
535   if (u->scheme == parent->scheme)
536     if (!opt.spanhost && 0 != strcasecmp (parent->host, u->host))
537       {
538         DEBUGP (("This is not the same hostname as the parent's (%s and %s).\n",
539                  u->host, parent->host));
540         goto out;
541       }
542
543   /* 8. */
544   if (opt.use_robots && (u->scheme == SCHEME_HTTP
545 #ifdef HAVE_SSL
546                           || u->scheme == SCHEME_HTTPS
547 #endif
548                           )
549          )
550     {
551       struct robot_specs *specs = res_get_specs (u->host, u->port);
552       if (!specs)
553         {
554           char *rfile;
555           if (res_retrieve_file (url, &rfile))
556             {
557               specs = res_parse_from_file (rfile);
558               xfree (rfile);
559             }
560           else
561             {
562               /* If we cannot get real specs, at least produce
563                  dummy ones so that we can register them and stop
564                  trying to retrieve them.  */
565               specs = res_parse ("", 0);
566             }
567           res_register_specs (u->host, u->port, specs);
568         }
569
570       /* Now that we have (or don't have) robots.txt specs, we can
571          check what they say.  */
572       if (!res_match_path (specs, u->path))
573         {
574           DEBUGP (("Not following %s because robots.txt forbids it.\n", url));
575           string_set_add (blacklist, url);
576           goto out;
577         }
578     }
579
580   /* The URL has passed all the tests.  It can be placed in the
581      download queue. */
582   DEBUGP (("Decided to load it.\n"));
583
584   return 1;
585
586  out:
587   DEBUGP (("Decided NOT to load it.\n"));
588
589   return 0;
590 }
591
592 /* This function determines whether we will consider downloading the
593    children of a URL whose download resulted in a redirection,
594    possibly to another host, etc.  It is needed very rarely, and thus
595    it is merely a simple-minded wrapper around download_child_p.  */
596
597 static int
598 descend_redirect_p (const char *redirected, const char *original, int depth,
599                     struct url *start_url_parsed, struct hash_table *blacklist)
600 {
601   struct url *orig_parsed, *new_parsed;
602   struct urlpos *upos;
603   int success;
604
605   orig_parsed = url_parse (original, NULL);
606   assert (orig_parsed != NULL);
607
608   new_parsed = url_parse (redirected, NULL);
609   assert (new_parsed != NULL);
610
611   upos = xmalloc (sizeof (struct urlpos));
612   memset (upos, 0, sizeof (*upos));
613   upos->url = new_parsed;
614
615   success = download_child_p (upos, orig_parsed, depth,
616                               start_url_parsed, blacklist);
617
618   url_free (orig_parsed);
619   url_free (new_parsed);
620   xfree (upos);
621
622   if (!success)
623     DEBUGP (("Redirection \"%s\" failed the test.\n", redirected));
624
625   return success;
626 }
627
628 \f
629 #define ENSURE_TABLES_EXIST do {                        \
630   if (!dl_file_url_map)                                 \
631     dl_file_url_map = make_string_hash_table (0);       \
632   if (!dl_url_file_map)                                 \
633     dl_url_file_map = make_string_hash_table (0);       \
634 } while (0)
635
636 /* Return 1 if S1 and S2 are the same, except for "/index.html".  The
637    three cases in which it returns one are (substitute any substring
638    for "foo"):
639
640    m("foo/index.html", "foo/")  ==> 1
641    m("foo/", "foo/index.html")  ==> 1
642    m("foo", "foo/index.html")   ==> 1
643    m("foo", "foo/"              ==> 1
644    m("foo", "foo")              ==> 1  */
645
646 static int
647 match_except_index (const char *s1, const char *s2)
648 {
649   int i;
650   const char *lng;
651
652   /* Skip common substring. */
653   for (i = 0; *s1 && *s2 && *s1 == *s2; s1++, s2++, i++)
654     ;
655   if (i == 0)
656     /* Strings differ at the very beginning -- bail out.  We need to
657        check this explicitly to avoid `lng - 1' reading outside the
658        array.  */
659     return 0;
660
661   if (!*s1 && !*s2)
662     /* Both strings hit EOF -- strings are equal. */
663     return 1;
664   else if (*s1 && *s2)
665     /* Strings are randomly different, e.g. "/foo/bar" and "/foo/qux". */
666     return 0;
667   else if (*s1)
668     /* S1 is the longer one. */
669     lng = s1;
670   else
671     /* S2 is the longer one. */
672     lng = s2;
673
674   /* foo            */            /* foo/           */
675   /* foo/index.html */  /* or */  /* foo/index.html */
676   /*    ^           */            /*     ^          */
677
678   if (*lng != '/')
679     /* The right-hand case. */
680     --lng;
681
682   if (*lng == '/' && *(lng + 1) == '\0')
683     /* foo  */
684     /* foo/ */
685     return 1;
686
687   return 0 == strcmp (lng, "/index.html");
688 }
689
690 static int
691 dissociate_urls_from_file_mapper (void *key, void *value, void *arg)
692 {
693   char *mapping_url = (char *)key;
694   char *mapping_file = (char *)value;
695   char *file = (char *)arg;
696
697   if (0 == strcmp (mapping_file, file))
698     {
699       hash_table_remove (dl_url_file_map, mapping_url);
700       xfree (mapping_url);
701       xfree (mapping_file);
702     }
703
704   /* Continue mapping. */
705   return 0;
706 }
707
708 /* Remove all associations from various URLs to FILE from dl_url_file_map. */
709
710 static void
711 dissociate_urls_from_file (const char *file)
712 {
713   hash_table_map (dl_url_file_map, dissociate_urls_from_file_mapper,
714                   (char *)file);
715 }
716
717 /* Register that URL has been successfully downloaded to FILE.  This
718    is used by the link conversion code to convert references to URLs
719    to references to local files.  It is also being used to check if a
720    URL has already been downloaded.  */
721
722 void
723 register_download (const char *url, const char *file)
724 {
725   char *old_file, *old_url;
726
727   ENSURE_TABLES_EXIST;
728
729   /* With some forms of retrieval, it is possible, although not likely
730      or particularly desirable.  If both are downloaded, the second
731      download will override the first one.  When that happens,
732      dissociate the old file name from the URL.  */
733
734   if (hash_table_get_pair (dl_file_url_map, file, &old_file, &old_url))
735     {
736       if (0 == strcmp (url, old_url))
737         /* We have somehow managed to download the same URL twice.
738            Nothing to do.  */
739         return;
740
741       if (match_except_index (url, old_url)
742           && !hash_table_contains (dl_url_file_map, url))
743         /* The two URLs differ only in the "index.html" ending.  For
744            example, one is "http://www.server.com/", and the other is
745            "http://www.server.com/index.html".  Don't remove the old
746            one, just add the new one as a non-canonical entry.  */
747         goto url_only;
748
749       hash_table_remove (dl_file_url_map, file);
750       xfree (old_file);
751       xfree (old_url);
752
753       /* Remove all the URLs that point to this file.  Yes, there can
754          be more than one such URL, because we store redirections as
755          multiple entries in dl_url_file_map.  For example, if URL1
756          redirects to URL2 which gets downloaded to FILE, we map both
757          URL1 and URL2 to FILE in dl_url_file_map.  (dl_file_url_map
758          only points to URL2.)  When another URL gets loaded to FILE,
759          we want both URL1 and URL2 dissociated from it.
760
761          This is a relatively expensive operation because it performs
762          a linear search of the whole hash table, but it should be
763          called very rarely, only when two URLs resolve to the same
764          file name, *and* the "<file>.1" extensions are turned off.
765          In other words, almost never.  */
766       dissociate_urls_from_file (file);
767     }
768
769   hash_table_put (dl_file_url_map, xstrdup (file), xstrdup (url));
770
771  url_only:
772   /* A URL->FILE mapping is not possible without a FILE->URL mapping.
773      If the latter were present, it should have been removed by the
774      above `if'.  So we could write:
775
776          assert (!hash_table_contains (dl_url_file_map, url));
777
778      The above is correct when running in recursive mode where the
779      same URL always resolves to the same file.  But if you do
780      something like:
781
782          wget URL URL
783
784      then the first URL will resolve to "FILE", and the other to
785      "FILE.1".  In that case, FILE.1 will not be found in
786      dl_file_url_map, but URL will still point to FILE in
787      dl_url_file_map.  */
788   if (hash_table_get_pair (dl_url_file_map, url, &old_url, &old_file))
789     {
790       hash_table_remove (dl_url_file_map, url);
791       xfree (old_url);
792       xfree (old_file);
793     }
794
795   hash_table_put (dl_url_file_map, xstrdup (url), xstrdup (file));
796 }
797
798 /* Register that FROM has been redirected to TO.  This assumes that TO
799    is successfully downloaded and already registered using
800    register_download() above.  */
801
802 void
803 register_redirection (const char *from, const char *to)
804 {
805   char *file;
806
807   ENSURE_TABLES_EXIST;
808
809   file = hash_table_get (dl_url_file_map, to);
810   assert (file != NULL);
811   if (!hash_table_contains (dl_url_file_map, from))
812     hash_table_put (dl_url_file_map, xstrdup (from), xstrdup (file));
813 }
814
815 /* Register that the file has been deleted. */
816
817 static void
818 register_delete_file (const char *file)
819 {
820   char *old_url, *old_file;
821
822   ENSURE_TABLES_EXIST;
823
824   if (!hash_table_get_pair (dl_file_url_map, file, &old_file, &old_url))
825     return;
826
827   hash_table_remove (dl_file_url_map, file);
828   xfree (old_file);
829   xfree (old_url);
830   dissociate_urls_from_file (file);
831 }
832
833 /* Register that FILE is an HTML file that has been downloaded. */
834
835 void
836 register_html (const char *url, const char *file)
837 {
838   if (!downloaded_html_set)
839     downloaded_html_set = make_string_hash_table (0);
840   else if (hash_table_contains (downloaded_html_set, file))
841     return;
842
843   /* The set and the list should use the same copy of FILE, but the
844      slist interface insists on strduping the string it gets.  Oh
845      well. */
846   string_set_add (downloaded_html_set, file);
847   downloaded_html_list = slist_prepend (downloaded_html_list, file);
848 }
849
850 /* This function is called when the retrieval is done to convert the
851    links that have been downloaded.  It has to be called at the end of
852    the retrieval, because only then does Wget know conclusively which
853    URLs have been downloaded, and which not, so it can tell which
854    direction to convert to.
855
856    The "direction" means that the URLs to the files that have been
857    downloaded get converted to the relative URL which will point to
858    that file.  And the other URLs get converted to the remote URL on
859    the server.
860
861    All the downloaded HTMLs are kept in downloaded_html_files, and
862    downloaded URLs in urls_downloaded.  All the information is
863    extracted from these two lists.  */
864
865 void
866 convert_all_links (void)
867 {
868   slist *html;
869   long msecs;
870   int file_count = 0;
871
872   struct wget_timer *timer = wtimer_new ();
873
874   /* Destructively reverse downloaded_html_files to get it in the right order.
875      recursive_retrieve() used slist_prepend() consistently.  */
876   downloaded_html_list = slist_nreverse (downloaded_html_list);
877
878   for (html = downloaded_html_list; html; html = html->next)
879     {
880       struct urlpos *urls, *cur_url;
881       char *url;
882       char *file = html->string;
883
884       /* Determine the URL of the HTML file.  get_urls_html will need
885          it.  */
886       url = hash_table_get (dl_file_url_map, file);
887       if (!url)
888         {
889           DEBUGP (("Apparently %s has been removed.\n", file));
890           continue;
891         }
892
893       DEBUGP (("Scanning %s (from %s)\n", file, url));
894
895       /* Parse the HTML file...  */
896       urls = get_urls_html (file, url, NULL);
897
898       /* We don't respect meta_disallow_follow here because, even if
899          the file is not followed, we might still want to convert the
900          links that have been followed from other files.  */
901
902       for (cur_url = urls; cur_url; cur_url = cur_url->next)
903         {
904           char *local_name;
905           struct url *u = cur_url->url;
906
907           if (cur_url->link_base_p)
908             {
909               /* Base references have been resolved by our parser, so
910                  we turn the base URL into an empty string.  (Perhaps
911                  we should remove the tag entirely?)  */
912               cur_url->convert = CO_NULLIFY_BASE;
913               continue;
914             }
915
916           /* We decide the direction of conversion according to whether
917              a URL was downloaded.  Downloaded URLs will be converted
918              ABS2REL, whereas non-downloaded will be converted REL2ABS.  */
919           local_name = hash_table_get (dl_url_file_map, u->url);
920
921           /* Decide on the conversion type.  */
922           if (local_name)
923             {
924               /* We've downloaded this URL.  Convert it to relative
925                  form.  We do this even if the URL already is in
926                  relative form, because our directory structure may
927                  not be identical to that on the server (think `-nd',
928                  `--cut-dirs', etc.)  */
929               cur_url->convert = CO_CONVERT_TO_RELATIVE;
930               cur_url->local_name = xstrdup (local_name);
931               DEBUGP (("will convert url %s to local %s\n", u->url, local_name));
932             }
933           else
934             {
935               /* We haven't downloaded this URL.  If it's not already
936                  complete (including a full host name), convert it to
937                  that form, so it can be reached while browsing this
938                  HTML locally.  */
939               if (!cur_url->link_complete_p)
940                 cur_url->convert = CO_CONVERT_TO_COMPLETE;
941               cur_url->local_name = NULL;
942               DEBUGP (("will convert url %s to complete\n", u->url));
943             }
944         }
945
946       /* Convert the links in the file.  */
947       convert_links (file, urls);
948       ++file_count;
949
950       /* Free the data.  */
951       free_urlpos (urls);
952     }
953
954   msecs = wtimer_elapsed (timer);
955   wtimer_delete (timer);
956   logprintf (LOG_VERBOSE, _("Converted %d files in %.2f seconds.\n"),
957              file_count, (double)msecs / 1000);
958 }
959
960 /* Cleanup the data structures associated with recursive retrieving
961    (the variables above).  */
962 void
963 recursive_cleanup (void)
964 {
965   if (dl_file_url_map)
966     {
967       free_keys_and_values (dl_file_url_map);
968       hash_table_destroy (dl_file_url_map);
969       dl_file_url_map = NULL;
970     }
971   if (dl_url_file_map)
972     {
973       free_keys_and_values (dl_url_file_map);
974       hash_table_destroy (dl_url_file_map);
975       dl_url_file_map = NULL;
976     }
977   if (downloaded_html_set)
978     string_set_free (downloaded_html_set);
979   slist_free (downloaded_html_list);
980   downloaded_html_list = NULL;
981 }