[libsoup/carlosgc/simple-api: 14/16] uri: add soup_uri_decode_data_uri to decode data URIs




commit 9f42c7b8dc1d099b1464070ca993189bf7a3cdd0
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Mon Oct 26 11:50:54 2020 +0100

    uri: add soup_uri_decode_data_uri to decode data URIs

 docs/reference/libsoup-3.0-sections.txt |  1 +
 libsoup/soup-uri.c                      | 78 +++++++++++++++++++++++++++++++++
 libsoup/soup-uri.h                      |  3 ++
 tests/uri-parsing-test.c                | 48 ++++++++++----------
 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


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]