]> sjero.net Git - wget/commitdiff
[svn] Parse Content-Disposition better. Implement memrchr where missing.
authorhniksic <devnull@localhost>
Tue, 28 Feb 2006 19:27:52 +0000 (11:27 -0800)
committerhniksic <devnull@localhost>
Tue, 28 Feb 2006 19:27:52 +0000 (11:27 -0800)
ChangeLog
configure.in
src/ChangeLog
src/cmpt.c
src/http.c
src/sysdep.h
src/url.c

index 56ee8ac436da9715926b9dae224cf15501fdceea..a2ba9aa51b6eb02d831c00576f4af04bd0564e3d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2006-02-28  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * configure.in: Check for memrchr.
+
 2005-11-19  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * configure.in: Check for uintptr_t.
index 413309e1624fceb0c7e59cb09c37046750e6bf96..436efc9958431581242faa9a68334d07215849e8 100644 (file)
@@ -206,7 +206,7 @@ AC_FUNC_ALLOCA
 AC_FUNC_MMAP
 AC_FUNC_FSEEKO
 AC_CHECK_FUNCS(strptime timegm snprintf vsnprintf vasprintf drand48)
-AC_CHECK_FUNCS(strtoll strtoimax usleep ftello sigblock sigsetjmp)
+AC_CHECK_FUNCS(strtoll strtoimax usleep ftello sigblock sigsetjmp memrchr)
 
 dnl We expect to have these functions on Unix-like systems configure
 dnl runs on.  The defines are provided to get them in config.h.in so
index 46a550feb94cc834cdde767369f91389e84905aa..54452b5bb394a5069f5e8227376886e2fd21bbba 100644 (file)
@@ -1,3 +1,14 @@
+2006-02-28  Hrvoje Niksic  <hniksic@xemacs.org>
+
+       * url.c (find_last_char): Define in terms of memrchr.
+
+       * cmpt.c (memrchr): Define it on systems that don't have it.
+
+       * http.c (extract_param): New function for parsing header values
+       with parameters.
+       (parse_content_disposition): Use it.  Don't allow slashes and
+       backslashes in the file name.
+
 2006-02-27  Hrvoje Niksic  <hniksic@xemacs.org>
 
        * url.c (path_simplify): Don't preserve ".." at beginning of path.
index 03ff3ceb64d61b27e8008f170662014d942886b1..d1cff3b7bdfdab6e9ff27c3786dd821dfa6bff48 100644 (file)
@@ -111,10 +111,28 @@ strncasecmp (const char *s1, const char *s2, size_t n)
   return c1 - c2;
 }
 #endif /* not HAVE_STRNCASECMP */
+
+#ifndef HAVE_MEMRCHR
+/* memrchr is a GNU extension.  It is like the memchr function, except
+   that it searches backwards from the end of the n bytes pointed to
+   by s instead of forwards from the front.  */
+
+void *
+memrchr (const void *s, int c, size_t n)
+{
+  const char *b = s;
+  const char *e = b + n;
+  while (e > b)
+    if (*--e == c)
+      return (void *) e;
+  return NULL;
+}
+#endif
 \f
 /* strptime is required by POSIX, but it is missing from Windows,
    which means we must keep a fallback implementation.  It is
-   reportedly missing or broken on many older systems as well.  */
+   reportedly missing or broken on many older Unix systems as well, so
+   it's good to have around.  */
 
 #ifndef HAVE_STRPTIME
 /* From GNU libc 2.1.3.  */
index c5827275896fce8283e5e9b42e70e40e3253d22e..9668d5043b1bebddba547815a9581d61f1655aa5 100644 (file)
@@ -894,37 +894,101 @@ extract_param_value_delim (const char *begin, const char *end,
   return false;
 }
 
-/* Parse the `Content-Disposition' header and extract the information it
-   contains.  Returns true if successful, false otherwise.  */
-static bool
-parse_content_disposition (const char *hdrval, char **filename)
-{
-  const char *b = hdrval; /* b - begin */
-  const char *e = hdrval; /* e - end   */
+typedef struct {
+  /* A token consists of characters in the [b, e) range. */
+  const char *b, *e;
+} param_token;
 
-  assert (hdrval);
-  assert (filename);
+/* Extract a parameter from the HTTP header at *SOURCE and advance
+   *SOURCE to the next parameter.  Return false when there are no more
+   parameters to extract.  The name of the parameter is returned in
+   NAME, and the value in VALUE.  If the parameter has no value, the
+   token's value is zeroed out.
 
-  for (; *e; ++e)
-    {
-      if (*e == ';'
-          && e > b)
-        {           
-          /* process chars b->e-1 */
-          if (true == extract_param_value_delim (b, e - 1, "filename", filename)) 
-            return true;
+   For example, if *SOURCE points to the string "attachment;
+   filename=\"foo bar\"", the first call to this function will return
+   the token named "attachment" and no value, and the second call will
+   return the token named "filename" and value "foo bar".  The third
+   call will return false, indicating no more valid tokens.  */
 
-          b = e + 1;
-        }      
+static bool
+extract_param (const char **source, param_token *name, param_token *value)
+{
+  const char *p = *source;
+
+  while (ISSPACE (*p)) ++p;
+  if (!*p)
+    return false;              /* nothing more to extract */
+
+  /* Extract name. */
+  name->b = p;
+  while (*p && !ISSPACE (*p) && *p != '=' && *p != ';') ++p;
+  name->e = p;
+  while (ISSPACE (*p)) ++p;
+  if (*p == ';' || !*p)                /* no value */
+    {
+      xzero (*value);
+      if (*p == ';') ++p;
+      *source = p;
+      return true;
     }
+  if (*p != '=')
+    return false;              /* error */
 
-  if (b != e)
+  /* *p is '=', extract value */
+  ++p;
+  while (ISSPACE (*p)) ++p;
+  if (*p == '"')               /* quoted */
     {
-      /* process chars b->e */
-      if (true == extract_param_value_delim (b, e, "filename", filename)) 
-        return true;
-    }
+      value->b = ++p;
+      while (*p && *p != '"') ++p;
+      if (!*p)
+        return false;
+      value->e = p++;
+      /* Currently at closing quote; find the end of param. */
+      while (ISSPACE (*p)) ++p;
+      while (*p && *p != ';') ++p;
+      if (*p == ';')
+       ++p;
+      else if (*p)
+       /* garbage after closed quote, e.g. foo="bar"baz */
+       return false;
+    }
+  else                         /* unquoted */
+    {
+      value->b = p;
+      while (*p && *p != ';') ++p;
+      value->e = p;
+      while (value->e != value->b && ISSPACE (value->e[-1]))
+        --value->e;
+      if (*p == ';') ++p;
+    }
+  *source = p;
+  return true;
+}
+
+#undef MAX
+#define MAX(p, q) ((p) > (q) ? (p) : (q))
 
+static bool
+parse_content_disposition (const char *hdr, char **filename)
+{
+  param_token name, value;
+  while (extract_param (&hdr, &name, &value))
+    if (BOUNDED_EQUAL_NO_CASE (name.b, name.e, "filename") && value.b != NULL)
+      {
+       /* Make the file name begin at the last slash or backslash. */
+        const char *last_slash = memrchr (value.b, '/', value.e - value.b);
+        const char *last_bs = memrchr (value.b, '\\', value.e - value.b);
+        if (last_slash && last_bs)
+          value.b = 1 + MAX (last_slash, last_bs);
+        else if (last_slash || last_bs)
+          value.b = 1 + (last_slash ? last_slash : last_bs);
+       if (value.b == value.e)
+         continue;
+        *filename = strdupdelim (value.b, value.e);
+        return true;
+      }
   return false;
 }
 \f
@@ -1687,7 +1751,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     {
       /* Honor Content-Disposition whether possible. */
       if (!resp_header_copy (resp, "Content-Disposition", hdrval, sizeof (hdrval))
-          || false == parse_content_disposition (hdrval, &hs->local_file))
+          || !parse_content_disposition (hdrval, &hs->local_file))
         {
           /* Choose filename according to URL name. */
           hs->local_file = url_file_name (u);
index 50c0d14dbb7467ad95bb4e2749c70ff8a041f841..38963901b1a73cb13ea640efda59d99348784178 100644 (file)
@@ -135,6 +135,9 @@ char *strptime ();
 # include <time.h>
 time_t timegm (struct tm *);
 #endif
+#ifndef HAVE_MEMRCHR
+void *memrchr (const void *, int, size_t);
+#endif
 
 /* These are defined in snprintf.c.  It would be nice to have an
    snprintf.h, though.  */
index f97a31801ea5a6861fccc47b4a1d4d60632bf6b0..30828c2a4b291f0cd76db7b1aab8daacbb20364a 100644 (file)
--- a/src/url.c
+++ b/src/url.c
@@ -1582,17 +1582,8 @@ path_end (const char *url)
 }
 
 /* Find the last occurrence of character C in the range [b, e), or
-   NULL, if none are present.  We might want to use memrchr (a GNU
-   extension) under GNU libc.  */
-
-static const char *
-find_last_char (const char *b, const char *e, char c)
-{
-  for (; e > b; e--)
-    if (*e == c)
-      return e;
-  return NULL;
-}
+   NULL, if none are present.  */
+#define find_last_char(b, e, c) memrchr ((b), (c), (e) - (b))
 
 /* Merge BASE with LINK and return the resulting URI.