]> sjero.net Git - wget/blob - src/recur.c
[svn] descend_url_p: When resolving no_parent, compare with the start url,
[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 == start_url_parsed->scheme
441       && 0 == strcasecmp (u->host, start_url_parsed->host)
442       && u->port == start_url_parsed->port)
443     {
444       if (!frontcmp (start_url_parsed->dir, u->dir))
445         {
446           DEBUGP (("Going to \"%s\" would escape \"%s\" with no_parent on.\n",
447                    u->dir, start_url_parsed->dir));
448           goto out;
449         }
450     }
451
452   /* 5. If the file does not match the acceptance list, or is on the
453      rejection list, chuck it out.  The same goes for the directory
454      exclusion and inclusion lists.  */
455   if (opt.includes || opt.excludes)
456     {
457       if (!accdir (u->dir, ALLABS))
458         {
459           DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir));
460           goto out;
461         }
462     }
463
464   /* 6. */
465   {
466     char *suf;
467     /* Check for acceptance/rejection rules.  We ignore these rules
468        for HTML documents because they might lead to other files which
469        need to be downloaded.  Of course, we don't know which
470        documents are HTML before downloading them, so we guess.
471
472        A file is subject to acceptance/rejection rules if:
473
474        * u->file is not "" (i.e. it is not a directory)
475        and either:
476          + there is no file suffix,
477          + or there is a suffix, but is not "html" or "htm",
478          + both:
479            - recursion is not infinite,
480            - and we are at its very end. */
481
482     if (u->file[0] != '\0'
483         && ((suf = suffix (url)) == NULL
484             || (0 != strcmp (suf, "html") && 0 != strcmp (suf, "htm"))
485             || (opt.reclevel == INFINITE_RECURSION && depth >= opt.reclevel)))
486       {
487         if (!acceptable (u->file))
488           {
489             DEBUGP (("%s (%s) does not match acc/rej rules.\n",
490                      url, u->file));
491             goto out;
492           }
493       }
494   }
495
496   /* 7. */
497   if (u->scheme == parent->scheme)
498     if (!opt.spanhost && 0 != strcasecmp (parent->host, u->host))
499       {
500         DEBUGP (("This is not the same hostname as the parent's (%s and %s).\n",
501                  u->host, parent->host));
502         goto out;
503       }
504
505   /* 8. */
506   if (opt.use_robots && u->scheme == SCHEME_HTTP)
507     {
508       struct robot_specs *specs = res_get_specs (u->host, u->port);
509       if (!specs)
510         {
511           char *rfile;
512           if (res_retrieve_file (url, &rfile))
513             {
514               specs = res_parse_from_file (rfile);
515               xfree (rfile);
516             }
517           else
518             {
519               /* If we cannot get real specs, at least produce
520                  dummy ones so that we can register them and stop
521                  trying to retrieve them.  */
522               specs = res_parse ("", 0);
523             }
524           res_register_specs (u->host, u->port, specs);
525         }
526
527       /* Now that we have (or don't have) robots.txt specs, we can
528          check what they say.  */
529       if (!res_match_path (specs, u->path))
530         {
531           DEBUGP (("Not following %s because robots.txt forbids it.\n", url));
532           string_set_add (blacklist, url);
533           goto out;
534         }
535     }
536
537   /* The URL has passed all the tests.  It can be placed in the
538      download queue. */
539   DEBUGP (("Decided to load it.\n"));
540
541   return 1;
542
543  out:
544   DEBUGP (("Decided NOT to load it.\n"));
545
546   return 0;
547 }
548
549 /* This function determines whether we should descend the children of
550    the URL whose download resulted in a redirection, possibly to
551    another host, etc.  It is needed very rarely, and thus it is merely
552    a simple-minded wrapper around descend_url_p.  */
553
554 static int
555 descend_redirect_p (const char *redirected, const char *original, int depth,
556                     struct url *start_url_parsed, struct hash_table *blacklist)
557 {
558   struct url *orig_parsed, *new_parsed;
559   struct urlpos *upos;
560   int success;
561
562   orig_parsed = url_parse (original, NULL);
563   assert (orig_parsed != NULL);
564
565   new_parsed = url_parse (redirected, NULL);
566   assert (new_parsed != NULL);
567
568   upos = xmalloc (sizeof (struct urlpos));
569   memset (upos, 0, sizeof (*upos));
570   upos->url = new_parsed;
571
572   success = descend_url_p (upos, orig_parsed, depth,
573                            start_url_parsed, blacklist);
574
575   url_free (orig_parsed);
576   url_free (new_parsed);
577   xfree (upos);
578
579   if (!success)
580     DEBUGP (("Redirection \"%s\" failed the test.\n", redirected));
581
582   return success;
583 }
584
585 \f
586 /* Register that URL has been successfully downloaded to FILE. */
587
588 void
589 register_download (const char *url, const char *file)
590 {
591   if (!opt.convert_links)
592     return;
593   if (!dl_file_url_map)
594     dl_file_url_map = make_string_hash_table (0);
595   if (!dl_url_file_map)
596     dl_url_file_map = make_string_hash_table (0);
597
598   if (!hash_table_contains (dl_file_url_map, file))
599     hash_table_put (dl_file_url_map, xstrdup (file), xstrdup (url));
600   if (!hash_table_contains (dl_url_file_map, url))
601     hash_table_put (dl_url_file_map, xstrdup (url), xstrdup (file));
602 }
603
604 /* Register that FROM has been redirected to TO.  This assumes that TO
605    is successfully downloaded and already registered using
606    register_download() above.  */
607
608 void
609 register_redirection (const char *from, const char *to)
610 {
611   char *file;
612
613   if (!opt.convert_links)
614     return;
615
616   file = hash_table_get (dl_url_file_map, to);
617   assert (file != NULL);
618   if (!hash_table_contains (dl_url_file_map, from))
619     hash_table_put (dl_url_file_map, xstrdup (from), xstrdup (file));
620 }
621
622 /* Register that URL corresponds to the HTML file FILE. */
623
624 void
625 register_html (const char *url, const char *file)
626 {
627   if (!opt.convert_links)
628     return;
629   downloaded_html_files = slist_prepend (downloaded_html_files, file);
630 }
631
632 /* This function is called when the retrieval is done to convert the
633    links that have been downloaded.  It has to be called at the end of
634    the retrieval, because only then does Wget know conclusively which
635    URLs have been downloaded, and which not, so it can tell which
636    direction to convert to.
637
638    The "direction" means that the URLs to the files that have been
639    downloaded get converted to the relative URL which will point to
640    that file.  And the other URLs get converted to the remote URL on
641    the server.
642
643    All the downloaded HTMLs are kept in downloaded_html_files, and
644    downloaded URLs in urls_downloaded.  All the information is
645    extracted from these two lists.  */
646
647 void
648 convert_all_links (void)
649 {
650   slist *html;
651   struct wget_timer *timer;
652   long msecs;
653   int file_count = 0;
654
655   timer = wtimer_new ();
656
657   /* Destructively reverse downloaded_html_files to get it in the right order.
658      recursive_retrieve() used slist_prepend() consistently.  */
659   downloaded_html_files = slist_nreverse (downloaded_html_files);
660
661   for (html = downloaded_html_files; html; html = html->next)
662     {
663       struct urlpos *urls, *cur_url;
664       char *url;
665
666       DEBUGP (("Rescanning %s\n", html->string));
667
668       /* Determine the URL of the HTML file.  get_urls_html will need
669          it.  */
670       url = hash_table_get (dl_file_url_map, html->string);
671       if (url)
672         DEBUGP (("It should correspond to %s.\n", url));
673       else
674         DEBUGP (("I cannot find the corresponding URL.\n"));
675
676       /* Parse the HTML file...  */
677       urls = get_urls_html (html->string, url, FALSE, NULL);
678
679       /* We don't respect meta_disallow_follow here because, even if
680          the file is not followed, we might still want to convert the
681          links that have been followed from other files.  */
682
683       for (cur_url = urls; cur_url; cur_url = cur_url->next)
684         {
685           char *local_name;
686           struct url *u = cur_url->url;
687
688           if (cur_url->link_base_p)
689             {
690               /* Base references have been resolved by our parser, so
691                  we turn the base URL into an empty string.  (Perhaps
692                  we should remove the tag entirely?)  */
693               cur_url->convert = CO_NULLIFY_BASE;
694               continue;
695             }
696
697           /* We decide the direction of conversion according to whether
698              a URL was downloaded.  Downloaded URLs will be converted
699              ABS2REL, whereas non-downloaded will be converted REL2ABS.  */
700           local_name = hash_table_get (dl_url_file_map, u->url);
701           if (local_name)
702             DEBUGP (("%s marked for conversion, local %s\n",
703                      u->url, local_name));
704
705           /* Decide on the conversion type.  */
706           if (local_name)
707             {
708               /* We've downloaded this URL.  Convert it to relative
709                  form.  We do this even if the URL already is in
710                  relative form, because our directory structure may
711                  not be identical to that on the server (think `-nd',
712                  `--cut-dirs', etc.)  */
713               cur_url->convert = CO_CONVERT_TO_RELATIVE;
714               cur_url->local_name = xstrdup (local_name);
715             }
716           else
717             {
718               /* We haven't downloaded this URL.  If it's not already
719                  complete (including a full host name), convert it to
720                  that form, so it can be reached while browsing this
721                  HTML locally.  */
722               if (!cur_url->link_complete_p)
723                 cur_url->convert = CO_CONVERT_TO_COMPLETE;
724               cur_url->local_name = NULL;
725             }
726         }
727
728       /* Convert the links in the file.  */
729       convert_links (html->string, urls);
730       ++file_count;
731
732       /* Free the data.  */
733       free_urlpos (urls);
734     }
735
736   msecs = wtimer_elapsed (timer);
737   wtimer_delete (timer);
738   logprintf (LOG_VERBOSE, _("Converted %d files in %.2f seconds.\n"),
739              file_count, (double)msecs / 1000);
740 }
741
742 /* Cleanup the data structures associated with recursive retrieving
743    (the variables above).  */
744 void
745 recursive_cleanup (void)
746 {
747   if (dl_file_url_map)
748     {
749       free_keys_and_values (dl_file_url_map);
750       hash_table_destroy (dl_file_url_map);
751       dl_file_url_map = NULL;
752     }
753   if (dl_url_file_map)
754     {
755       free_keys_and_values (dl_url_file_map);
756       hash_table_destroy (dl_url_file_map);
757       dl_url_file_map = NULL;
758     }
759   slist_free (downloaded_html_files);
760   downloaded_html_files = NULL;
761 }