The transformation is done in place. If you need the original
string intact, make a copy before calling this function. */
-static void
+void
url_unescape (char *s)
{
char *t = s; /* t - tortoise */
if (!iri->utf8_encode)
new_url = NULL;
else
- iri->orig_url = xstrdup (url);
+ {
+ iri->orig_url = xstrdup (url);
+ percent_encode = true;
+ }
}
/* XXX XXX Could that change introduce (security) bugs ??? XXX XXX*/
The idea is to have a convenient and efficient way to construct a
string by having various functions append data to it. Instead of
passing the obligatory BASEVAR, SIZEVAR and TAILPOS to all the
- functions in questions, we pass the pointer to this struct. */
+ functions in questions, we pass the pointer to this struct.
+
+ Functions that write to the members in this struct must make sure
+ that base remains null terminated by calling append_null().
+ */
struct growable {
char *base;
- int size;
- int tail;
+ int size; /* memory allocated */
+ int tail; /* string length */
};
/* Ensure that the string can accept APPEND_COUNT more characters past
/* Move the tail position by APPEND_COUNT characters. */
#define TAIL_INCR(r, append_count) ((r)->tail += append_count)
-/* Append the string STR to DEST. NOTICE: the string in DEST is not
- terminated. */
+/* Append NULL to DEST. */
static void
-append_string (const char *str, struct growable *dest)
+append_null (struct growable *dest)
{
- int l = strlen (str);
- GROW (dest, l);
- memcpy (TAIL (dest), str, l);
- TAIL_INCR (dest, l);
+ GROW (dest, 1);
+ *TAIL (dest) = 0;
}
-/* Append CH to DEST. For example, append_char (0, DEST)
- zero-terminates DEST. */
-
+/* Append CH to DEST. */
static void
append_char (char ch, struct growable *dest)
{
- GROW (dest, 1);
- *TAIL (dest) = ch;
- TAIL_INCR (dest, 1);
+ if (ch)
+ {
+ GROW (dest, 1);
+ *TAIL (dest) = ch;
+ TAIL_INCR (dest, 1);
+ }
+
+ append_null (dest);
}
+/* Append the string STR to DEST. */
+static void
+append_string (const char *str, struct growable *dest)
+{
+ int l = strlen (str);
+
+ if (l)
+ {
+ GROW (dest, l);
+ memcpy (TAIL (dest), str, l);
+ TAIL_INCR (dest, l);
+ }
+
+ append_null (dest);
+}
+
+
enum {
filechr_not_unix = 1, /* unusable on Unix, / and \0 */
filechr_not_windows = 2, /* unusable on Windows, one of \|/<>?:*" */
query, normally '?'. Since Windows cannot handle '?' as part of
file name, we use '@' instead there. */
#define FN_QUERY_SEP (opt.restrict_files_os != restrict_windows ? '?' : '@')
+#define FN_QUERY_SEP_STR (opt.restrict_files_os != restrict_windows ? "?" : "@")
/* Quote path element, characters in [b, e), as file name, and append
the quoted string to DEST. Each character is quoted as per
}
TAIL_INCR (dest, outlen);
+ append_null (dest);
}
/* Append to DEST the directory structure that corresponds the
}
}
-/* Return a unique file name that matches the given URL as good as
+/* Return a unique file name that matches the given URL as well as
possible. Does not create directories on the file system. */
char *
url_file_name (const struct url *u, char *replaced_filename)
{
struct growable fnres; /* stands for "file name result" */
+ struct growable temp_fnres;
const char *u_file;
- char *fname, *unique;
+ char *fname, *unique, *fname_len_check;
const char *index_filename = "index.html"; /* The default index file is index.html */
+ size_t max_length;
fnres.base = NULL;
fnres.size = 0;
fnres.tail = 0;
+ temp_fnres.base = NULL;
+ temp_fnres.size = 0;
+ temp_fnres.tail = 0;
+
/* If an alternative index file was defined, change index_filename */
if (opt.default_page)
index_filename = opt.default_page;
if (!replaced_filename)
{
- /* Add the file name. */
- if (fnres.tail)
- append_char ('/', &fnres);
+ /* Create the filename. */
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, even if empty */
+ /* Append "?query" to the file name, even if empty,
+ * and create fname_len_check. */
if (u->query)
- {
- append_char (FN_QUERY_SEP, &fnres);
- append_uri_pathel (u->query, u->query + strlen (u->query),
- true, &fnres);
- }
+ fname_len_check = concat_strings (u_file, FN_QUERY_SEP_STR, u->query, NULL);
+ else
+ fname_len_check = strdupdelim (u_file, u_file + strlen (u_file));
}
else
{
- if (fnres.tail)
- append_char ('/', &fnres);
u_file = replaced_filename;
- append_uri_pathel (u_file, u_file + strlen (u_file), false, &fnres);
+ fname_len_check = strdupdelim (u_file, u_file + strlen (u_file));
}
- /* Zero-terminate the file name. */
- append_char ('\0', &fnres);
+ append_uri_pathel (fname_len_check,
+ fname_len_check + strlen (fname_len_check), false, &temp_fnres);
+
+ /* Zero-terminate the temporary file name. */
+ append_char ('\0', &temp_fnres);
+
+ /* Check that the length of the file name is acceptable. */
+#ifdef WINDOWS
+ if (MAX_PATH > (fnres.tail + CHOMP_BUFFER + 2))
+ {
+ max_length = MAX_PATH - (fnres.tail + CHOMP_BUFFER + 2);
+ /* FIXME: In Windows a filename is usually limited to 255 characters.
+ To really be accurate you could call GetVolumeInformation() to get
+ lpMaximumComponentLength
+ */
+ if (max_length > 255)
+ {
+ max_length = 255;
+ }
+ }
+ else
+ {
+ max_length = 0;
+ }
+#else
+ max_length = get_max_length (fnres.base, fnres.tail, _PC_NAME_MAX) - CHOMP_BUFFER;
+#endif
+ if (max_length > 0 && strlen (temp_fnres.base) > max_length)
+ {
+ logprintf (LOG_NOTQUIET, "The name is too long, %lu chars total.\n",
+ (unsigned long) strlen (temp_fnres.base));
+ logprintf (LOG_NOTQUIET, "Trying to shorten...\n");
+
+ /* Shorten the file name. */
+ temp_fnres.base[max_length] = '\0';
+
+ logprintf (LOG_NOTQUIET, "New name is %s.\n", temp_fnres.base);
+ }
+
+ free (fname_len_check);
+
+ /* The filename has already been 'cleaned' by append_uri_pathel() above. So,
+ * just append it. */
+ if (fnres.tail)
+ append_char ('/', &fnres);
+ append_string (temp_fnres.base, &fnres);
fname = fnres.base;
+ /* Make a final check that the path length is acceptable? */
+ /* TODO: check fnres.base for path length problem */
+
+ free (temp_fnres.base);
+
/* Check the cases in which the unique extensions are not used:
1) Clobbering is turned off (-nc).
2) Retrieval with regetting.
3) Timestamping is used.
4) Hierarchy is built.
+ 5) Backups are specified.
The exception is the case when file does exist and is a
directory (see `mkalldirs' for explanation). */
- if ((opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct)
+ if (ALLOW_CLOBBER
&& !(file_exists_p (fname) && !file_non_directory_p (fname)))
{
unique = fname;
if (url->passwd)
{
if (auth_mode == URL_AUTH_HIDE_PASSWD)
- quoted_passwd = HIDDEN_PASSWORD;
+ quoted_passwd = (char *) HIDDEN_PASSWORD;
else
quoted_passwd = url_escape_allow_passthrough (url->passwd);
}
#endif
static const char *
-run_test (char *test, char *expected_result, enum url_scheme scheme,
+run_test (const char *test, const char *expected_result, enum url_scheme scheme,
bool expected_change)
{
char *test_copy = xstrdup (test);
const char *
test_path_simplify (void)
{
- static struct {
- char *test, *result;
+ static const struct {
+ const char *test, *result;
enum url_scheme scheme;
bool should_modify;
} tests[] = {
{ "a/b/../../c", "c", SCHEME_HTTP, true },
{ "./a/../b", "b", SCHEME_HTTP, true }
};
- int i;
+ unsigned i;
for (i = 0; i < countof (tests); i++)
{
const char *message;
- char *test = tests[i].test;
- char *expected_result = tests[i].result;
+ const char *test = tests[i].test;
+ const char *expected_result = tests[i].result;
enum url_scheme scheme = tests[i].scheme;
bool expected_change = tests[i].should_modify;
+
message = run_test (test, expected_result, scheme, expected_change);
if (message) return message;
}
}
const char *
-test_append_uri_pathel()
+test_append_uri_pathel(void)
{
- int i;
- struct {
- char *original_url;
- char *input;
+ unsigned i;
+ static const struct {
+ const char *original_url;
+ const char *input;
bool escaped;
- char *expected_result;
+ const char *expected_result;
} test_array[] = {
{ "http://www.yoyodyne.com/path/", "somepage.html", false, "http://www.yoyodyne.com/path/somepage.html" },
};
- for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i)
+ for (i = 0; i < countof(test_array); ++i)
{
struct growable dest;
const char *p = test_array[i].input;
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);
return NULL;
}
-const char*
-test_are_urls_equal()
+const char *
+test_are_urls_equal(void)
{
- int i;
- struct {
- char *url1;
- char *url2;
+ unsigned i;
+ static const struct {
+ const char *url1;
+ const char *url2;
bool expected_result;
} test_array[] = {
{ "http://www.adomain.com/apath/", "http://www.adomain.com/apath/", true },
{ "http://www.adomain.com/path%2f", "http://www.adomain.com/path/", false },
};
- for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i)
+ for (i = 0; i < countof(test_array); ++i)
{
mu_assert ("test_are_urls_equal: wrong result",
are_urls_equal (test_array[i].url1, test_array[i].url2) == test_array[i].expected_result);