]> sjero.net Git - wget/blob - src/recur.c
Correct iri handling while fetching a remote file list with -i and provide a test
[wget] / src / recur.c
1 /* Handling of recursive HTTP retrieving.
2    Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
3    2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
4
5 This file is part of GNU Wget.
6
7 GNU Wget is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version.
11
12 GNU Wget is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Wget.  If not, see <http://www.gnu.org/licenses/>.
19
20 Additional permission under GNU GPL version 3 section 7
21
22 If you modify this program, or any covered work, by linking or
23 combining it with the OpenSSL project's OpenSSL library (or a
24 modified version of that library), containing parts covered by the
25 terms of the OpenSSL or SSLeay licenses, the Free Software Foundation
26 grants you additional permission to convey the resulting work.
27 Corresponding Source for a non-source form of such a combination
28 shall include the source code for the parts of OpenSSL used as well
29 as that of the covered work.  */
30
31 #include "wget.h"
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #ifdef HAVE_UNISTD_H
37 # include <unistd.h>
38 #endif /* HAVE_UNISTD_H */
39 #include <errno.h>
40 #include <assert.h>
41
42 #include "url.h"
43 #include "recur.h"
44 #include "utils.h"
45 #include "retr.h"
46 #include "ftp.h"
47 #include "host.h"
48 #include "hash.h"
49 #include "res.h"
50 #include "convert.h"
51 #include "html-url.h"
52 #include "css-url.h"
53 #include "spider.h"
54 \f
55 /* Functions for maintaining the URL queue.  */
56
57 struct queue_element {
58   const char *url;              /* the URL to download */
59   const char *referer;          /* the referring document */
60   int depth;                    /* the depth */
61   bool html_allowed;            /* whether the document is allowed to
62                                    be treated as HTML. */
63   struct iri *iri;                /* sXXXav */
64   bool css_allowed;             /* whether the document is allowed to
65                                    be treated as CSS. */
66   struct queue_element *next;   /* next element in queue */
67 };
68
69 struct url_queue {
70   struct queue_element *head;
71   struct queue_element *tail;
72   int count, maxcount;
73 };
74
75 /* Create a URL queue. */
76
77 static struct url_queue *
78 url_queue_new (void)
79 {
80   struct url_queue *queue = xnew0 (struct url_queue);
81   return queue;
82 }
83
84 /* Delete a URL queue. */
85
86 static void
87 url_queue_delete (struct url_queue *queue)
88 {
89   xfree (queue);
90 }
91
92 /* Enqueue a URL in the queue.  The queue is FIFO: the items will be
93    retrieved ("dequeued") from the queue in the order they were placed
94    into it.  */
95
96 static void
97 url_enqueue (struct url_queue *queue, struct iri *i,
98              const char *url, const char *referer, int depth,
99              bool html_allowed, bool css_allowed)
100 {
101   struct queue_element *qel = xnew (struct queue_element);
102   qel->iri = i;
103   qel->url = url;
104   qel->referer = referer;
105   qel->depth = depth;
106   qel->html_allowed = html_allowed;
107   qel->css_allowed = css_allowed;
108   qel->next = NULL;
109
110   ++queue->count;
111   if (queue->count > queue->maxcount)
112     queue->maxcount = queue->count;
113
114   DEBUGP (("Enqueuing %s at depth %d\n", url, depth));
115   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
116
117   if (i)
118     DEBUGP (("[IRI Enqueuing %s with %s\n", quote (url),
119              i->uri_encoding ? quote (i->uri_encoding) : "None"));
120
121   if (queue->tail)
122     queue->tail->next = qel;
123   queue->tail = qel;
124
125   if (!queue->head)
126     queue->head = queue->tail;
127 }
128
129 /* Take a URL out of the queue.  Return true if this operation
130    succeeded, or false if the queue is empty.  */
131
132 static bool
133 url_dequeue (struct url_queue *queue, struct iri **i,
134              const char **url, const char **referer, int *depth,
135              bool *html_allowed, bool *css_allowed)
136 {
137   struct queue_element *qel = queue->head;
138
139   if (!qel)
140     return false;
141
142   queue->head = queue->head->next;
143   if (!queue->head)
144     queue->tail = NULL;
145
146   *i = qel->iri;
147   *url = qel->url;
148   *referer = qel->referer;
149   *depth = qel->depth;
150   *html_allowed = qel->html_allowed;
151   *css_allowed = qel->css_allowed;
152
153   --queue->count;
154
155   DEBUGP (("Dequeuing %s at depth %d\n", qel->url, qel->depth));
156   DEBUGP (("Queue count %d, maxcount %d.\n", queue->count, queue->maxcount));
157
158   xfree (qel);
159   return true;
160 }
161 \f
162 static bool download_child_p (const struct urlpos *, struct url *, int,
163                               struct url *, struct hash_table *, struct iri *);
164 static bool descend_redirect_p (const char *, const char *, int,
165                                 struct url *, struct hash_table *, struct iri *);
166
167
168 /* Retrieve a part of the web beginning with START_URL.  This used to
169    be called "recursive retrieval", because the old function was
170    recursive and implemented depth-first search.  retrieve_tree on the
171    other hand implements breadth-search traversal of the tree, which
172    results in much nicer ordering of downloads.
173
174    The algorithm this function uses is simple:
175
176    1. put START_URL in the queue.
177    2. while there are URLs in the queue:
178
179      3. get next URL from the queue.
180      4. download it.
181      5. if the URL is HTML and its depth does not exceed maximum depth,
182         get the list of URLs embedded therein.
183      6. for each of those URLs do the following:
184
185        7. if the URL is not one of those downloaded before, and if it
186           satisfies the criteria specified by the various command-line
187           options, add it to the queue. */
188
189 uerr_t
190 retrieve_tree (const char *start_url, struct iri *pi)
191 {
192   uerr_t status = RETROK;
193
194   /* The queue of URLs we need to load. */
195   struct url_queue *queue;
196
197   /* The URLs we do not wish to enqueue, because they are already in
198      the queue, but haven't been downloaded yet.  */
199   struct hash_table *blacklist;
200
201   int up_error_code;
202   struct url *start_url_parsed;
203   struct iri *i = iri_new ();
204
205 #define COPYSTR(x)  (x) ? xstrdup(x) : NULL;
206   /* Duplicate pi struct if not NULL */
207   if (pi)
208     {
209       i->uri_encoding = COPYSTR (pi->uri_encoding);
210       i->content_encoding = COPYSTR (pi->content_encoding);
211       i->utf8_encode = pi->utf8_encode;
212     }
213   else
214     set_uri_encoding (i, opt.locale, true);
215 #undef COPYSTR
216
217   start_url_parsed = url_parse (start_url, &up_error_code, i);
218   if (!start_url_parsed)
219     {
220       logprintf (LOG_NOTQUIET, "%s: %s.\n", start_url,
221                  url_error (up_error_code));
222       return URLERROR;
223     }
224
225   queue = url_queue_new ();
226   blacklist = make_string_hash_table (0);
227
228   /* Enqueue the starting URL.  Use start_url_parsed->url rather than
229      just URL so we enqueue the canonical form of the URL.  */
230   url_enqueue (queue, i, xstrdup (start_url_parsed->url), NULL, 0, true,
231                false);
232   string_set_add (blacklist, start_url_parsed->url);
233
234   while (1)
235     {
236       bool descend = false;
237       char *url, *referer, *file = NULL;
238       int depth;
239       bool html_allowed, css_allowed;
240       bool is_css = false;
241       bool dash_p_leaf_HTML = false;
242
243       if (opt.quota && total_downloaded_bytes > opt.quota)
244         break;
245       if (status == FWRITEERR)
246         break;
247
248       /* Get the next URL from the queue... */
249
250       if (!url_dequeue (queue, (struct iri **) &i,
251                         (const char **)&url, (const char **)&referer,
252                         &depth, &html_allowed, &css_allowed))
253         break;
254
255       /* ...and download it.  Note that this download is in most cases
256          unconditional, as download_child_p already makes sure a file
257          doesn't get enqueued twice -- and yet this check is here, and
258          not in download_child_p.  This is so that if you run `wget -r
259          URL1 URL2', and a random URL is encountered once under URL1
260          and again under URL2, but at a different (possibly smaller)
261          depth, we want the URL's children to be taken into account
262          the second time.  */
263       if (dl_url_file_map && hash_table_contains (dl_url_file_map, url))
264         {
265           file = xstrdup (hash_table_get (dl_url_file_map, url));
266
267           DEBUGP (("Already downloaded \"%s\", reusing it from \"%s\".\n",
268                    url, file));
269
270           /* this sucks, needs to be combined! */
271           if (html_allowed
272               && downloaded_html_set
273               && string_set_contains (downloaded_html_set, file))
274             {
275               descend = true;
276               is_css = false;
277             }
278           if (css_allowed
279               && downloaded_css_set
280               && string_set_contains (downloaded_css_set, file))
281             {
282               descend = true;
283               is_css = true;
284             }
285         }
286       else
287         {
288           int dt = 0;
289           char *redirected = NULL;
290
291           status = retrieve_url (url, &file, &redirected, referer, &dt,
292                                  false, i);
293
294           if (html_allowed && file && status == RETROK
295               && (dt & RETROKF) && (dt & TEXTHTML))
296             {
297               descend = true;
298               is_css = false;
299             }
300
301           /* a little different, css_allowed can override content type
302              lots of web servers serve css with an incorrect content type
303           */
304           if (file && status == RETROK
305               && (dt & RETROKF) &&
306               ((dt & TEXTCSS) || css_allowed))
307             {
308               descend = true;
309               is_css = true;
310             }
311
312           if (redirected)
313             {
314               /* We have been redirected, possibly to another host, or
315                  different path, or wherever.  Check whether we really
316                  want to follow it.  */
317               if (descend)
318                 {
319                   if (!descend_redirect_p (redirected, url, depth,
320                                            start_url_parsed, blacklist, i))
321                     descend = false;
322                   else
323                     /* Make sure that the old pre-redirect form gets
324                        blacklisted. */
325                     string_set_add (blacklist, url);
326                 }
327
328               xfree (url);
329               url = redirected;
330             }
331         }
332
333       if (opt.spider)
334         {
335           visited_url (url, referer);
336         }
337
338       if (descend
339           && depth >= opt.reclevel && opt.reclevel != INFINITE_RECURSION)
340         {
341           if (opt.page_requisites
342               && (depth == opt.reclevel || depth == opt.reclevel + 1))
343             {
344               /* When -p is specified, we are allowed to exceed the
345                  maximum depth, but only for the "inline" links,
346                  i.e. those that are needed to display the page.
347                  Originally this could exceed the depth at most by
348                  one, but we allow one more level so that the leaf
349                  pages that contain frames can be loaded
350                  correctly.  */
351               dash_p_leaf_HTML = true;
352             }
353           else
354             {
355               /* Either -p wasn't specified or it was and we've
356                  already spent the two extra (pseudo-)levels that it
357                  affords us, so we need to bail out. */
358               DEBUGP (("Not descending further; at depth %d, max. %d.\n",
359                        depth, opt.reclevel));
360               descend = false;
361             }
362         }
363
364       /* If the downloaded document was HTML or CSS, parse it and enqueue the
365          links it contains. */
366
367       if (descend)
368         {
369           bool meta_disallow_follow = false;
370           struct urlpos *children
371             = is_css ? get_urls_css_file (file, url) :
372                        get_urls_html (file, url, &meta_disallow_follow, i);
373
374           if (opt.use_robots && meta_disallow_follow)
375             {
376               free_urlpos (children);
377               children = NULL;
378             }
379
380           if (children)
381             {
382               struct urlpos *child = children;
383               struct url *url_parsed = url_parse (url, NULL, i);
384               struct iri *ci;
385               char *referer_url = url;
386               bool strip_auth = (url_parsed != NULL
387                                  && url_parsed->user != NULL);
388               assert (url_parsed != NULL);
389
390               /* Strip auth info if present */
391               if (strip_auth)
392                 referer_url = url_string (url_parsed, URL_AUTH_HIDE);
393
394               for (; child; child = child->next)
395                 {
396                   if (child->ignore_when_downloading)
397                     continue;
398                   if (dash_p_leaf_HTML && !child->link_inline_p)
399                     continue;
400                   if (download_child_p (child, url_parsed, depth, start_url_parsed,
401                                         blacklist, i))
402                     {
403                       ci = iri_new ();
404                       set_uri_encoding (ci, i->content_encoding, false);
405                       url_enqueue (queue, ci, xstrdup (child->url->url),
406                                    xstrdup (referer_url), depth + 1,
407                                    child->link_expect_html,
408                                    child->link_expect_css);
409                       /* We blacklist the URL we have enqueued, because we
410                          don't want to enqueue (and hence download) the
411                          same URL twice.  */
412                       string_set_add (blacklist, child->url->url);
413                     }
414                 }
415
416               if (strip_auth)
417                 xfree (referer_url);
418               url_free (url_parsed);
419               free_urlpos (children);
420             }
421         }
422
423       if (file
424           && (opt.delete_after
425               || opt.spider /* opt.recursive is implicitely true */
426               || !acceptable (file)))
427         {
428           /* Either --delete-after was specified, or we loaded this
429              (otherwise unneeded because of --spider or rejected by -R)
430              HTML file just to harvest its hyperlinks -- in either case,
431              delete the local file. */
432           DEBUGP (("Removing file due to %s in recursive_retrieve():\n",
433                    opt.delete_after ? "--delete-after" :
434                    (opt.spider ? "--spider" :
435                     "recursive rejection criteria")));
436           logprintf (LOG_VERBOSE,
437                      (opt.delete_after || opt.spider
438                       ? _("Removing %s.\n")
439                       : _("Removing %s since it should be rejected.\n")),
440                      file);
441           if (unlink (file))
442             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
443           logputs (LOG_VERBOSE, "\n");
444           register_delete_file (file);
445         }
446
447       xfree (url);
448       xfree_null (referer);
449       xfree_null (file);
450       iri_free (i);
451     }
452
453   /* If anything is left of the queue due to a premature exit, free it
454      now.  */
455   {
456     char *d1, *d2;
457     int d3;
458     bool d4, d5;
459     struct iri *d6;
460     while (url_dequeue (queue, (struct iri **)&d6,
461                         (const char **)&d1, (const char **)&d2, &d3, &d4, &d5))
462       {
463         iri_free (d6);
464         xfree (d1);
465         xfree_null (d2);
466       }
467   }
468   url_queue_delete (queue);
469
470   if (start_url_parsed)
471     url_free (start_url_parsed);
472   string_set_free (blacklist);
473
474   if (opt.quota && total_downloaded_bytes > opt.quota)
475     return QUOTEXC;
476   else if (status == FWRITEERR)
477     return FWRITEERR;
478   else
479     return RETROK;
480 }
481
482 /* Based on the context provided by retrieve_tree, decide whether a
483    URL is to be descended to.  This is only ever called from
484    retrieve_tree, but is in a separate function for clarity.
485
486    The most expensive checks (such as those for robots) are memoized
487    by storing these URLs to BLACKLIST.  This may or may not help.  It
488    will help if those URLs are encountered many times.  */
489
490 static bool
491 download_child_p (const struct urlpos *upos, struct url *parent, int depth,
492                   struct url *start_url_parsed, struct hash_table *blacklist,
493                   struct iri *iri)
494 {
495   struct url *u = upos->url;
496   const char *url = u->url;
497   bool u_scheme_like_http;
498
499   DEBUGP (("Deciding whether to enqueue \"%s\".\n", url));
500
501   if (string_set_contains (blacklist, url))
502     {
503       if (opt.spider)
504         {
505           char *referrer = url_string (parent, URL_AUTH_HIDE_PASSWD);
506           DEBUGP (("download_child_p: parent->url is: %s\n", quote (parent->url)));
507           visited_url (url, referrer);
508           xfree (referrer);
509         }
510       DEBUGP (("Already on the black list.\n"));
511       goto out;
512     }
513
514   /* Several things to check for:
515      1. if scheme is not http, and we don't load it
516      2. check for relative links (if relative_only is set)
517      3. check for domain
518      4. check for no-parent
519      5. check for excludes && includes
520      6. check for suffix
521      7. check for same host (if spanhost is unset), with possible
522      gethostbyname baggage
523      8. check for robots.txt
524
525      Addendum: If the URL is FTP, and it is to be loaded, only the
526      domain and suffix settings are "stronger".
527
528      Note that .html files will get loaded regardless of suffix rules
529      (but that is remedied later with unlink) unless the depth equals
530      the maximum depth.
531
532      More time- and memory- consuming tests should be put later on
533      the list.  */
534
535   /* Determine whether URL under consideration has a HTTP-like scheme. */
536   u_scheme_like_http = schemes_are_similar_p (u->scheme, SCHEME_HTTP);
537
538   /* 1. Schemes other than HTTP are normally not recursed into. */
539   if (!u_scheme_like_http && !(u->scheme == SCHEME_FTP && opt.follow_ftp))
540     {
541       DEBUGP (("Not following non-HTTP schemes.\n"));
542       goto out;
543     }
544
545   /* 2. If it is an absolute link and they are not followed, throw it
546      out.  */
547   if (u_scheme_like_http)
548     if (opt.relative_only && !upos->link_relative_p)
549       {
550         DEBUGP (("It doesn't really look like a relative link.\n"));
551         goto out;
552       }
553
554   /* 3. If its domain is not to be accepted/looked-up, chuck it
555      out.  */
556   if (!accept_domain (u))
557     {
558       DEBUGP (("The domain was not accepted.\n"));
559       goto out;
560     }
561
562   /* 4. Check for parent directory.
563
564      If we descended to a different host or changed the scheme, ignore
565      opt.no_parent.  Also ignore it for documents needed to display
566      the parent page when in -p mode.  */
567   if (opt.no_parent
568       && schemes_are_similar_p (u->scheme, start_url_parsed->scheme)
569       && 0 == strcasecmp (u->host, start_url_parsed->host)
570       && u->port == start_url_parsed->port
571       && !(opt.page_requisites && upos->link_inline_p))
572     {
573       if (!subdir_p (start_url_parsed->dir, u->dir))
574         {
575           DEBUGP (("Going to \"%s\" would escape \"%s\" with no_parent on.\n",
576                    u->dir, start_url_parsed->dir));
577           goto out;
578         }
579     }
580
581   /* 5. If the file does not match the acceptance list, or is on the
582      rejection list, chuck it out.  The same goes for the directory
583      exclusion and inclusion lists.  */
584   if (opt.includes || opt.excludes)
585     {
586       if (!accdir (u->dir))
587         {
588           DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir));
589           goto out;
590         }
591     }
592
593   /* 6. Check for acceptance/rejection rules.  We ignore these rules
594      for directories (no file name to match) and for non-leaf HTMLs,
595      which can lead to other files that do need to be downloaded.  (-p
596      automatically implies non-leaf because with -p we can, if
597      necesary, overstep the maximum depth to get the page requisites.)  */
598   if (u->file[0] != '\0'
599       && !(has_html_suffix_p (u->file)
600            /* The exception only applies to non-leaf HTMLs (but -p
601               always implies non-leaf because we can overstep the
602               maximum depth to get the requisites): */
603            && (/* non-leaf */
604                opt.reclevel == INFINITE_RECURSION
605                /* also non-leaf */
606                || depth < opt.reclevel - 1
607                /* -p, which implies non-leaf (see above) */
608                || opt.page_requisites)))
609     {
610       if (!acceptable (u->file))
611         {
612           DEBUGP (("%s (%s) does not match acc/rej rules.\n",
613                    url, u->file));
614           goto out;
615         }
616     }
617
618   /* 7. */
619   if (schemes_are_similar_p (u->scheme, parent->scheme))
620     if (!opt.spanhost && 0 != strcasecmp (parent->host, u->host))
621       {
622         DEBUGP (("This is not the same hostname as the parent's (%s and %s).\n",
623                  u->host, parent->host));
624         goto out;
625       }
626
627   /* 8. */
628   if (opt.use_robots && u_scheme_like_http)
629     {
630       struct robot_specs *specs = res_get_specs (u->host, u->port);
631       if (!specs)
632         {
633           char *rfile;
634           if (res_retrieve_file (url, &rfile, iri))
635             {
636               specs = res_parse_from_file (rfile);
637
638               /* Delete the robots.txt file if we chose to either delete the
639                  files after downloading or we're just running a spider. */
640               if (opt.delete_after || opt.spider)
641                 {
642                   logprintf (LOG_VERBOSE, "Removing %s.\n", rfile);
643                   if (unlink (rfile))
644                       logprintf (LOG_NOTQUIET, "unlink: %s\n",
645                                  strerror (errno));
646                 }
647
648               xfree (rfile);
649             }
650           else
651             {
652               /* If we cannot get real specs, at least produce
653                  dummy ones so that we can register them and stop
654                  trying to retrieve them.  */
655               specs = res_parse ("", 0);
656             }
657           res_register_specs (u->host, u->port, specs);
658         }
659
660       /* Now that we have (or don't have) robots.txt specs, we can
661          check what they say.  */
662       if (!res_match_path (specs, u->path))
663         {
664           DEBUGP (("Not following %s because robots.txt forbids it.\n", url));
665           string_set_add (blacklist, url);
666           goto out;
667         }
668     }
669
670   /* The URL has passed all the tests.  It can be placed in the
671      download queue. */
672   DEBUGP (("Decided to load it.\n"));
673
674   return true;
675
676  out:
677   DEBUGP (("Decided NOT to load it.\n"));
678
679   return false;
680 }
681
682 /* This function determines whether we will consider downloading the
683    children of a URL whose download resulted in a redirection,
684    possibly to another host, etc.  It is needed very rarely, and thus
685    it is merely a simple-minded wrapper around download_child_p.  */
686
687 static bool
688 descend_redirect_p (const char *redirected, const char *original, int depth,
689                     struct url *start_url_parsed, struct hash_table *blacklist,
690                     struct iri *iri)
691 {
692   struct url *orig_parsed, *new_parsed;
693   struct urlpos *upos;
694   bool success;
695
696   orig_parsed = url_parse (original, NULL, NULL);
697   assert (orig_parsed != NULL);
698
699   new_parsed = url_parse (redirected, NULL, NULL);
700   assert (new_parsed != NULL);
701
702   upos = xnew0 (struct urlpos);
703   upos->url = new_parsed;
704
705   success = download_child_p (upos, orig_parsed, depth,
706                               start_url_parsed, blacklist, iri);
707
708   url_free (orig_parsed);
709   url_free (new_parsed);
710   xfree (upos);
711
712   if (!success)
713     DEBUGP (("Redirection \"%s\" failed the test.\n", redirected));
714
715   return success;
716 }
717
718 /* vim:set sts=2 sw=2 cino+={s: */