[librsvg] Improve decoding of data: URIs



commit eb2b2c094fa4aa9dab43d443267b1b96983e8165
Author: Christian Persch <chpe gnome org>
Date:   Fri Jan 27 21:09:31 2012 +0100

    Improve decoding of data: URIs
    
    Support non-base64 data: URIs, and also get the content type out.
    Code adapted from libsoup.

 rsvg-io.c |  113 ++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 90 insertions(+), 23 deletions(-)
---
diff --git a/rsvg-io.c b/rsvg-io.c
index c0c609a..a3782c6 100644
--- a/rsvg-io.c
+++ b/rsvg-io.c
@@ -27,32 +27,99 @@
 
 #include <string.h>
 
+/* Copied from soup-request-data.c (LGPL2+):
+ * Copyright (C) 2009, 2010 Red Hat, Inc.
+ * Copyright (C) 2010 Igalia, S.L.
+ * and from soup-uri.c:
+ * Copyright 1999-2003 Ximian, Inc.
+ */
+
+#define XDIGIT(c) ((c) <= '9' ? (c) - '0' : ((c) & 0x4F) - 'A' + 10)
+#define HEXCHAR(s) ((XDIGIT (s[1]) << 4) + XDIGIT (s[2]))
+
+static char *
+uri_decoded_copy (const char *part, 
+                  gsize length)
+{
+    unsigned char *s, *d;
+    char *decoded = g_strndup (part, length);
+
+    s = d = (unsigned char *)decoded;
+    do {
+        if (*s == '%') {
+            if (!g_ascii_isxdigit (s[1]) ||
+                !g_ascii_isxdigit (s[2])) {
+                *d++ = *s;
+                continue;
+            }
+            *d++ = HEXCHAR (s);
+            s += 2;
+        } else {
+            *d++ = *s;
+        }
+    } while (*s++);
+
+    return decoded;
+}
+
+#define BASE64_INDICATOR     ";base64"
+#define BASE64_INDICATOR_LEN (sizeof (";base64") - 1)
+
 static guint8 *
-rsvg_acquire_base64_data (const char *data, 
-                          const char *base_uri, 
-                          gsize *len,
-                          GError **error)
+rsvg_acquire_data_data (const char *uri,
+                        const char *base_uri, 
+                        gsize *out_len,
+                        char **out_content_type,
+                        GError **error)
 {
-    guint8 *bytes;
-    gsize data_len, written_len;
-    int state = 0;
-    guint save = 0;
-
-    /* FIXME: be more correct! Check that is indeed a base64 data: URI */
-    while (*data)
-        if (*data++ == ',')
-            break;
-
-    data_len = strlen (data);
-    bytes = g_try_malloc (data_len / 4 * 3);
-    if (bytes == NULL)
-        return NULL;
+    const char *comma, *start, *end;
+    char *content_type = NULL;
+    char *data;
+    gsize data_len;
+    gboolean base64 = FALSE;
+
+    g_assert (out_len != NULL);
+
+    g_assert (g_str_has_prefix (uri, "data:"));
+    start = uri + 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 = uri_decoded_copy (start, end - start);
+    }
+
+    if (comma)
+        start = comma + 1;
 
-    written_len = g_base64_decode_step (data, data_len, bytes, &state, &save);
+    if (*start) {
+        data = uri_decoded_copy (start, strlen (start));
+
+        if (base64)
+            data = g_base64_decode_inplace ((char*) data, &data_len);
+        else
+            data_len = strlen ((const char *) data);
+    } else {
+        data = NULL;
+        data_len = 0;
+    }
 
-    *len = written_len;
+    if (out_content_type)
+        *out_content_type = content_type;
+    else
+        g_free (content_type);
 
-    return bytes;
+    *out_len = data_len;
+    return data;
 }
 
 gchar *
@@ -191,7 +258,7 @@ _rsvg_io_acquire_data (const char *href,
     }
 
     if (strncmp (href, "data:", 5) == 0)
-      return rsvg_acquire_base64_data (href, NULL, len, error);
+      return rsvg_acquire_data_data (href, NULL, len, NULL, error);
 
     if ((data = rsvg_acquire_file_data (href, base_uri, len, NULL)))
       return data;
@@ -218,7 +285,7 @@ _rsvg_io_acquire_stream (const char *href,
     }
 
     if (strncmp (href, "data:", 5) == 0) {
-        if (!(data = rsvg_acquire_base64_data (href, NULL, &len, error)))
+        if (!(data = rsvg_acquire_data_data (href, NULL, &len, NULL, error)))
             return NULL;
 
         return g_memory_input_stream_new_from_data (data, len, (GDestroyNotify) g_free);



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