From c1c58c9fc79562b0da4ceeeb3fc378407f8b9c85 Mon Sep 17 00:00:00 2001 From: Zan Dobersek Date: Thu, 7 Mar 2013 21:23:15 +0100 Subject: [PATCH] data: URL requests should serve the whole decoded URL Address the possibility of data: URLs containing null characters when the data request is being performed. The uri_decoded_copy method is enhanced with a third argument, a pointer to an integer that should be set to the length of decoded data when provided. This length is then set as the request's content length. A test checking the correct behavior is added in requester-test. Calls to uri_decoded_copy where the length of the decoded output is not required are adjusted to provide NULL as the third argument. --- libsoup/soup-misc-private.h | 2 +- libsoup/soup-request-data.c | 7 ++-- libsoup/soup-uri.c | 13 ++++--- tests/requester-test.c | 86 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/libsoup/soup-misc-private.h b/libsoup/soup-misc-private.h index b9f0724..cd83618 100644 --- a/libsoup/soup-misc-private.h +++ b/libsoup/soup-misc-private.h @@ -9,7 +9,7 @@ #include "soup-socket.h" -char *uri_decoded_copy (const char *str, int length); +char *uri_decoded_copy (const char *str, int length, int *decoded_length); guint soup_socket_handshake_sync (SoupSocket *sock, GCancellable *cancellable); diff --git a/libsoup/soup-request-data.c b/libsoup/soup-request-data.c index 66836a5..246854a 100644 --- a/libsoup/soup-request-data.c +++ b/libsoup/soup-request-data.c @@ -96,7 +96,7 @@ soup_request_data_send (SoupRequest *request, end = comma; if (end != start) - data->priv->content_type = uri_decoded_copy (start, end - start); + data->priv->content_type = uri_decoded_copy (start, end - start, NULL); } memstream = g_memory_input_stream_new (); @@ -105,12 +105,13 @@ soup_request_data_send (SoupRequest *request, start = comma + 1; if (*start) { - guchar *buf = (guchar *) soup_uri_decode (start); + int decoded_length = 0; + guchar *buf = (guchar *) uri_decoded_copy (start, strlen (start), &decoded_length); if (base64) buf = g_base64_decode_inplace ((gchar*) buf, &data->priv->content_length); else - data->priv->content_length = strlen ((const char *) buf); + data->priv->content_length = decoded_length; g_memory_input_stream_add_data (G_MEMORY_INPUT_STREAM (memstream), buf, data->priv->content_length, diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c index 28b3025..723e361 100644 --- a/libsoup/soup-uri.c +++ b/libsoup/soup-uri.c @@ -291,14 +291,14 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string) colon = strchr (uri_string, ':'); if (colon && colon < at) { uri->password = uri_decoded_copy (colon + 1, - at - colon - 1); + at - colon - 1, NULL); } else { uri->password = NULL; colon = at; } uri->user = uri_decoded_copy (uri_string, - colon - uri_string); + colon - uri_string, NULL); uri_string = at + 1; } else uri->user = uri->password = NULL; @@ -320,7 +320,7 @@ soup_uri_new_with_base (SoupURI *base, const char *uri_string) hostend = colon ? colon : path; } - uri->host = uri_decoded_copy (uri_string, hostend - uri_string); + uri->host = uri_decoded_copy (uri_string, hostend - uri_string, NULL); if (colon && colon != path - 1) { char *portend; @@ -694,7 +694,7 @@ soup_uri_encode (const char *part, const char *escape_extra) #define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2])) char * -uri_decoded_copy (const char *part, int length) +uri_decoded_copy (const char *part, int length, int *decoded_length) { unsigned char *s, *d; char *decoded = g_strndup (part, length); @@ -715,6 +715,9 @@ uri_decoded_copy (const char *part, int length) *d++ = *s; } while (*s++); + if (decoded_length) + *decoded_length = d - (unsigned char *)decoded - 1; + return decoded; } @@ -735,7 +738,7 @@ soup_uri_decode (const char *part) { g_return_val_if_fail (part != NULL, NULL); - return uri_decoded_copy (part, strlen (part)); + return uri_decoded_copy (part, strlen (part), NULL); } static char * diff --git a/tests/requester-test.c b/tests/requester-test.c index a4bb5b6..147ba4c 100644 --- a/tests/requester-test.c +++ b/tests/requester-test.c @@ -649,6 +649,90 @@ do_sync_test (const char *uri_string, gboolean plain_session) soup_uri_free (uri); } + +static void +do_null_char_request (SoupSession *session, const char *encoded_data, + const char *expected_data, int expected_len) +{ + GError *error = NULL; + GInputStream *stream; + SoupRequest *request; + SoupURI *uri; + char *uri_string, buf[256]; + gsize nread; + + uri_string = g_strdup_printf ("data:text/html,%s", encoded_data); + uri = soup_uri_new (uri_string); + g_free (uri_string); + + request = soup_session_request_uri (session, uri, NULL); + stream = soup_test_request_send (request, NULL, 0, &error); + + if (error) { + debug_printf (1, " could not send request: %s\n", error->message); + g_error_free (error); + g_object_unref (request); + soup_uri_free (uri); + return; + } + + g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error); + if (error) { + debug_printf (1, " could not read response: %s\n", error->message); + errors++; + g_clear_error (&error); + } + + soup_test_request_close_stream (request, stream, NULL, &error); + if (error) { + debug_printf (1, " could not close stream: %s\n", error->message); + errors++; + g_clear_error (&error); + } + + if (nread != expected_len) { + debug_printf (1, " response length mismatch: expected %d, got %lu\n", expected_len, nread); + errors++; + } else if (memcmp (buf, expected_data, nread) != 0) { + debug_printf (1, " response data mismatch\n"); + errors++; + } + + g_object_unref (stream); + g_object_unref (request); + soup_uri_free (uri); +} + +static void +do_null_char_test (gboolean plain_session) +{ + SoupSession *session; + int i; + static struct { + const char *encoded_data; + const char *expected_data; + int expected_len; + } test_cases[] = { + { "%3Cscript%3Ea%3D'%00'%3C%2Fscript%3E", "", 22 }, + { "%00%3Cscript%3Ea%3D42%3C%2Fscript%3E", "\0", 22 }, + { "%3Cscript%3E%00%3Cbr%2F%3E%3C%2Fscript%3E%00", "\0", 24 }, + }; + static int num_test_cases = G_N_ELEMENTS(test_cases); + + debug_printf (1, "Streaming data URLs containing null chars with %s\n", + plain_session ? "SoupSession" : "SoupSessionSync"); + + session = soup_test_session_new (plain_session ? SOUP_TYPE_SESSION : SOUP_TYPE_SESSION_ASYNC, + SOUP_SESSION_USE_THREAD_CONTEXT, TRUE, + NULL); + + for (i = 0; i < num_test_cases; i++) + do_null_char_request (session, test_cases[i].encoded_data, + test_cases[i].expected_data, test_cases[i].expected_len); + + soup_test_session_abort_unref (session); +} + int main (int argc, char **argv) { @@ -666,11 +750,13 @@ main (int argc, char **argv) do_thread_test (uri, FALSE); do_context_test (uri, FALSE); do_sync_test (uri, FALSE); + do_null_char_test (FALSE); do_simple_test (uri, TRUE); do_thread_test (uri, TRUE); do_context_test (uri, TRUE); do_sync_test (uri, TRUE); + do_null_char_test (TRUE); g_free (uri); soup_buffer_free (response); -- 2.7.4