X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Furl.c;h=48b23d6c649cb208e23933ef8aa08ee58d999815;hp=46060df5fc54db42c18912273a11d19e2c031ba9;hb=3b8b1b82d66a826d178b37b52dd9d158a5bb2ddc;hpb=86395b297b58c8c9b43d7ca8c42b831d4926dac6 diff --git a/src/url.c b/src/url.c index 46060df5..48b23d6c 100644 --- a/src/url.c +++ b/src/url.c @@ -1,6 +1,6 @@ /* URL handling. Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. This file is part of GNU Wget. @@ -28,7 +28,7 @@ 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 +#include "wget.h" #include #include @@ -39,10 +39,10 @@ as that of the covered work. */ #include #include -#include "wget.h" #include "utils.h" #include "url.h" #include "host.h" /* for is_valid_ipv6_address */ +#include "iri.h" #ifdef TESTING #include "test.h" @@ -82,7 +82,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. */ @@ -184,7 +184,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 @@ -273,7 +273,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 `%'. */ @@ -429,7 +429,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 @@ -591,10 +591,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; } @@ -671,6 +671,12 @@ url_parse (const char *url, int *error) goto error; } + if (opt.enable_iri) + { + url_unescape ((char *) url); + url = locale_to_utf8(url); + } + url_encoded = reencode_escapes (url); p = url_encoded; @@ -770,7 +776,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 */ /* ^ */ @@ -830,7 +836,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); @@ -845,6 +851,17 @@ url_parse (const char *url, int *error) host_modified = true; } + if (opt.enable_iri) + { + char *new = idn_encode (u->host); + if (new) + { + xfree (u->host); + u->host = new; + host_modified = true; + } + } + if (params_b) u->params = strdupdelim (params_b, params_e); if (query_b) @@ -852,7 +869,7 @@ url_parse (const char *url, int *error) if (fragment_b) u->fragment = strdupdelim (fragment_b, fragment_e); - if (path_modified || u->fragment || host_modified || path_b == path_e) + if (opt.enable_iri || path_modified || u->fragment || host_modified || path_b == path_e) { /* If we suspect that a transformation has rendered what url_string might return different from URL_ENCODED, rebuild @@ -890,7 +907,7 @@ url_parse (const char *url, int *error) const char * url_error (int error_code) { - assert (error_code >= 0 && error_code < countof (parse_errors)); + assert (error_code >= 0 && ((size_t) error_code) < countof (parse_errors)); return _(parse_errors[error_code]); } @@ -1222,7 +1239,7 @@ append_char (char ch, struct growable *dest) enum { filechr_not_unix = 1, /* unusable on Unix, / and \0 */ - filechr_not_windows = 2, /* unusable on MSDOS/Windows, one of \|/<>?:*" */ + filechr_not_windows = 2, /* unusable on Windows, one of \|/<>?:*" */ filechr_control = 4 /* a control character, e.g. 0-31 */ }; @@ -1374,9 +1391,9 @@ append_uri_pathel (const char *b, const char *e, bool escaped, 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); } } @@ -1527,10 +1544,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) @@ -1546,17 +1564,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. */ @@ -1941,7 +1971,7 @@ getchar_from_escaped_string (const char *str, char *c) if (p[0] == '%') { - if (!ISXDIGIT(p[1]) || !ISXDIGIT(p[2])) + if (!c_isxdigit(p[1]) || !c_isxdigit(p[2])) { *c = '%'; return 1; @@ -1983,7 +2013,7 @@ are_urls_equal (const char *u1, const char *u2) 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; @@ -1992,9 +2022,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 * @@ -2004,17 +2035,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) { @@ -2026,51 +2060,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()