X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Furl.c;h=3f4b89920fd2f5d2b37956f3c6f05ba0086051af;hp=e95d572ff05377e5b518aa4353675e882e6acd2a;hb=dc9abe478f8175d342f190993d0dc61891a7da8e;hpb=c17f57f1fa7d0583795ef7321a819c3fb8677a23 diff --git a/src/url.c b/src/url.c index e95d572f..3f4b8992 100644 --- a/src/url.c +++ b/src/url.c @@ -1,5 +1,6 @@ /* URL handling. - Copyright (C) 1996-2006 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -16,17 +17,18 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Wget. If not, see . -In addition, as a special exception, the Free Software Foundation -gives permission to link the code of its release of Wget with the -OpenSSL project's "OpenSSL" library (or with modified versions of it -that use the same license as the "OpenSSL" library), and distribute -the linked executables. You must obey the GNU General Public License -in all respects for all of the code used other than "OpenSSL". If you -modify this file, you may extend this exception to your version of the -file, but you are not obligated to do so. If you do not wish to do -so, delete this exception statement from your version. */ +Additional permission under GNU GPL version 3 section 7 -#include +If you modify this program, or any covered work, by linking or +combining it with the OpenSSL project's OpenSSL library (or a +modified version of that library), containing parts covered by the +terms of the OpenSSL or SSLeay licenses, the Free Software Foundation +grants you additional permission to convey the resulting work. +Corresponding Source for a non-source form of such a combination +shall include the source code for the parts of OpenSSL used as well +as that of the covered work. */ + +#include "wget.h" #include #include @@ -37,7 +39,6 @@ so, delete this exception statement from your version. */ #include #include -#include "wget.h" #include "utils.h" #include "url.h" #include "host.h" /* for is_valid_ipv6_address */ @@ -80,7 +81,7 @@ static struct scheme_data supported_schemes[] = /* Forward declarations: */ -static bool path_simplify (char *); +static bool path_simplify (enum url_scheme, char *); /* Support for escaping and unescaping of URL strings. */ @@ -182,7 +183,7 @@ url_unescape (char *s) { char c; /* Do nothing if '%' is not followed by two hex digits. */ - if (!h[1] || !h[2] || !(ISXDIGIT (h[1]) && ISXDIGIT (h[2]))) + if (!h[1] || !h[2] || !(c_isxdigit (h[1]) && c_isxdigit (h[2]))) goto copychar; c = X2DIGITS_TO_NUM (h[1], h[2]); /* Don't unescape %00 because there is no way to insert it @@ -271,7 +272,7 @@ char_needs_escaping (const char *p) { if (*p == '%') { - if (ISXDIGIT (*(p + 1)) && ISXDIGIT (*(p + 2))) + if (c_isxdigit (*(p + 1)) && c_isxdigit (*(p + 2))) return false; else /* Garbled %.. sequence: encode `%'. */ @@ -427,7 +428,7 @@ url_scheme (const char *url) return SCHEME_INVALID; } -#define SCHEME_CHAR(ch) (ISALNUM (ch) || (ch) == '-' || (ch) == '+') +#define SCHEME_CHAR(ch) (c_isalnum (ch) || (ch) == '-' || (ch) == '+') /* Return 1 if the URL begins with any "scheme", 0 otherwise. As currently implemented, it returns true if URL begins with @@ -589,10 +590,10 @@ lowercase_str (char *str) { bool changed = false; for (; *str; str++) - if (ISUPPER (*str)) + if (c_isupper (*str)) { changed = true; - *str = TOLOWER (*str); + *str = c_tolower (*str); } return changed; } @@ -618,7 +619,7 @@ static const char *parse_errors[] = { #define PE_NO_ERROR 0 N_("No error"), #define PE_UNSUPPORTED_SCHEME 1 - N_("Unsupported scheme"), + N_("Unsupported scheme %s"), #define PE_INVALID_HOST_NAME 2 N_("Invalid host name"), #define PE_BAD_PORT_NUMBER 3 @@ -768,7 +769,7 @@ url_parse (const char *url, int *error) if (port_b != port_e) for (port = 0, pp = port_b; pp < port_e; pp++) { - if (!ISDIGIT (*pp)) + if (!c_isdigit (*pp)) { /* http://host:12randomgarbage/blah */ /* ^ */ @@ -828,7 +829,7 @@ url_parse (const char *url, int *error) 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); @@ -885,11 +886,29 @@ url_parse (const char *url, int *error) /* Return the error message string from ERROR_CODE, which should have been retrieved from url_parse. The error message is translated. */ -const char * -url_error (int error_code) +char * +url_error (const char *url, int error_code) { - assert (error_code >= 0 && error_code < countof (parse_errors)); - return _(parse_errors[error_code]); + assert (error_code >= 0 && ((size_t) error_code) < countof (parse_errors)); + + if (error_code == PE_UNSUPPORTED_SCHEME) + { + char *error, *p; + char *scheme = xstrdup (url); + assert (url_has_scheme (url)); + + if ((p = strchr (scheme, ':'))) + *p = '\0'; + if (!strcasecmp (scheme, "https")) + asprintf (&error, _("HTTPS support not compiled in")); + else + asprintf (&error, _(parse_errors[error_code]), quote (scheme)); + xfree (scheme); + + return error; + } + else + return xstrdup (_(parse_errors[error_code])); } /* Split PATH into DIR and FILE. PATH comes from the URL and is @@ -1369,12 +1388,12 @@ append_uri_pathel (const char *b, const char *e, bool escaped, || opt.restrict_files_case == restrict_uppercase) { char *q; - for (q = TAIL (dest); *q; ++q) + for (q = TAIL (dest); q < TAIL (dest) + outlen; ++q) { if (opt.restrict_files_case == restrict_lowercase) - *q = TOLOWER (*q); + *q = c_tolower (*q); else - *q = TOUPPER (*q); + *q = c_toupper (*q); } } @@ -1429,11 +1448,17 @@ url_file_name (const struct url *u) const char *u_file, *u_query; char *fname, *unique; + char *index_filename = "index.html"; /* The default index file is index.html */ fnres.base = NULL; fnres.size = 0; fnres.tail = 0; + /* If an alternative index file was defined, change index_filename */ + if (opt.default_page) + index_filename = opt.default_page; + + /* Start with the directory prefix, if specified. */ if (opt.dir_prefix) append_string (opt.dir_prefix, &fnres); @@ -1475,7 +1500,7 @@ url_file_name (const struct url *u) /* Add the file name. */ if (fnres.tail) append_char ('/', &fnres); - u_file = *u->file ? u->file : "index.html"; + u_file = *u->file ? u->file : index_filename; append_uri_pathel (u_file, u_file + strlen (u_file), false, &fnres); /* Append "?query" to the file name. */ @@ -1525,10 +1550,11 @@ url_file_name (const struct url *u) 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 *beg = path; char *end = strchr (path, '\0'); while (h < end) @@ -1544,17 +1570,29 @@ path_simplify (char *path) { /* 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. */ - 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 { + 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. */ @@ -1836,7 +1874,7 @@ url_string (const struct url *url, enum url_auth_mode auth_mode) quoted_user = url_escape_allow_passthrough (url->user); if (url->passwd) { - if (auth_mode = URL_AUTH_HIDE_PASSWD) + if (auth_mode == URL_AUTH_HIDE_PASSWD) quoted_passwd = HIDDEN_PASSWORD; else quoted_passwd = url_escape_allow_passthrough (url->passwd); @@ -1939,10 +1977,7 @@ getchar_from_escaped_string (const char *str, char *c) if (p[0] == '%') { - if (p[1] == 0) - return 0; /* error: invalid string */ - - if (p[1] == '%') + if (!c_isxdigit(p[1]) || !c_isxdigit(p[2])) { *c = '%'; return 1; @@ -1953,8 +1988,13 @@ getchar_from_escaped_string (const char *str, char *c) return 0; /* error: invalid string */ *c = X2DIGITS_TO_NUM (p[1], p[2]); - - return 3; + if (URL_RESERVED_CHAR(*c)) + { + *c = '%'; + return 1; + } + else + return 3; } } else @@ -1971,14 +2011,15 @@ are_urls_equal (const char *u1, const char *u2) const char *p, *q; int pp, qq; char ch1, ch2; + assert(u1 && u2); p = u1; q = u2; - while (*p + while (*p && *q && (pp = getchar_from_escaped_string (p, &ch1)) && (qq = getchar_from_escaped_string (q, &ch2)) - && (TOLOWER(ch1) == TOLOWER(ch2))) + && (c_tolower(ch1) == c_tolower(ch2))) { p += pp; q += qq; @@ -1987,9 +2028,10 @@ are_urls_equal (const char *u1, const char *u2) return (*p == 0 && *q == 0 ? true : false); } -#if 0 +#ifdef TESTING /* 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 * @@ -1999,17 +2041,20 @@ ps (char *path) 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); - 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); + mu_assert ("", 0); } if (modified != expected_change) { @@ -2021,51 +2066,60 @@ run_test (char *test, char *expected_result, bool expected_change) test); } xfree (test_copy); + mu_assert ("", modified == expected_change); + return NULL; } -static void +const char * test_path_simplify (void) { static struct { char *test, *result; + enum url_scheme scheme; 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++) { + const char *message; 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; - run_test (test, expected_result, expected_change); + message = run_test (test, expected_result, scheme, expected_change); + if (message) return message; } + return NULL; } -#endif - -#ifdef TESTING const char * test_append_uri_pathel() @@ -2089,6 +2143,7 @@ test_append_uri_pathel() append_string (test_array[i].original_url, &dest); append_uri_pathel (p, p + strlen(p), test_array[i].escaped, &dest); + append_char ('\0', &dest); mu_assert ("test_append_uri_pathel: wrong result", strcmp (dest.base, test_array[i].expected_result) == 0); @@ -2110,6 +2165,8 @@ test_are_urls_equal() { "http://www.adomain.com/apath/", "http://www.adomain.com/anotherpath/", false }, { "http://www.adomain.com/apath/", "http://www.anotherdomain.com/path/", false }, { "http://www.adomain.com/~path/", "http://www.adomain.com/%7epath/", true }, + { "http://www.adomain.com/longer-path/", "http://www.adomain.com/path/", false }, + { "http://www.adomain.com/path%2f", "http://www.adomain.com/path/", false }, }; for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i)