]> sjero.net Git - wget/blob - src/recur.c
83a9b4ee84d5b155196263841ea37214fd3d014c
[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_n (0, url),
119              i->uri_encoding ? quote_n (1, 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 *, struct url *, 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 (struct url *start_url_parsed, 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 iri *i = iri_new ();
203
204 #define COPYSTR(x)  (x) ? xstrdup(x) : NULL;
205   /* Duplicate pi struct if not NULL */
206   if (pi)
207     {
208       i->uri_encoding = COPYSTR (pi->uri_encoding);
209       i->content_encoding = COPYSTR (pi->content_encoding);
210       i->utf8_encode = pi->utf8_encode;
211     }
212   else
213     set_uri_encoding (i, opt.locale, true);
214 #undef COPYSTR
215
216   queue = url_queue_new ();
217   blacklist = make_string_hash_table (0);
218
219   /* Enqueue the starting URL.  Use start_url_parsed->url rather than
220      just URL so we enqueue the canonical form of the URL.  */
221   url_enqueue (queue, i, xstrdup (start_url_parsed->url), NULL, 0, true,
222                false);
223   string_set_add (blacklist, start_url_parsed->url);
224
225   while (1)
226     {
227       bool descend = false;
228       char *url, *referer, *file = NULL;
229       int depth;
230       bool html_allowed, css_allowed;
231       bool is_css = false;
232       bool dash_p_leaf_HTML = false;
233
234       if (opt.quota && total_downloaded_bytes > opt.quota)
235         break;
236       if (status == FWRITEERR)
237         break;
238
239       /* Get the next URL from the queue... */
240
241       if (!url_dequeue (queue, (struct iri **) &i,
242                         (const char **)&url, (const char **)&referer,
243                         &depth, &html_allowed, &css_allowed))
244         break;
245
246       /* ...and download it.  Note that this download is in most cases
247          unconditional, as download_child_p already makes sure a file
248          doesn't get enqueued twice -- and yet this check is here, and
249          not in download_child_p.  This is so that if you run `wget -r
250          URL1 URL2', and a random URL is encountered once under URL1
251          and again under URL2, but at a different (possibly smaller)
252          depth, we want the URL's children to be taken into account
253          the second time.  */
254       if (dl_url_file_map && hash_table_contains (dl_url_file_map, url))
255         {
256           file = xstrdup (hash_table_get (dl_url_file_map, url));
257
258           DEBUGP (("Already downloaded \"%s\", reusing it from \"%s\".\n",
259                    url, file));
260
261           /* this sucks, needs to be combined! */
262           if (html_allowed
263               && downloaded_html_set
264               && string_set_contains (downloaded_html_set, file))
265             {
266               descend = true;
267               is_css = false;
268             }
269           if (css_allowed
270               && downloaded_css_set
271               && string_set_contains (downloaded_css_set, file))
272             {
273               descend = true;
274               is_css = true;
275             }
276         }
277       else
278         {
279           int dt = 0, url_err;
280           char *redirected = NULL;
281           struct url *url_parsed = url_parse (url, &url_err, i, false);
282
283           status = retrieve_url (url_parsed, url, &file, &redirected, referer,
284                                  &dt, false, i);
285
286           if (html_allowed && file && status == RETROK
287               && (dt & RETROKF) && (dt & TEXTHTML))
288             {
289               descend = true;
290               is_css = false;
291             }
292
293           /* a little different, css_allowed can override content type
294              lots of web servers serve css with an incorrect content type
295           */
296           if (file && status == RETROK
297               && (dt & RETROKF) &&
298               ((dt & TEXTCSS) || css_allowed))
299             {
300               descend = true;
301               is_css = true;
302             }
303
304           if (redirected)
305             {
306               /* We have been redirected, possibly to another host, or
307                  different path, or wherever.  Check whether we really
308                  want to follow it.  */
309               if (descend)
310                 {
311                   if (!descend_redirect_p (redirected, url_parsed, depth,
312                                            start_url_parsed, blacklist, i))
313                     descend = false;
314                   else
315                     /* Make sure that the old pre-redirect form gets
316                        blacklisted. */
317                     string_set_add (blacklist, url);
318                 }
319
320               xfree (url);
321               url = redirected;
322             }
323           url_free(url_parsed);
324         }
325
326       if (opt.spider)
327         {
328           visited_url (url, referer);
329         }
330
331       if (descend
332           && depth >= opt.reclevel && opt.reclevel != INFINITE_RECURSION)
333         {
334           if (opt.page_requisites
335               && (depth == opt.reclevel || depth == opt.reclevel + 1))
336             {
337               /* When -p is specified, we are allowed to exceed the
338                  maximum depth, but only for the "inline" links,
339                  i.e. those that are needed to display the page.
340                  Originally this could exceed the depth at most by
341                  one, but we allow one more level so that the leaf
342                  pages that contain frames can be loaded
343                  correctly.  */
344               dash_p_leaf_HTML = true;
345             }
346           else
347             {
348               /* Either -p wasn't specified or it was and we've
349                  already spent the two extra (pseudo-)levels that it
350                  affords us, so we need to bail out. */
351               DEBUGP (("Not descending further; at depth %d, max. %d.\n",
352                        depth, opt.reclevel));
353               descend = false;
354             }
355         }
356
357       /* If the downloaded document was HTML or CSS, parse it and enqueue the
358          links it contains. */
359
360       if (descend)
361         {
362           bool meta_disallow_follow = false;
363           struct urlpos *children
364             = is_css ? get_urls_css_file (file, url) :
365                        get_urls_html (file, url, &meta_disallow_follow, i);
366
367           if (opt.use_robots && meta_disallow_follow)
368             {
369               free_urlpos (children);
370               children = NULL;
371             }
372
373           if (children)
374             {
375               struct urlpos *child = children;
376               struct url *url_parsed = url_parse (url, NULL, i, false);
377               struct iri *ci;
378               char *referer_url = url;
379               bool strip_auth = (url_parsed != NULL
380                                  && url_parsed->user != NULL);
381               assert (url_parsed != NULL);
382
383               /* Strip auth info if present */
384               if (strip_auth)
385                 referer_url = url_string (url_parsed, URL_AUTH_HIDE);
386
387               for (; child; child = child->next)
388                 {
389                   if (child->ignore_when_downloading)
390                     continue;
391                   if (dash_p_leaf_HTML && !child->link_inline_p)
392                     continue;
393                   if (download_child_p (child, url_parsed, depth, start_url_parsed,
394                                         blacklist, i))
395                     {
396                       ci = iri_new ();
397                       set_uri_encoding (ci, i->content_encoding, false);
398                       url_enqueue (queue, ci, xstrdup (child->url->url),
399                                    xstrdup (referer_url), depth + 1,
400                                    child->link_expect_html,
401                                    child->link_expect_css);
402                       /* We blacklist the URL we have enqueued, because we
403                          don't want to enqueue (and hence download) the
404                          same URL twice.  */
405                       string_set_add (blacklist, child->url->url);
406                     }
407                 }
408
409               if (strip_auth)
410                 xfree (referer_url);
411               url_free (url_parsed);
412               free_urlpos (children);
413             }
414         }
415
416       if (file
417           && (opt.delete_after
418               || opt.spider /* opt.recursive is implicitely true */
419               || !acceptable (file)))
420         {
421           /* Either --delete-after was specified, or we loaded this
422              (otherwise unneeded because of --spider or rejected by -R)
423              HTML file just to harvest its hyperlinks -- in either case,
424              delete the local file. */
425           DEBUGP (("Removing file due to %s in recursive_retrieve():\n",
426                    opt.delete_after ? "--delete-after" :
427                    (opt.spider ? "--spider" :
428                     "recursive rejection criteria")));
429           logprintf (LOG_VERBOSE,
430                      (opt.delete_after || opt.spider
431                       ? _("Removing %s.\n")
432                       : _("Removing %s since it should be rejected.\n")),
433                      file);
434           if (unlink (file))
435             logprintf (LOG_NOTQUIET, "unlink: %s\n", strerror (errno));
436           logputs (LOG_VERBOSE, "\n");
437           register_delete_file (file);
438         }
439
440       xfree (url);
441       xfree_null (referer);
442       xfree_null (file);
443       iri_free (i);
444     }
445
446   /* If anything is left of the queue due to a premature exit, free it
447      now.  */
448   {
449     char *d1, *d2;
450     int d3;
451     bool d4, d5;
452     struct iri *d6;
453     while (url_dequeue (queue, (struct iri **)&d6,
454                         (const char **)&d1, (const char **)&d2, &d3, &d4, &d5))
455       {
456         iri_free (d6);
457         xfree (d1);
458         xfree_null (d2);
459       }
460   }
461   url_queue_delete (queue);
462
463   string_set_free (blacklist);
464
465   if (opt.quota && total_downloaded_bytes > opt.quota)
466     return QUOTEXC;
467   else if (status == FWRITEERR)
468     return FWRITEERR;
469   else
470     return RETROK;
471 }
472
473 /* Based on the context provided by retrieve_tree, decide whether a
474    URL is to be descended to.  This is only ever called from
475    retrieve_tree, but is in a separate function for clarity.
476
477    The most expensive checks (such as those for robots) are memoized
478    by storing these URLs to BLACKLIST.  This may or may not help.  It
479    will help if those URLs are encountered many times.  */
480
481 static bool
482 download_child_p (const struct urlpos *upos, struct url *parent, int depth,
483                   struct url *start_url_parsed, struct hash_table *blacklist,
484                   struct iri *iri)
485 {
486   struct url *u = upos->url;
487   const char *url = u->url;
488   bool u_scheme_like_http;
489
490   DEBUGP (("Deciding whether to enqueue \"%s\".\n", url));
491
492   if (string_set_contains (blacklist, url))
493     {
494       if (opt.spider)
495         {
496           char *referrer = url_string (parent, URL_AUTH_HIDE_PASSWD);
497           DEBUGP (("download_child_p: parent->url is: %s\n", quote (parent->url)));
498           visited_url (url, referrer);
499           xfree (referrer);
500         }
501       DEBUGP (("Already on the black list.\n"));
502       goto out;
503     }
504
505   /* Several things to check for:
506      1. if scheme is not http, and we don't load it
507      2. check for relative links (if relative_only is set)
508      3. check for domain
509      4. check for no-parent
510      5. check for excludes && includes
511      6. check for suffix
512      7. check for same host (if spanhost is unset), with possible
513      gethostbyname baggage
514      8. check for robots.txt
515
516      Addendum: If the URL is FTP, and it is to be loaded, only the
517      domain and suffix settings are "stronger".
518
519      Note that .html files will get loaded regardless of suffix rules
520      (but that is remedied later with unlink) unless the depth equals
521      the maximum depth.
522
523      More time- and memory- consuming tests should be put later on
524      the list.  */
525
526   /* Determine whether URL under consideration has a HTTP-like scheme. */
527   u_scheme_like_http = schemes_are_similar_p (u->scheme, SCHEME_HTTP);
528
529   /* 1. Schemes other than HTTP are normally not recursed into. */
530   if (!u_scheme_like_http && !(u->scheme == SCHEME_FTP && opt.follow_ftp))
531     {
532       DEBUGP (("Not following non-HTTP schemes.\n"));
533       goto out;
534     }
535
536   /* 2. If it is an absolute link and they are not followed, throw it
537      out.  */
538   if (u_scheme_like_http)
539     if (opt.relative_only && !upos->link_relative_p)
540       {
541         DEBUGP (("It doesn't really look like a relative link.\n"));
542         goto out;
543       }
544
545   /* 3. If its domain is not to be accepted/looked-up, chuck it
546      out.  */
547   if (!accept_domain (u))
548     {
549       DEBUGP (("The domain was not accepted.\n"));
550       goto out;
551     }
552
553   /* 4. Check for parent directory.
554
555      If we descended to a different host or changed the scheme, ignore
556      opt.no_parent.  Also ignore it for documents needed to display
557      the parent page when in -p mode.  */
558   if (opt.no_parent
559       && schemes_are_similar_p (u->scheme, start_url_parsed->scheme)
560       && 0 == strcasecmp (u->host, start_url_parsed->host)
561       && u->port == start_url_parsed->port
562       && !(opt.page_requisites && upos->link_inline_p))
563     {
564       if (!subdir_p (start_url_parsed->dir, u->dir))
565         {
566           DEBUGP (("Going to \"%s\" would escape \"%s\" with no_parent on.\n",
567                    u->dir, start_url_parsed->dir));
568           goto out;
569         }
570     }
571
572   /* 5. If the file does not match the acceptance list, or is on the
573      rejection list, chuck it out.  The same goes for the directory
574      exclusion and inclusion lists.  */
575   if (opt.includes || opt.excludes)
576     {
577       if (!accdir (u->dir))
578         {
579           DEBUGP (("%s (%s) is excluded/not-included.\n", url, u->dir));
580           goto out;
581         }
582     }
583
584   /* 6. Check for acceptance/rejection rules.  We ignore these rules
585      for directories (no file name to match) and for non-leaf HTMLs,
586      which can lead to other files that do need to be downloaded.  (-p
587      automatically implies non-leaf because with -p we can, if
588      necesary, overstep the maximum depth to get the page requisites.)  */
589   if (u->file[0] != '\0'
590       && !(has_html_suffix_p (u->file)
591            /* The exception only applies to non-leaf HTMLs (but -p
592               always implies non-leaf because we can overstep the
593               maximum depth to get the requisites): */
594            && (/* non-leaf */
595                opt.reclevel == INFINITE_RECURSION
596                /* also non-leaf */
597                || depth < opt.reclevel - 1
598                /* -p, which implies non-leaf (see above) */
599                || opt.page_requisites)))
600     {
601       if (!acceptable (u->file))
602         {
603           DEBUGP (("%s (%s) does not match acc/rej rules.\n",
604                    url, u->file));
605           goto out;
606         }
607     }
608
609   /* 7. */
610   if (schemes_are_similar_p (u->scheme, parent->scheme))
611     if (!opt.spanhost && 0 != strcasecmp (parent->host, u->host))
612       {
613         DEBUGP (("This is not the same hostname as the parent's (%s and %s).\n",
614                  u->host, parent->host));
615         goto out;
616       }
617
618   /* 8. */
619   if (opt.use_robots && u_scheme_like_http)
620     {
621       struct robot_specs *specs = res_get_specs (u->host, u->port);
622       if (!specs)
623         {
624           char *rfile;
625           if (res_retrieve_file (url, &rfile, iri))
626             {
627               specs = res_parse_from_file (rfile);
628
629               /* Delete the robots.txt file if we chose to either delete the
630                  files after downloading or we're just running a spider. */
631               if (opt.delete_after || opt.spider)
632                 {
633                   logprintf (LOG_VERBOSE, "Removing %s.\n", rfile);
634                   if (unlink (rfile))
635                       logprintf (LOG_NOTQUIET, "unlink: %s\n",
636                                  strerror (errno));
637                 }
638
639               xfree (rfile);
640             }
641           else
642             {
643               /* If we cannot get real specs, at least produce
644                  dummy ones so that we can register them and stop
645                  trying to retrieve them.  */
646               specs = res_parse ("", 0);
647             }
648           res_register_specs (u->host, u->port, specs);
649         }
650
651       /* Now that we have (or don't have) robots.txt specs, we can
652          check what they say.  */
653       if (!res_match_path (specs, u->path))
654         {
655           DEBUGP (("Not following %s because robots.txt forbids it.\n", url));
656           string_set_add (blacklist, url);
657           goto out;
658         }
659     }
660
661   /* The URL has passed all the tests.  It can be placed in the
662      download queue. */
663   DEBUGP (("Decided to load it.\n"));
664
665   return true;
666
667  out:
668   DEBUGP (("Decided NOT to load it.\n"));
669
670   return false;
671 }
672
673 /* This function determines whether we will consider downloading the
674    children of a URL whose download resulted in a redirection,
675    possibly to another host, etc.  It is needed very rarely, and thus
676    it is merely a simple-minded wrapper around download_child_p.  */
677
678 static bool
679 descend_redirect_p (const char *redirected, struct url *orig_parsed, int depth,
680                     struct url *start_url_parsed, struct hash_table *blacklist,
681                     struct iri *iri)
682 {
683   struct url *new_parsed;
684   struct urlpos *upos;
685   bool success;
686
687   assert (orig_parsed != NULL);
688
689   new_parsed = url_parse (redirected, NULL, NULL, false);
690   assert (new_parsed != NULL);
691
692   upos = xnew0 (struct urlpos);
693   upos->url = new_parsed;
694
695   success = download_child_p (upos, orig_parsed, depth,
696                               start_url_parsed, blacklist, iri);
697
698   url_free (new_parsed);
699   xfree (upos);
700
701   if (!success)
702     DEBUGP (("Redirection \"%s\" failed the test.\n", redirected));
703
704   return success;
705 }
706
707 /* vim:set sts=2 sw=2 cino+={s: */