summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2020-10-26 11:50:54 +0100
committerCarlos Garcia Campos <cgarcia@igalia.com>2020-10-26 11:50:54 +0100
commit9f42c7b8dc1d099b1464070ca993189bf7a3cdd0 (patch)
tree9ea18674d942cf9d9a4388151f65a8e6d42452a3
parent1c444ee4b9ba612f71621b6ef2370fd8bbd8c676 (diff)
downloadlibsoup-9f42c7b8dc1d099b1464070ca993189bf7a3cdd0.tar.gz
uri: add soup_uri_decode_data_uri to decode data URIs
-rw-r--r--docs/reference/libsoup-3.0-sections.txt1
-rw-r--r--libsoup/soup-uri.c78
-rw-r--r--libsoup/soup-uri.h3
-rw-r--r--tests/uri-parsing-test.c48
4 files changed, 106 insertions, 24 deletions
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index a2d65cf3..6e664d92 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -594,6 +594,7 @@ soup_uri_free
soup_uri_encode
soup_uri_decode
soup_uri_normalize
+soup_uri_decode_data_uri
<SUBSECTION>
SOUP_URI_SCHEME_HTTP
SOUP_URI_SCHEME_HTTPS
diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c
index fd8dc828..2ad744be 100644
--- a/libsoup/soup-uri.c
+++ b/libsoup/soup-uri.c
@@ -1381,4 +1381,82 @@ soup_uri_is_https (SoupURI *uri, char **aliases)
return FALSE;
}
+#define BASE64_INDICATOR ";base64"
+#define BASE64_INDICATOR_LEN (sizeof (";base64") - 1)
+
+/**
+ * soup_uri_decode_data:
+ * @uri: a data URI, in string form
+ * @content_type: (out) (nullable) (transfer full): location to store content type, or %NULL
+ *
+ * Decodes the given data URI and returns its contents and @content_type.
+ *
+ * Returns: (transfer full): a #GBytes with the contents of @uri,
+ * or %NULL if @uri is not a valid data URI
+ */
+GBytes *
+soup_uri_decode_data_uri (const char *uri,
+ char **content_type)
+{
+ SoupURI *soup_uri;
+ const char *comma, *start, *end;
+ gboolean base64 = FALSE;
+ char *uri_string;
+ GBytes *bytes;
+
+ g_return_val_if_fail (uri != NULL, NULL);
+
+ soup_uri = soup_uri_new (uri);
+ if (!soup_uri)
+ return NULL;
+
+ if (soup_uri->scheme != SOUP_URI_SCHEME_DATA || soup_uri->host != NULL)
+ return NULL;
+
+ if (content_type)
+ *content_type = NULL;
+
+ uri_string = soup_uri_to_string (soup_uri, FALSE);
+ soup_uri_free (soup_uri);
+
+ start = uri_string + 5;
+ comma = strchr (start, ',');
+ if (comma && comma != start) {
+ /* Deal with MIME type / params */
+ if (comma >= start + BASE64_INDICATOR_LEN && !g_ascii_strncasecmp (comma - BASE64_INDICATOR_LEN, BASE64_INDICATOR, BASE64_INDICATOR_LEN)) {
+ end = comma - BASE64_INDICATOR_LEN;
+ base64 = TRUE;
+ } else
+ end = comma;
+
+ if (end != start && content_type)
+ *content_type = soup_uri_decoded_copy (start, end - start, NULL);
+ }
+
+ if (content_type && !*content_type)
+ *content_type = g_strdup ("text/plain;charset=US-ASCII");
+
+ if (comma)
+ start = comma + 1;
+
+ if (*start) {
+ gsize content_length;
+ int decoded_length = 0;
+ guchar *buffer = (guchar *) soup_uri_decoded_copy (start, strlen (start),
+ &decoded_length);
+
+ if (base64)
+ buffer = g_base64_decode_inplace ((gchar*)buffer, &content_length);
+ else
+ content_length = decoded_length;
+
+ bytes = g_bytes_new_take (buffer, content_length);
+ } else {
+ bytes = g_bytes_new_static (NULL, 0);
+ }
+ g_free (uri_string);
+
+ return bytes;
+}
+
G_DEFINE_BOXED_TYPE (SoupURI, soup_uri, soup_uri_copy, soup_uri_free)
diff --git a/libsoup/soup-uri.h b/libsoup/soup-uri.h
index 6e949ea1..ea24a53c 100644
--- a/libsoup/soup-uri.h
+++ b/libsoup/soup-uri.h
@@ -72,6 +72,9 @@ char *soup_uri_decode (const char *part);
SOUP_AVAILABLE_IN_2_4
char *soup_uri_normalize (const char *part,
const char *unescape_extra);
+SOUP_AVAILABLE_IN_ALL
+GBytes *soup_uri_decode_data_uri (const char *uri,
+ char **content_type);
SOUP_AVAILABLE_IN_2_4
gboolean soup_uri_uses_default_port (SoupURI *uri);
diff --git a/tests/uri-parsing-test.c b/tests/uri-parsing-test.c
index d463f1f4..9acadf7c 100644
--- a/tests/uri-parsing-test.c
+++ b/tests/uri-parsing-test.c
@@ -525,43 +525,43 @@ static const DataURITest data_tests[] = {
"" },
{ "data:text/plain,",
"text/plain",
- "" }
+ "" },
+ { "data://text/plain,foo",
+ NULL,
+ NULL },
+ { "http:text/plain,foo%20bar",
+ NULL,
+ NULL },
+ { "./foo",
+ NULL,
+ NULL },
};
static void
do_data_tests (void)
{
- SoupSession *session;
- SoupRequest *req;
- GInputStream *stream;
- char buf[128];
- gsize nread;
int i;
- GError *error = NULL;
- session = soup_test_session_new (SOUP_TYPE_SESSION, NULL);
for (i = 0; i < G_N_ELEMENTS (data_tests); i++) {
- req = soup_session_request (session, data_tests[i].uri, &error);
- g_assert_no_error (error);
-
- stream = soup_request_send (req, NULL, &error);
- g_assert_no_error (error);
+ GBytes *bytes;
+ char *content_type = NULL;
- g_input_stream_read_all (stream, buf, sizeof (buf), &nread, NULL, &error);
+ bytes = soup_uri_decode_data_uri (data_tests[i].uri, &content_type);
+ if (!data_tests[i].body) {
+ g_assert_null (bytes);
+ g_assert_null (content_type);
- g_assert_no_error (error);
- g_assert_cmpint (nread, ==, strlen (data_tests[i].body));
- buf[nread] = 0;
- g_assert_cmpstr (buf, ==, data_tests[i].body);
+ continue;
+ }
- g_assert_cmpstr (soup_request_get_content_type (req), ==, data_tests[i].mime_type);
+ g_assert_nonnull (bytes);
+ g_assert_cmpmem (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes),
+ data_tests[i].body, strlen (data_tests[i].body));
+ g_assert_cmpstr (content_type, ==, data_tests[i].mime_type);
- g_input_stream_close (stream, NULL, &error);
- g_assert_no_error (error);
- g_object_unref (stream);
- g_object_unref (req);
+ g_free (content_type);
+ g_bytes_unref (bytes);
}
- soup_test_session_abort_unref (session);
}
static void