]> sjero.net Git - wget/blob - src/recur.c
[svn] A lot of host name changes.
[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.  This list should only be traversed
58    in order.  If you need to check whether a file has been downloaded,
59    use a hash table, e.g. dl_file_url_map.  */
60 static slist *downloaded_html_files;
61 \f
62 /* Functions for maintaining the URL queue.  */
63
64 struct queue_element {
65   const char *url;
66   const char *referer;
67   int depth;
68   struct queue_element *next;
69 };
70
71 struct url_queue {
72   struct queue_element *head;
73   struct queue_element *tail;
74   int count, maxcount;
75 };
76
77 /* Create a URL queue. */
78
79 static struct url_queue *
80 url_queue_new (void)
81 {
82   struct url_queue *queue = xmalloc (sizeof (*queue));
83   memset (queue, '\0', sizeof (*queue));
84   return queue;
85 }
86
87 /* Delete a URL queue. */
88
89 static void
90 url_queue_delete (struct url_queue *queue)
91 {
92   xfree (queue);
93 }
94
95 /* Enqueue a URL in the queue.  The queue is FIFO: the items will be
96    retrieved ("dequeued") from the queue in the order they were placed
97    into it.  */
98
99 static void
100 url_enqueue (struct url_queue *queue,
101              const char *url, const char *referer, int depth)
102 {
103   struct queue_element *qel = xmalloc (sizeof (*qel));
104   qel->url = url;
105   qel->referer = referer;
106   qel->depth = depth;
107   qel->next = NULL;
108
109   ++queue->count;
110   if (queue->count > queue->maxcount)
111     queue->maxcount = queue->count;
112
113   DEBUGP (("Enqueuing %s at depth %d\n", url, depth));
114   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
115
116   if (queue->tail)
117     queue->tail->next = qel;
118   queue->tail = qel;
119
120   if (!queue->head)
121     queue->head = queue->tail;
122 }
123
124 /* Take a URL out of the queue.  Return 1 if this operation succeeded,
125    or 0 if the queue is empty.  */
126
127 static int
128 url_dequeue (struct url_queue *queue,
129              const char **url, const char **referer, int *depth)
130 {
131   struct queue_element *qel = queue->head;
132
133   if (!qel)
134     return 0;
135
136   queue->head = queue->head->next;
137   if (!queue->head)
138     queue->tail = NULL;
139
140   *url = qel->url;
141   *referer = qel->referer;
142   *depth = qel->depth;
143
144   --queue->count;
145
146   DEBUGP (("Dequeuing %s at depth %d\n", qel->url, qel->depth));
147   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
148
149   xfree (qel);
150   return 1;
151 }
152 \f
153 static int descend_url_p PARAMS ((const struct urlpos *, struct url *, int,
154                                   struct url *, struct hash_table *));
155 static int descend_redirect_p PARAMS ((const char *, const char *, int,
156                                        struct url *, struct hash_table *));
157
158
159 /* Retrieve a part of the web beginning with START_URL.  This used to
160    be called "recursive retrieval", because the old function was
161    recursive and implemented depth-first search.  retrieve_tree on the
162    other hand implements breadth-search traversal of the tree, which
163    results in much nicer ordering of downloads.
164
165    The algorithm this function uses is simple:
166
167    1. put START_URL in the queue.
168    2. while there are URLs in the queue:
169
170      3. get next URL from the queue.
171      4. download it.
172      5. if the URL is HTML and its depth does not exceed maximum depth,
173         get the list of URLs embedded therein.
174      6. for each of those URLs do the following:
175
176        7. if the URL is not one of those downloaded before, and if it
177           satisfies the criteria specified by the various command-line
178           options, add it to the queue. */
179
180 uerr_t
181 retrieve_tree (const char *start_url)
182 {
183   uerr_t status = RETROK;
184
185   /* The queue of URLs we need to load. */
186   struct url_queue *queue = url_queue_new ();
187
188   /* The URLs we do not wish to enqueue, because they are already in
189      the queue, but haven't been downloaded yet.  */
190   struct hash_table *blacklist = make_string_hash_table (0);
191
192   /* We'll need various components of this, so better get it over with
193      now. */
194   struct url *start_url_parsed = url_parse (start_url, NULL);
195
196   url_enqueue (queue, xstrdup (start_url), NULL, 0);
197   string_set_add (blacklist, start_url);
198
199   while (1)
200     {
201       int descend = 0;
202       char *url, *referer, *file = NULL;
203       int depth;
204       boolean dash_p_leaf_HTML = FALSE;
205
206       if (downloaded_exceeds_quota ())
207         break;
208
209       if (status == FWRITEERR)
210         break;
211
212       /* Get the next URL from the queue. */
213
214       if (!url_dequeue (queue,
215                         (const char **)&url, (const char **)&referer,
216                         &depth))
217         break;
218
219       /* And download it. */
220
221       {
222         int dt = 0;
223         char *redirected = NULL;
224         int oldrec = opt.recursive;
225
226         opt.recursive = 0;
227         status = retrieve_url (url, &file, &redirected, NULL, &dt);
228         opt.recursive = oldrec;
229
230         if (file && status == RETROK
231             && (dt & RETROKF) && (dt & TEXTHTML))
232           descend = 1;
233
234         if (redirected)
235           {
236             /* We have been redirected, possibly to another host, or
237                different path, or wherever.  Check whether we really
238                want to follow it.  */
239             if (descend)
240               {
241                 if (!descend_redirect_p (redirected, url, depth,
242                                          start_url_parsed, blacklist))
243                   descend = 0;
244                 else
245                   /* Make sure that the old pre-redirect form gets
246                      blacklisted. */
247                   string_set_add (blacklist, url);
248               }
249
250             xfree (url);
251             url = redirected;
252           }
253       }
254
255       if (descend
256           && depth >= opt.reclevel && opt.reclevel != INFINITE_RECURSION)
257         {
258           if (opt.page_requisites && depth == opt.reclevel)
259             /* When -p is specified, we can do one more partial
260                recursion from the "leaf nodes" on the HTML document
261                tree.  The recursion is partial in that we won't
262                traverse any <A> or <AREA> tags, nor any <LINK> tags
263                except for <LINK REL="stylesheet">. */
264             dash_p_leaf_HTML = TRUE;
265           else
266             {
267               /* Either -p wasn't specified or it was and we've
268                  already gone the one extra (pseudo-)level that it
269                  affords us, so we need to bail out. */
270               DEBUGP (("Not descending further; at depth %d, max. %d.\n",
271                        depth, opt.reclevel));
272               descend = 0;
273             }
274         }
275
276       /* If the downloaded document was HTML, parse it and enqueue the
277          links it contains. */
278
279       if (descend)
280         {
281           int meta_disallow_follow = 0;
282           struct urlpos *children = get_urls_html (file, url, dash_p_leaf_HTML,
283                                                    &meta_disallow_follow);
284
285           if (opt.use_robots && meta_disallow_follow)
286             {
287               free_urlpos (children);
288               children = NULL;
289             }
290
291           if (children)
292             {
293               struct urlpos *child = children;
294               struct url *url_parsed = url_parsed = url_parse (url, NULL);
295               assert (url_parsed != NULL);
296
297               for (; child; child = child->next)
298                 {
299                   if (child->ignore_when_downloading)
300                     continue;
301                   if (descend_url_p (child, url_parsed, depth, start_url_parsed,
302                                      blacklist))
303                     {
304                       url_enqueue (queue, xstrdup (child->url->url),
305                                    xstrdup (url), depth + 1);
306                       /* We blacklist the URL we have enqueued, because we
307                          don't want to enqueue (and hence download) the
308                          same URL twice.  */
309                       string_set_add (blacklist, child->url->url);
310                     }
311                 }
312
313               url_free (url_parsed);
314               free_urlpos (children);
315             }
316         }
317
318       if (opt.delete_after || (file && !acceptable (file)))
319         {
320           /* Either --delete-after was specified, or we loaded this
321              otherwise rejected (e.g. by -R) HTML file just so we
322              could harvest its hyperlinks -- in either case, delete
323              the local file. */
324           DEBUGP (("Removing file due to %s in recursive_retrieve():\n",
325                    opt.delete_after ? "--delete-after" :
326                    "recursive rejection criteria"));
327           logprintf (LOG_VERBOSE,
328                      (opt.delete_after
329                       ? _("Removing %s.\n")
330                       : _("Removing %s since it should be rejected.\n")),
331                      file);
332           if (unlink (file))
333             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
334         }
335
336       xfree (url);
337       FREE_MAYBE (referer);
338       FREE_MAYBE (file);
339     }
340
341   /* If anything is left of the queue due to a premature exit, free it
342      now.  */
343   {
344     char *d1, *d2;
345     int d3;
346     while (url_dequeue (queue, (const char **)&d1, (const char **)&d2, &d3))
347       {
348         xfree (d1);
349         FREE_MAYBE (d2);
350       }
351   }
352   url_queue_delete (queue);
353
354   if (start_url_parsed)
355     url_free (start_url_parsed);
356   string_set_free (blacklist);
357
358   if (downloaded_exceeds_quota ())
359     return QUOTEXC;
360   else if (status == FWRITEERR)
361     return FWRITEERR;
362   else
363     return RETROK;
364 }
365
366 /* Based on the context provided by retrieve_tree, decide whether a
367    URL is to be descended to.  This is only ever called from
368    retrieve_tree, but is in a separate function for clarity.
369
370    The most expensive checks (such as those for robots) are memoized
371    by storing these URLs to BLACKLIST.  This may or may not help.  It
372    will help if those URLs are encountered many times.  */
373
374 static int
375 descend_url_p (const struct urlpos *upos, struct url *parent, int depth,
376                struct url *start_url_parsed, struct hash_table *blacklist)
377 {
378   struct url *u = upos->url;
379   const char *url = u->url;
380
381   DEBUGP (("Deciding whether to enqueue \"%s\".\n", url));
382
383   if (string_set_contains (blacklist, url))
384     {
385       DEBUGP (("Already on the black list.\n"));
386       goto out;
387     }
388
389   /* Several things to check for:
390      1. if scheme is not http, and we don't load it
391      2. check for relative links (if relative_only is set)
392      3. check for domain
393      4. check for no-parent
394      5. check for excludes && includes
395      6. check for suffix
396      7. check for same host (if spanhost is unset), with possible
397      gethostbyname baggage
398      8. check for robots.txt
399
400      Addendum: If the URL is FTP, and it is to be loaded, only the
401      domain and suffix settings are "stronger".
402
403      Note that .html files will get loaded regardless of suffix rules
404      (but that is remedied later with unlink) unless the depth equals
405      the maximum depth.
406
407      More time- and memory- consuming tests should be put later on
408      the list.  */
409
410   /* 1. Schemes other than HTTP are normally not recursed into. */
411   if (u->scheme != SCHEME_HTTP
412       && !(u->scheme == SCHEME_FTP && opt.follow_ftp))
413     {
414       DEBUGP (("Not following non-HTTP schemes.\n"));
415       goto out;
416     }
417
418   /* 2. If it is an absolute link and they are not followed, throw it
419      out.  */
420   if (u->scheme == SCHEME_HTTP)
421     if (opt.relative_only && !upos->link_relative_p)
422       {
423         DEBUGP (("It doesn't really look like a relative link.\n"));
424         goto out;
425       }
426
427   /* 3. If its domain is not to be accepted/looked-up, chuck it
428      out.  */
429   if (!accept_domain (u))
430     {
431       DEBUGP (("The domain was not accepted.\n"));
432       goto out;
433     }
434
435   /* 4. Check for parent directory.
436
437      If we descended to a different host or changed the scheme, ignore
438      opt.no_parent.  Also ignore it for -p leaf retrievals.  */
439   if (opt.no_parent
440       && u->scheme == parent->scheme
441       && 0 == strcasecmp (u->host, parent->host)
442       && u->port == parent->port)
443     {
444       if (!frontcmp (parent->dir, u->dir))
445         {
446           DEBUGP (("Trying to escape the root directory with no_parent in effect.\n"));
447           goto out;
448         }
449     }
450
451   /* 5. If the file does not match the acceptance list, or is on the
452      rejection list, chuck it out.  The same goes for the directory
453      exclusion and inclusion lists.  */
454   if (opt.includes || opt.excludes)
455     {
456       if (!accdir (u->dir, ALLABS))
457         {
458           DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir));
459           goto out;
460         }
461     }
462
463   /* 6. */
464   {
465     char *suf;
466     /* Check for acceptance/rejection rules.  We ignore these rules
467        for HTML documents because they might lead to other files which
468        need to be downloaded.  Of course, we don't know which
469        documents are HTML before downloading them, so we guess.
470
471        A file is subject to acceptance/rejection rules if:
472
473        * u->file is not "" (i.e. it is not a directory)
474        and either:
475          + there is no file suffix,
476          + or there is a suffix, but is not "html" or "htm",
477          + both:
478            - recursion is not infinite,
479            - and we are at its very end. */
480
481     if (u->file[0] != '\0'
482         && ((suf = suffix (url)) == NULL
483             || (0 != strcmp (suf, "html") && 0 != strcmp (suf, "htm"))
484             || (opt.reclevel == INFINITE_RECURSION && depth >= opt.reclevel)))
485       {
486         if (!acceptable (u->file))
487           {
488             DEBUGP (("%s (%s) does not match acc/rej rules.\n",
489                      url, u->file));
490             goto out;
491           }
492       }
493   }
494
495   /* 7. */
496   if (u->scheme == parent->scheme)
497     if (!opt.spanhost && 0 != strcasecmp (parent->host, u->host))
498       {
499         DEBUGP (("This is not the same hostname as the parent's (%s and %s).\n",
500                  u->host, parent->host));
501         goto out;
502       }
503
504   /* 8. */
505   if (opt.use_robots && u->scheme == SCHEME_HTTP)
506     {
507       struct robot_specs *specs = res_get_specs (u->host, u->port);
508       if (!specs)
509         {
510           char *rfile;
511           if (res_retrieve_file (url, &rfile))
512             {
513               specs = res_parse_from_file (rfile);
514               xfree (rfile);
515             }
516           else
517             {
518               /* If we cannot get real specs, at least produce
519                  dummy ones so that we can register them and stop
520                  trying to retrieve them.  */
521               specs = res_parse ("", 0);
522             }
523           res_register_specs (u->host, u->port, specs);
524         }
525
526       /* Now that we have (or don't have) robots.txt specs, we can
527          check what they say.  */
528       if (!res_match_path (specs, u->path))
529         {
530           DEBUGP (("Not following %s because robots.txt forbids it.\n", url));
531           string_set_add (blacklist, url);
532           goto out;
533         }
534     }
535
536   /* The URL has passed all the tests.  It can be placed in the
537      download queue. */
538   DEBUGP (("Decided to load it.\n"));
539
540   return 1;
541
542  out:
543   DEBUGP (("Decided NOT to load it.\n"));
544
545   return 0;
546 }
547
548 /* This function determines whether we should descend the children of
549    the URL whose download resulted in a redirection, possibly to
550    another host, etc.  It is needed very rarely, and thus it is merely
551    a simple-minded wrapper around descend_url_p.  */
552
553 static int
554 descend_redirect_p (const char *redirected, const char *original, int depth,
555                     struct url *start_url_parsed, struct hash_table *blacklist)
556 {
557   struct url *orig_parsed, *new_parsed;
558   struct urlpos *upos;
559   int success;
560
561   orig_parsed = url_parse (original, NULL);
562   assert (orig_parsed != NULL);
563
564   new_parsed = url_parse (redirected, NULL);
565   assert (new_parsed != NULL);
566
567   upos = xmalloc (sizeof (struct urlpos));
568   memset (upos, 0, sizeof (*upos));
569   upos->url = new_parsed;
570
571   success = descend_url_p (upos, orig_parsed, depth,
572                            start_url_parsed, blacklist);
573
574   url_free (orig_parsed);
575   url_free (new_parsed);
576   xfree (upos);
577
578   if (!success)
579     DEBUGP (("Redirection \"%s\" failed the test.\n", redirected));
580
581   return success;
582 }
583
584 \f
585 /* Register that URL has been successfully downloaded to FILE. */
586
587 void
588 register_download (const char *url, const char *file)
589 {
590   if (!opt.convert_links)
591     return;
592   if (!dl_file_url_map)
593     dl_file_url_map = make_string_hash_table (0);
594   if (!dl_url_file_map)
595     dl_url_file_map = make_string_hash_table (0);
596
597   if (!hash_table_contains (dl_file_url_map, file))
598     hash_table_put (dl_file_url_map, xstrdup (file), xstrdup (url));
599   if (!hash_table_contains (dl_url_file_map, url))
600     hash_table_put (dl_url_file_map, xstrdup (url), xstrdup (file));
601 }
602
603 /* Register that FROM has been redirected to TO.  This assumes that TO
604    is successfully downloaded and already registered using
605    register_download() above.  */
606
607 void
608 register_redirection (const char *from, const char *to)
609 {
610   char *file;
611
612   if (!opt.convert_links)
613     return;
614
615   file = hash_table_get (dl_url_file_map, to);
616   assert (file != NULL);
617   if (!hash_table_contains (dl_url_file_map, from))
618     hash_table_put (dl_url_file_map, xstrdup (from), xstrdup (file));
619 }
620
621 /* Register that URL corresponds to the HTML file FILE. */
622
623 void
624 register_html (const char *url, const char *file)
625 {
626   if (!opt.convert_links)
627     return;
628   downloaded_html_files = slist_prepend (downloaded_html_files, file);
629 }
630
631 /* This function is called when the retrieval is done to convert the
632    links that have been downloaded.  It has to be called at the end of
633    the retrieval, because only then does Wget know conclusively which
634    URLs have been downloaded, and which not, so it can tell which
635    direction to convert to.
636
637    The "direction" means that the URLs to the files that have been
638    downloaded get converted to the relative URL which will point to
639    that file.  And the other URLs get converted to the remote URL on
640    the server.
641
642    All the downloaded HTMLs are kept in downloaded_html_files, and
643    downloaded URLs in urls_downloaded.  All the information is
644    extracted from these two lists.  */
645
646 void
647 convert_all_links (void)
648 {
649   slist *html;
650   struct wget_timer *timer;
651   long msecs;
652   int file_count = 0;
653
654   timer = wtimer_new ();
655
656   /* Destructively reverse downloaded_html_files to get it in the right order.
657      recursive_retrieve() used slist_prepend() consistently.  */
658   downloaded_html_files = slist_nreverse (downloaded_html_files);
659
660   for (html = downloaded_html_files; html; html = html->next)
661     {
662       struct urlpos *urls, *cur_url;
663       char *url;
664
665       DEBUGP (("Rescanning %s\n", html->string));
666
667       /* Determine the URL of the HTML file.  get_urls_html will need
668          it.  */
669       url = hash_table_get (dl_file_url_map, html->string);
670       if (url)
671         DEBUGP (("It should correspond to %s.\n", url));
672       else
673         DEBUGP (("I cannot find the corresponding URL.\n"));
674
675       /* Parse the HTML file...  */
676       urls = get_urls_html (html->string, url, FALSE, NULL);
677
678       /* We don't respect meta_disallow_follow here because, even if
679          the file is not followed, we might still want to convert the
680          links that have been followed from other files.  */
681
682       for (cur_url = urls; cur_url; cur_url = cur_url->next)
683         {
684           char *local_name;
685           struct url *u = cur_url->url;
686
687           if (cur_url->link_base_p)
688             {
689               /* Base references have been resolved by our parser, so
690                  we turn the base URL into an empty string.  (Perhaps
691                  we should remove the tag entirely?)  */
692               cur_url->convert = CO_NULLIFY_BASE;
693               continue;
694             }
695
696           /* We decide the direction of conversion according to whether
697              a URL was downloaded.  Downloaded URLs will be converted
698              ABS2REL, whereas non-downloaded will be converted REL2ABS.  */
699           local_name = hash_table_get (dl_url_file_map, u->url);
700           if (local_name)
701             DEBUGP (("%s marked for conversion, local %s\n",
702                      u->url, local_name));
703
704           /* Decide on the conversion type.  */
705           if (local_name)
706             {
707               /* We've downloaded this URL.  Convert it to relative
708                  form.  We do this even if the URL already is in
709                  relative form, because our directory structure may
710                  not be identical to that on the server (think `-nd',
711                  `--cut-dirs', etc.)  */
712               cur_url->convert = CO_CONVERT_TO_RELATIVE;
713               cur_url->local_name = xstrdup (local_name);
714             }
715           else
716             {
717               /* We haven't downloaded this URL.  If it's not already
718                  complete (including a full host name), convert it to
719                  that form, so it can be reached while browsing this
720                  HTML locally.  */
721               if (!cur_url->link_complete_p)
722                 cur_url->convert = CO_CONVERT_TO_COMPLETE;
723               cur_url->local_name = NULL;
724             }
725         }
726
727       /* Convert the links in the file.  */
728       convert_links (html->string, urls);
729       ++file_count;
730
731       /* Free the data.  */
732       free_urlpos (urls);
733     }
734
735   msecs = wtimer_elapsed (timer);
736   wtimer_delete (timer);
737   logprintf (LOG_VERBOSE, _("Converted %d files in %.2f seconds.\n"),
738              file_count, (double)msecs / 1000);
739 }
740
741 /* Cleanup the data structures associated with recursive retrieving
742    (the variables above).  */
743 void
744 recursive_cleanup (void)
745 {
746   if (dl_file_url_map)
747     {
748       free_keys_and_values (dl_file_url_map);
749       hash_table_destroy (dl_file_url_map);
750       dl_file_url_map = NULL;
751     }
752   if (dl_url_file_map)
753     {
754       free_keys_and_values (dl_url_file_map);
755       hash_table_destroy (dl_url_file_map);
756       dl_url_file_map = NULL;
757     }
758   slist_free (downloaded_html_files);
759   downloaded_html_files = NULL;
760 }