]> sjero.net Git - wget/commitdiff
Add support for .. at beginning of paths back in, but only for FTP.
authorMicah Cowan <micah@cowan.name>
Sun, 27 Apr 2008 10:23:54 +0000 (03:23 -0700)
committerMicah Cowan <micah@cowan.name>
Sun, 27 Apr 2008 10:23:54 +0000 (03:23 -0700)
src/ChangeLog
src/test.c
src/url.c

index 24ab91756397920095bb4c24d49d34eaccab6b33..0b273f4568c86def9097da9395e4686047e4eb54 100644 (file)
@@ -1,5 +1,12 @@
 2008-04-27  Micah Cowan  <micah@cowan.name>
 
 2008-04-27  Micah Cowan  <micah@cowan.name>
 
+       * url.c (path_simplify): Go back to allowing leading ".." in
+       paths, but only for FTP URLs.
+       (test_path_simplify): Add scheme-specificness to tests, adapt for
+       mu_run_test.
+
+       * test.c (all_tests): Add test_path_simplify.
+
        * main.c (main): Downgrade -r, -p with -O to a warning rather than
        an error; elaborate just a bit more for other -O combination
        cases.
        * main.c (main): Downgrade -r, -p with -O to a warning rather than
        an error; elaborate just a bit more for other -O combination
        cases.
index 1c749130a3199d3d09847735b12eae558a49dc35..3187900db44c5da3c3f01746dcf69adb012b4434 100644 (file)
@@ -40,6 +40,7 @@ const char *test_subdir_p();
 const char *test_dir_matches_p();
 const char *test_commands_sorted();
 const char *test_cmd_spec_restrict_file_names();
 const char *test_dir_matches_p();
 const char *test_commands_sorted();
 const char *test_cmd_spec_restrict_file_names();
+const char *test_path_simplify ();
 const char *test_append_uri_pathel();
 const char *test_are_urls_equal();
 const char *test_is_robots_txt_url();
 const char *test_append_uri_pathel();
 const char *test_are_urls_equal();
 const char *test_is_robots_txt_url();
@@ -54,6 +55,7 @@ all_tests()
   mu_run_test (test_dir_matches_p);
   mu_run_test (test_commands_sorted);
   mu_run_test (test_cmd_spec_restrict_file_names);
   mu_run_test (test_dir_matches_p);
   mu_run_test (test_commands_sorted);
   mu_run_test (test_cmd_spec_restrict_file_names);
+  mu_run_test (test_path_simplify);
   mu_run_test (test_append_uri_pathel);
   mu_run_test (test_are_urls_equal);
   mu_run_test (test_is_robots_txt_url);
   mu_run_test (test_append_uri_pathel);
   mu_run_test (test_are_urls_equal);
   mu_run_test (test_is_robots_txt_url);
index a561725a200db5bebc75d5284bdbdb21130c7ff9..87ba3cc9dc3e848b6617bb2680f9f90d99d14a5a 100644 (file)
--- a/src/url.c
+++ b/src/url.c
@@ -81,7 +81,7 @@ static struct scheme_data supported_schemes[] =
 
 /* Forward declarations: */
 
 
 /* Forward declarations: */
 
-static bool path_simplify (char *);
+static bool path_simplify (enum url_scheme, char *);
 \f
 /* Support for escaping and unescaping of URL strings.  */
 
 \f
 /* Support for escaping and unescaping of URL strings.  */
 
@@ -829,7 +829,7 @@ url_parse (const char *url, int *error)
   u->passwd = passwd;
 
   u->path = strdupdelim (path_b, path_e);
   u->passwd = passwd;
 
   u->path = strdupdelim (path_b, path_e);
-  path_modified = path_simplify (u->path);
+  path_modified = path_simplify (scheme, u->path);
   split_path (u->path, &u->dir, &u->file);
 
   host_modified = lowercase_str (u->host);
   split_path (u->path, &u->dir, &u->file);
 
   host_modified = lowercase_str (u->host);
@@ -1526,10 +1526,11 @@ url_file_name (const struct url *u)
    test case.  */
 
 static bool
    test case.  */
 
 static bool
-path_simplify (char *path)
+path_simplify (enum url_scheme scheme, char *path)
 {
   char *h = path;               /* hare */
   char *t = path;               /* tortoise */
 {
   char *h = path;               /* hare */
   char *t = path;               /* tortoise */
+  char *beg = path;
   char *end = strchr (path, '\0');
 
   while (h < end)
   char *end = strchr (path, '\0');
 
   while (h < end)
@@ -1545,17 +1546,29 @@ path_simplify (char *path)
         {
           /* Handle "../" by retreating the tortoise by one path
              element -- but not past beggining.  */
         {
           /* Handle "../" by retreating the tortoise by one path
              element -- but not past beggining.  */
-          if (t > path)
+          if (t > beg)
             {
               /* Move backwards until T hits the beginning of the
                  previous path element or the beginning of path. */
             {
               /* Move backwards until T hits the beginning of the
                  previous path element or the beginning of path. */
-              for (--t; t > path && t[-1] != '/'; t--)
+              for (--t; t > beg && t[-1] != '/'; t--)
                 ;
             }
                 ;
             }
+          else if (scheme == SCHEME_FTP)
+            {
+              /* If we're at the beginning, copy the "../" literally
+                 and move the beginning so a later ".." doesn't remove
+                 it.  This violates RFC 3986; but we do it for FTP
+                 anyway because there is otherwise no way to get at a
+                 parent directory, when the FTP server drops us in a
+                 non-root directory (which is not uncommon). */
+              beg = t + 3;
+              goto regular;
+            }
           h += 3;
         }
       else
         {
           h += 3;
         }
       else
         {
+        regular:
           /* A regular path element.  If H hasn't advanced past T,
              simply skip to the next path element.  Otherwise, copy
              the path element until the next slash.  */
           /* A regular path element.  If H hasn't advanced past T,
              simply skip to the next path element.  Otherwise, copy
              the path element until the next slash.  */
@@ -1991,9 +2004,10 @@ are_urls_equal (const char *u1, const char *u2)
   return (*p == 0 && *q == 0 ? true : false);
 }
 \f
   return (*p == 0 && *q == 0 ? true : false);
 }
 \f
-#if 0
+#ifdef TESTING
 /* Debugging and testing support for path_simplify. */
 
 /* Debugging and testing support for path_simplify. */
 
+#if 0
 /* Debug: run path_simplify on PATH and return the result in a new
    string.  Useful for calling from the debugger.  */
 static char *
 /* Debug: run path_simplify on PATH and return the result in a new
    string.  Useful for calling from the debugger.  */
 static char *
@@ -2003,17 +2017,20 @@ ps (char *path)
   path_simplify (copy);
   return copy;
 }
   path_simplify (copy);
   return copy;
 }
+#endif
 
 
-static void
-run_test (char *test, char *expected_result, bool expected_change)
+static const char *
+run_test (char *test, char *expected_result, enum url_scheme scheme,
+          bool expected_change)
 {
   char *test_copy = xstrdup (test);
 {
   char *test_copy = xstrdup (test);
-  bool modified = path_simplify (test_copy);
+  bool modified = path_simplify (scheme, test_copy);
 
   if (0 != strcmp (test_copy, expected_result))
     {
       printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
               test, expected_result, test_copy);
 
   if (0 != strcmp (test_copy, expected_result))
     {
       printf ("Failed path_simplify(\"%s\"): expected \"%s\", got \"%s\".\n",
               test, expected_result, test_copy);
+      mu_assert ("", 0);
     }
   if (modified != expected_change)
     {
     }
   if (modified != expected_change)
     {
@@ -2025,51 +2042,60 @@ run_test (char *test, char *expected_result, bool expected_change)
                 test);
     }
   xfree (test_copy);
                 test);
     }
   xfree (test_copy);
+  mu_assert ("", modified == expected_change);
+  return NULL;
 }
 
 }
 
-static void
+const char *
 test_path_simplify (void)
 {
   static struct {
     char *test, *result;
 test_path_simplify (void)
 {
   static struct {
     char *test, *result;
+    enum url_scheme scheme;
     bool should_modify;
   } tests[] = {
     bool should_modify;
   } tests[] = {
-    { "",                       "",             false },
-    { ".",                      "",             true },
-    { "./",                     "",             true },
-    { "..",                     "",             true },
-    { "../",                    "",             true },
-    { "foo",                    "foo",          false },
-    { "foo/bar",                "foo/bar",      false },
-    { "foo///bar",              "foo///bar",    false },
-    { "foo/.",                  "foo/",         true },
-    { "foo/./",                 "foo/",         true },
-    { "foo./",                  "foo./",        false },
-    { "foo/../bar",             "bar",          true },
-    { "foo/../bar/",            "bar/",         true },
-    { "foo/bar/..",             "foo/",         true },
-    { "foo/bar/../x",           "foo/x",        true },
-    { "foo/bar/../x/",          "foo/x/",       true },
-    { "foo/..",                 "",             true },
-    { "foo/../..",              "",             true },
-    { "foo/../../..",           "",             true },
-    { "foo/../../bar/../../baz", "baz",         true },
-    { "a/b/../../c",            "c",            true },
-    { "./a/../b",               "b",            true }
+    { "",                       "",             SCHEME_HTTP, false },
+    { ".",                      "",             SCHEME_HTTP, true },
+    { "./",                     "",             SCHEME_HTTP, true },
+    { "..",                     "",             SCHEME_HTTP, true },
+    { "../",                    "",             SCHEME_HTTP, true },
+    { "..",                     "..",           SCHEME_FTP,  false },
+    { "../",                    "../",          SCHEME_FTP,  false },
+    { "foo",                    "foo",          SCHEME_HTTP, false },
+    { "foo/bar",                "foo/bar",      SCHEME_HTTP, false },
+    { "foo///bar",              "foo///bar",    SCHEME_HTTP, false },
+    { "foo/.",                  "foo/",         SCHEME_HTTP, true },
+    { "foo/./",                 "foo/",         SCHEME_HTTP, true },
+    { "foo./",                  "foo./",        SCHEME_HTTP, false },
+    { "foo/../bar",             "bar",          SCHEME_HTTP, true },
+    { "foo/../bar/",            "bar/",         SCHEME_HTTP, true },
+    { "foo/bar/..",             "foo/",         SCHEME_HTTP, true },
+    { "foo/bar/../x",           "foo/x",        SCHEME_HTTP, true },
+    { "foo/bar/../x/",          "foo/x/",       SCHEME_HTTP, true },
+    { "foo/..",                 "",             SCHEME_HTTP, true },
+    { "foo/../..",              "",             SCHEME_HTTP, true },
+    { "foo/../../..",           "",             SCHEME_HTTP, true },
+    { "foo/../../bar/../../baz", "baz",         SCHEME_HTTP, true },
+    { "foo/../..",              "..",           SCHEME_FTP,  true },
+    { "foo/../../..",           "../..",        SCHEME_FTP,  true },
+    { "foo/../../bar/../../baz", "../../baz",   SCHEME_FTP,  true },
+    { "a/b/../../c",            "c",            SCHEME_HTTP, true },
+    { "./a/../b",               "b",            SCHEME_HTTP, true }
   };
   int i;
 
   for (i = 0; i < countof (tests); i++)
     {
   };
   int i;
 
   for (i = 0; i < countof (tests); i++)
     {
+      const char *message;
       char *test = tests[i].test;
       char *expected_result = tests[i].result;
       char *test = tests[i].test;
       char *expected_result = tests[i].result;
+      enum url_scheme scheme = tests[i].scheme;
       bool  expected_change = tests[i].should_modify;
       bool  expected_change = tests[i].should_modify;
-      run_test (test, expected_result, expected_change);
+      message = run_test (test, expected_result, scheme, expected_change);
+      if (message) return message;
     }
     }
+  return NULL;
 }
 }
-#endif
-\f
-#ifdef TESTING
 
 const char *
 test_append_uri_pathel()
 
 const char *
 test_append_uri_pathel()