]> sjero.net Git - wget/blobdiff - src/html-url.c
Ted Mielczarek's CSS wonder-patch, applied against the source from around the time...
[wget] / src / html-url.c
index 80f5b96c19ffcec006a0f9d3565868ddd11be95a..ebf8494db99b29f3e6941e70e96b3dbe8e8fc6b0 100644 (file)
@@ -1,5 +1,5 @@
 /* Collect URLs from HTML source.
-   Copyright (C) 1998, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   Copyright (C) 1998-2006 Free Software Foundation, Inc.
 
 This file is part of GNU Wget.
 
@@ -14,8 +14,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+along with Wget; if not, write to the Free Software Foundation, Inc.,
+51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 
 In addition, as a special exception, the Free Software Foundation
 gives permission to link the code of its release of Wget with the
@@ -30,11 +30,7 @@ so, delete this exception statement from your version.  */
 #include <config.h>
 
 #include <stdio.h>
-#ifdef HAVE_STRING_H
-# include <string.h>
-#else
-# include <strings.h>
-#endif
+#include <string.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <assert.h>
@@ -45,18 +41,14 @@ so, delete this exception statement from your version.  */
 #include "utils.h"
 #include "hash.h"
 #include "convert.h"
+#include "recur.h"
+#include "html-url.h"
+#include "css-url.h"
 
-#ifndef errno
-extern int errno;
-#endif
+typedef void (*tag_handler_t) (int, struct taginfo *, struct map_context *);
 
-struct map_context;
-
-typedef void (*tag_handler_t) PARAMS ((int, struct taginfo *,
-                                      struct map_context *));
-
-#define DECLARE_TAG_HANDLER(fun)                                       \
-  static void fun PARAMS ((int, struct taginfo *, struct map_context *))
+#define DECLARE_TAG_HANDLER(fun)                               \
+  static void fun (int, struct taginfo *, struct map_context *)
 
 DECLARE_TAG_HANDLER (tag_find_urls);
 DECLARE_TAG_HANDLER (tag_handle_base);
@@ -81,6 +73,7 @@ enum {
   TAG_LAYER,
   TAG_LINK,
   TAG_META,
+  TAG_OBJECT,
   TAG_OVERLAY,
   TAG_SCRIPT,
   TAG_TABLE,
@@ -111,6 +104,7 @@ static struct known_tag {
   { TAG_LAYER,  "layer",       tag_find_urls },
   { TAG_LINK,   "link",        tag_handle_link },
   { TAG_META,   "meta",        tag_handle_meta },
+  { TAG_OBJECT,  "object",     tag_find_urls },
   { TAG_OVERLAY, "overlay",    tag_find_urls },
   { TAG_SCRIPT,         "script",      tag_find_urls },
   { TAG_TABLE,  "table",       tag_find_urls },
@@ -121,11 +115,19 @@ static struct known_tag {
 /* tag_url_attributes documents which attributes of which tags contain
    URLs to harvest.  It is used by tag_find_urls.  */
 
-/* Defines for the FLAGS field; currently only one flag is defined. */
+/* Defines for the FLAGS. */
+
+/* The link is "inline", i.e. needs to be retrieved for this document
+   to be correctly rendered.  Inline links include inlined images,
+   stylesheets, children frames, etc.  */
+#define ATTR_INLINE    1
 
-/* This tag points to an external document not necessary for rendering this 
-   document (i.e. it's not an inlined image, stylesheet, etc.). */
-#define TUA_EXTERNAL 1
+/* The link is expected to yield HTML contents.  It's important not to
+   try to follow HTML obtained by following e.g. <img src="...">
+   regardless of content-type.  Doing this causes infinite loops for
+   "images" that return non-404 error pages with links to the same
+   image.  */
+#define ATTR_HTML      2
 
 /* For tags handled by tag_find_urls: attributes that contain URLs to
    download. */
@@ -134,41 +136,43 @@ static struct {
   const char *attr_name;
   int flags;
 } tag_url_attributes[] = {
-  { TAG_A,             "href",         TUA_EXTERNAL },
-  { TAG_APPLET,                "code",         0 },
-  { TAG_AREA,          "href",         TUA_EXTERNAL },
-  { TAG_BGSOUND,       "src",          0 },
-  { TAG_BODY,          "background",   0 },
-  { TAG_EMBED,         "href",         TUA_EXTERNAL },
-  { TAG_EMBED,         "src",          0 },
-  { TAG_FIG,           "src",          0 },
-  { TAG_FRAME,         "src",          0 },
-  { TAG_IFRAME,                "src",          0 },
-  { TAG_IMG,           "href",         0 },
-  { TAG_IMG,           "lowsrc",       0 },
-  { TAG_IMG,           "src",          0 },
-  { TAG_INPUT,         "src",          0 },
-  { TAG_LAYER,         "src",          0 },
-  { TAG_OVERLAY,       "src",          0 },
-  { TAG_SCRIPT,                "src",          0 },
-  { TAG_TABLE,         "background",   0 },
-  { TAG_TD,            "background",   0 },
-  { TAG_TH,            "background",   0 }
+  { TAG_A,             "href",         ATTR_HTML },
+  { TAG_APPLET,                "code",         ATTR_INLINE },
+  { TAG_AREA,          "href",         ATTR_HTML },
+  { TAG_BGSOUND,       "src",          ATTR_INLINE },
+  { TAG_BODY,          "background",   ATTR_INLINE },
+  { TAG_EMBED,         "href",         ATTR_HTML },
+  { TAG_EMBED,         "src",          ATTR_INLINE | ATTR_HTML },
+  { TAG_FIG,           "src",          ATTR_INLINE },
+  { TAG_FRAME,         "src",          ATTR_INLINE | ATTR_HTML },
+  { TAG_IFRAME,                "src",          ATTR_INLINE | ATTR_HTML },
+  { TAG_IMG,           "href",         ATTR_INLINE },
+  { TAG_IMG,           "lowsrc",       ATTR_INLINE },
+  { TAG_IMG,           "src",          ATTR_INLINE },
+  { TAG_INPUT,         "src",          ATTR_INLINE },
+  { TAG_LAYER,         "src",          ATTR_INLINE | ATTR_HTML },
+  { TAG_OBJECT,                "data",         ATTR_INLINE },
+  { TAG_OVERLAY,       "src",          ATTR_INLINE | ATTR_HTML },
+  { TAG_SCRIPT,                "src",          ATTR_INLINE },
+  { TAG_TABLE,         "background",   ATTR_INLINE },
+  { TAG_TD,            "background",   ATTR_INLINE },
+  { TAG_TH,            "background",   ATTR_INLINE }
 };
 
 /* The lists of interesting tags and attributes are built dynamically,
    from the information above.  However, some places in the code refer
    to the attributes not mentioned here.  We add them manually.  */
 static const char *additional_attributes[] = {
-  "rel",                       /* used by tag_handle_link */
-  "http-equiv",                        /* used by tag_handle_meta */
-  "name",                      /* used by tag_handle_meta */
-  "content",                   /* used by tag_handle_meta */
-  "action"                     /* used by tag_handle_form */
+  "rel",                       /* used by tag_handle_link  */
+  "http-equiv",                        /* used by tag_handle_meta  */
+  "name",                      /* used by tag_handle_meta  */
+  "content",                   /* used by tag_handle_meta  */
+  "action",                    /* used by tag_handle_form  */
+  "style"                      /* used by check_style_attr */
 };
 
-struct hash_table *interesting_tags;
-struct hash_table *interesting_attributes;
+static struct hash_table *interesting_tags;
+static struct hash_table *interesting_attributes;
 
 static void
 init_interesting (void)
@@ -219,9 +223,10 @@ init_interesting (void)
   /* Add the attributes we care about. */
   interesting_attributes = make_nocase_string_hash_table (10);
   for (i = 0; i < countof (additional_attributes); i++)
-    string_set_add (interesting_attributes, additional_attributes[i]);
+    hash_table_put (interesting_attributes, additional_attributes[i], "1");
   for (i = 0; i < countof (tag_url_attributes); i++)
-    string_set_add (interesting_attributes, tag_url_attributes[i].attr_name);
+    hash_table_put (interesting_attributes,
+                   tag_url_attributes[i].attr_name, "1");
 }
 
 /* Find the value of attribute named NAME in the taginfo TAG.  If the
@@ -242,28 +247,20 @@ find_attr (struct taginfo *tag, const char *name, int *attrind)
   return NULL;
 }
 
-struct map_context {
-  char *text;                  /* HTML text. */
-  char *base;                  /* Base URI of the document, possibly
-                                  changed through <base href=...>. */
-  const char *parent_base;     /* Base of the current document. */
-  const char *document_file;   /* File name of this document. */
-  int nofollow;                        /* whether NOFOLLOW was specified in a
-                                   <meta name=robots> tag. */
-
-  struct urlpos *head, *tail;  /* List of URLs that is being
-                                  built. */
-};
+/* used for calls to append_url */
+#define ATTR_POS(tag, attrind, ctx) \
+ (tag->attrs[attrind].value_raw_beginning - ctx->text)
+#define ATTR_SIZE(tag, attrind) \
+ (tag->attrs[attrind].value_raw_size)
 
 /* Append LINK_URI to the urlpos structure that is being built.
 
-   LINK_URI will be merged with the current document base.  TAG and
-   ATTRIND are the necessary context to store the position and
-   size.  */
+   LINK_URI will be merged with the current document base.
+*/
 
-static struct urlpos *
-append_one_url (const char *link_uri, int inlinep,
-               struct taginfo *tag, int attrind, struct map_context *ctx)
+struct urlpos *
+append_url (const char *link_uri, int position, int size,
+            struct map_context *ctx)
 {
   int link_has_scheme = url_has_scheme (link_uri);
   struct urlpos *newel;
@@ -319,14 +316,10 @@ append_one_url (const char *link_uri, int inlinep,
 
   DEBUGP (("appending \"%s\" to urlpos.\n", url->url));
 
-  newel = (struct urlpos *)xmalloc (sizeof (struct urlpos));
-  memset (newel, 0, sizeof (*newel));
-
-  newel->next = NULL;
+  newel = xnew0 (struct urlpos);
   newel->url = url;
-  newel->pos = tag->attrs[attrind].value_raw_beginning - ctx->text;
-  newel->size = tag->attrs[attrind].value_raw_size;
-  newel->link_inline_p = inlinep;
+  newel->pos = position;
+  newel->size = size;
 
   /* A URL is relative if the host is not named, and the name does not
      start with `/'.  */
@@ -346,6 +339,18 @@ append_one_url (const char *link_uri, int inlinep,
   return newel;
 }
 \f
+static void
+check_style_attr (struct taginfo *tag, struct map_context *ctx)
+{
+  int attrind;
+  char *style = find_attr (tag, "style", &attrind);
+  if (!style)
+    return;
+
+  /* raw pos and raw size include the quotes, hence the +1 -2 */
+  get_urls_css (ctx, ATTR_POS(tag,attrind,ctx)+1, ATTR_SIZE(tag,attrind)-2);
+}
+
 /* All the tag_* functions are called from collect_tags_mapper, as
    specified by KNOWN_TAGS.  */
 
@@ -393,8 +398,16 @@ tag_find_urls (int tagid, struct taginfo *tag, struct map_context *ctx)
          if (0 == strcasecmp (tag->attrs[attrind].name,
                               tag_url_attributes[i].attr_name))
            {
-             int flags = tag_url_attributes[i].flags;
-             append_one_url (link, !(flags & TUA_EXTERNAL), tag, attrind, ctx);
+             struct urlpos *up = append_url (link, ATTR_POS(tag,attrind,ctx),
+                                              ATTR_SIZE(tag,attrind), ctx);
+             if (up)
+               {
+                 int flags = tag_url_attributes[i].flags;
+                 if (flags & ATTR_INLINE)
+                   up->link_inline_p = 1;
+                 if (flags & ATTR_HTML)
+                   up->link_expect_html = 1;
+               }
            }
        }
     }
@@ -411,7 +424,8 @@ tag_handle_base (int tagid, struct taginfo *tag, struct map_context *ctx)
   if (!newbase)
     return;
 
-  base_urlpos = append_one_url (newbase, 0, tag, attrind, ctx);
+  base_urlpos = append_url (newbase, ATTR_POS(tag,attrind,ctx),
+                            ATTR_SIZE(tag,attrind), ctx);
   if (!base_urlpos)
     return;
   base_urlpos->ignore_when_downloading = 1;
@@ -432,12 +446,13 @@ tag_handle_form (int tagid, struct taginfo *tag, struct map_context *ctx)
 {
   int attrind;
   char *action = find_attr (tag, "action", &attrind);
+
   if (action)
     {
-      struct urlpos *action_urlpos = append_one_url (action, 0, tag,
-                                                    attrind, ctx);
-      if (action_urlpos)
-       action_urlpos->ignore_when_downloading = 1;
+      struct urlpos *up = append_url (action, ATTR_POS(tag,attrind,ctx),
+                                      ATTR_SIZE(tag,attrind), ctx);
+      if (up)
+       up->ignore_when_downloading = 1;
     }
 }
 
@@ -458,11 +473,28 @@ tag_handle_link (int tagid, struct taginfo *tag, struct map_context *ctx)
   */
   if (href)
     {
-      char *rel  = find_attr (tag, "rel", NULL);
-      int inlinep = (rel
-                    && (0 == strcasecmp (rel, "stylesheet")
-                        || 0 == strcasecmp (rel, "shortcut icon")));
-      append_one_url (href, inlinep, tag, attrind, ctx);
+      struct urlpos *up = append_url (href, ATTR_POS(tag,attrind,ctx),
+                                      ATTR_SIZE(tag,attrind), ctx);
+      if (up)
+       {
+         char *rel = find_attr (tag, "rel", NULL);
+         if (rel)
+            {
+             if (0 == strcasecmp (rel, "stylesheet"))
+                {
+                  up->link_inline_p = 1;
+                  up->link_expect_css = 1;
+                }
+             else if (0 == strcasecmp (rel, "shortcut icon"))
+                {
+                  up->link_inline_p = 1;
+                }
+            }
+         else
+           /* The external ones usually point to HTML pages, such as
+              <link rel="next" href="..."> */
+           up->link_expect_html = 1;
+       }
     }
 }
 
@@ -511,11 +543,13 @@ tag_handle_meta (int tagid, struct taginfo *tag, struct map_context *ctx)
       while (ISSPACE (*p))
        ++p;
 
-      entry = append_one_url (p, 0, tag, attrind, ctx);
+      entry = append_url (p, ATTR_POS(tag,attrind,ctx),
+                          ATTR_SIZE(tag,attrind), ctx);
       if (entry)
        {
          entry->link_refresh_p = 1;
          entry->refresh_timeout = timeout;
+         entry->link_expect_html = 1;
        }
     }
   else if (name && 0 == strcasecmp (name, "robots"))
@@ -526,7 +560,7 @@ tag_handle_meta (int tagid, struct taginfo *tag, struct map_context *ctx)
       if (!content)
        return;
       if (!strcasecmp (content, "none"))
-       ctx->nofollow = 1;
+       ctx->nofollow = true;
       else
        {
          while (*content)
@@ -539,7 +573,7 @@ tag_handle_meta (int tagid, struct taginfo *tag, struct map_context *ctx)
              else
                end = content + strlen (content);
              if (!strncasecmp (content, "nofollow", end - content))
-               ctx->nofollow = 1;
+               ctx->nofollow = true;
              content = end;
            }
        }
@@ -555,11 +589,26 @@ collect_tags_mapper (struct taginfo *tag, void *arg)
   struct map_context *ctx = (struct map_context *)arg;
 
   /* Find the tag in our table of tags.  This must not fail because
-     map_html_tags only returns tags found in interesting_tags.  */
+     map_html_tags only returns tags found in interesting_tags.
+     
+     I've changed this for now, I'm passing NULL as interesting_tags
+     to map_html_tags.  This way we can check all tags for a style
+     attribute.
+  */
   struct known_tag *t = hash_table_get (interesting_tags, tag->name);
-  assert (t != NULL);
 
-  t->handler (t->tagid, tag, ctx);
+  if (t != NULL)
+    t->handler (t->tagid, tag, ctx);
+
+  check_style_attr (tag, ctx);
+
+  if (tag->end_tag_p && (0 == strcasecmp (tag->name, "style")) &&
+      tag->contents_begin && tag->contents_end)
+  {
+    /* parse contents */
+    get_urls_css (ctx, tag->contents_begin - ctx->text,
+                  tag->contents_end - tag->contents_begin);
+  }
 }
 \f
 /* Analyze HTML tags FILE and construct a list of URLs referenced from
@@ -567,7 +616,7 @@ collect_tags_mapper (struct taginfo *tag, void *arg)
    <base href=...> and does the right thing.  */
 
 struct urlpos *
-get_urls_html (const char *file, const char *url, int *meta_disallow_follow)
+get_urls_html (const char *file, const char *url, bool *meta_disallow_follow)
 {
   struct file_memory *fm;
   struct map_context ctx;
@@ -580,34 +629,38 @@ get_urls_html (const char *file, const char *url, int *meta_disallow_follow)
       logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
       return NULL;
     }
-  DEBUGP (("Loaded %s (size %ld).\n", file, fm->length));
+  DEBUGP (("Loaded %s (size %s).\n", file, number_to_static_string (fm->length)));
 
   ctx.text = fm->content;
   ctx.head = ctx.tail = NULL;
   ctx.base = NULL;
   ctx.parent_base = url ? url : opt.base_href;
   ctx.document_file = file;
-  ctx.nofollow = 0;
+  ctx.nofollow = false;
 
   if (!interesting_tags)
     init_interesting ();
 
   /* Specify MHT_TRIM_VALUES because of buggy HTML generators that
-     generate <a href=" foo"> instead of <a href="foo"> (Netscape
-     ignores spaces as well.)  If you really mean space, use &32; or
-     %20.  */
+     generate <a href=" foo"> instead of <a href="foo"> (browsers
+     ignore spaces as well.)  If you really mean space, use &32; or
+     %20.  MHT_TRIM_VALUES also causes squashing of embedded newlines,
+     e.g. in <img src="foo.[newline]html">.  Such newlines are also
+     ignored by IE and Mozilla and are presumably introduced by
+     writing HTML with editors that force word wrap.  */
   flags = MHT_TRIM_VALUES;
   if (opt.strict_comments)
     flags |= MHT_STRICT_COMMENTS;
 
+  /* the NULL here used to be interesting_tags */
   map_html_tags (fm->content, fm->length, collect_tags_mapper, &ctx, flags,
-                interesting_tags, interesting_attributes);
+                NULL, interesting_attributes);
 
   DEBUGP (("no-follow in %s: %d\n", file, ctx.nofollow));
   if (meta_disallow_follow)
     *meta_disallow_follow = ctx.nofollow;
 
-  FREE_MAYBE (ctx.base);
+  xfree_null (ctx.base);
   read_file_free (fm);
   return ctx.head;
 }
@@ -629,7 +682,7 @@ get_urls_file (const char *file)
       logprintf (LOG_NOTQUIET, "%s: %s\n", file, strerror (errno));
       return NULL;
     }
-  DEBUGP (("Loaded %s (size %ld).\n", file, fm->length));
+  DEBUGP (("Loaded %s (size %s).\n", file, number_to_static_string (fm->length)));
 
   head = tail = NULL;
   text = fm->content;
@@ -675,16 +728,14 @@ get_urls_file (const char *file)
       url = url_parse (url_text, &up_error_code);
       if (!url)
        {
-         logprintf (LOG_NOTQUIET, "%s: Invalid URL %s: %s\n",
+         logprintf (LOG_NOTQUIET, _("%s: Invalid URL %s: %s\n"),
                     file, url_text, url_error (up_error_code));
          xfree (url_text);
          continue;
        }
       xfree (url_text);
 
-      entry = (struct urlpos *)xmalloc (sizeof (struct urlpos));
-      memset (entry, 0, sizeof (*entry));
-      entry->next = NULL;
+      entry = xnew0 (struct urlpos);
       entry->url = url;
 
       if (!head)
@@ -700,6 +751,10 @@ get_urls_file (const char *file)
 void
 cleanup_html_url (void)
 {
-  FREE_MAYBE (interesting_tags);
-  FREE_MAYBE (interesting_attributes);
+  /* Destroy the hash tables.  The hash table keys and values are not
+     allocated by this code, so we don't need to free them here.  */
+  if (interesting_tags)
+    hash_table_destroy (interesting_tags);
+  if (interesting_attributes)
+    hash_table_destroy (interesting_attributes);
 }