X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Furl.c;h=d416fcf7fae38e6ee5b19276773ace45425d41a5;hb=766df9d4e9392045a4e5c730ed81e599b509557a;hp=4a9b69e40e02baed5d711b3415750dab6edd6827;hpb=1c7493b83ed8cecbbf1f70ef6bf834f94c5fcd43;p=wget diff --git a/src/url.c b/src/url.c index 4a9b69e4..d416fcf7 100644 --- a/src/url.c +++ b/src/url.c @@ -1,11 +1,12 @@ /* 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. GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or (at +the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, @@ -14,20 +15,20 @@ 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., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +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 @@ -38,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 */ @@ -81,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. */ @@ -183,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 @@ -252,6 +252,15 @@ url_escape (const char *s) return url_escape_1 (s, urlchr_unsafe, false); } +/* URL-escape the unsafe and reserved characters (see urlchr_table) in + a given string, returning a freshly allocated string. */ + +char * +url_escape_unsafe_and_reserved (const char *s) +{ + return url_escape_1 (s, urlchr_unsafe|urlchr_reserved, false); +} + /* URL-escape the unsafe characters (see urlchr_table) in a given string. If no characters are unsafe, S is returned. */ @@ -272,7 +281,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 `%'. */ @@ -428,7 +437,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 @@ -590,10 +599,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; } @@ -619,7 +628,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 @@ -769,7 +778,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 */ /* ^ */ @@ -829,7 +838,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); @@ -856,7 +865,7 @@ url_parse (const char *url, int *error) /* If we suspect that a transformation has rendered what url_string might return different from URL_ENCODED, rebuild u->url using url_string. */ - u->url = url_string (u, false); + u->url = url_string (u, URL_AUTH_SHOW); if (url_encoded != url) xfree ((char *) url_encoded); @@ -886,11 +895,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")) + error = aprintf (_("HTTPS support not compiled in")); + else + error = aprintf (_(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 @@ -1072,7 +1099,7 @@ sync_path (struct url *u) /* Regenerate u->url as well. */ xfree (u->url); - u->url = url_string (u, false); + u->url = url_string (u, URL_AUTH_SHOW); } /* Mutators. Code in ftp.c insists on changing u->dir and u->file. @@ -1370,12 +1397,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); } } @@ -1430,11 +1457,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); @@ -1476,7 +1509,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. */ @@ -1526,10 +1559,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) @@ -1545,17 +1579,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. */ @@ -1815,7 +1861,7 @@ uri_merge (const char *base, const char *link) the URL will be quoted. */ char * -url_string (const struct url *url, bool hide_password) +url_string (const struct url *url, enum url_auth_mode auth_mode) { int size; char *result, *p; @@ -1832,13 +1878,16 @@ url_string (const struct url *url, bool hide_password) /* Make sure the user name and password are quoted. */ if (url->user) { - quoted_user = url_escape_allow_passthrough (url->user); - if (url->passwd) + if (auth_mode != URL_AUTH_HIDE) { - if (hide_password) - quoted_passwd = HIDDEN_PASSWORD; - else - quoted_passwd = url_escape_allow_passthrough (url->passwd); + quoted_user = url_escape_allow_passthrough (url->user); + if (url->passwd) + { + if (auth_mode == URL_AUTH_HIDE_PASSWD) + quoted_passwd = HIDDEN_PASSWORD; + else + quoted_passwd = url_escape_allow_passthrough (url->passwd); + } } } @@ -1900,7 +1949,8 @@ url_string (const struct url *url, bool hide_password) if (quoted_user && quoted_user != url->user) xfree (quoted_user); - if (quoted_passwd && !hide_password && quoted_passwd != url->passwd) + if (quoted_passwd && auth_mode == URL_AUTH_SHOW + && quoted_passwd != url->passwd) xfree (quoted_passwd); if (quoted_host != url->host) xfree (quoted_host); @@ -1936,10 +1986,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; @@ -1950,8 +1997,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 @@ -1968,14 +2020,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; @@ -1984,9 +2037,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 * @@ -1996,17 +2050,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) { @@ -2018,51 +2075,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() @@ -2086,6 +2152,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); @@ -2107,6 +2174,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)