[librsvg: 2/3] Port data: url parsing to Rust



commit d9464d20108ee6adb322cfa12fd849d637e5d783
Author: Federico Mena Quintero <federico gnome org>
Date:   Sun Nov 18 03:23:39 2018 -0600

    Port data: url parsing to Rust
    
    This uses the data-url crate.  One change in behavior is that data-url
    is strict about data: URLs having a MIME-type chunk in them.  This
    will be an apparent regression from issue #152, which requested that
    image data be sniffed if a data: URL doesn't have a MIME-type.
    
    However, https://fetch.spec.whatwg.org/#data-urls indeed indicates
    that data: URLs without a MIME-type are invalid.
    
    This removes the test files for issue #152, since they are expected to
    fail now.

 Cargo.lock                                         |  10 +++
 Makefile.am                                        |   1 +
 librsvg/rsvg-io.c                                  |  95 +--------------------
 rsvg_internals/Cargo.toml                          |   1 +
 rsvg_internals/src/error.rs                        |  23 +++++
 rsvg_internals/src/io.rs                           |  63 ++++++++++++++
 rsvg_internals/src/lib.rs                          |   4 +
 .../bugs/152-image-data-with-no-mimetype-ref.png   | Bin 184 -> 0 bytes
 .../bugs/152-image-data-with-no-mimetype.svg       |   7 --
 9 files changed, 105 insertions(+), 99 deletions(-)
---
diff --git a/Cargo.lock b/Cargo.lock
index 1c0a1651..37be5da3 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -276,6 +276,14 @@ dependencies = [
  "memchr 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
+[[package]]
+name = "data-url"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+dependencies = [
+ "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
 [[package]]
 name = "downcast-rs"
 version = "1.0.3"
@@ -929,6 +937,7 @@ dependencies = [
  "cairo-sys-rs 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "criterion 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.25.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "data-url 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "float-cmp 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -1252,6 +1261,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index";
 "checksum cssparser-macros 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = 
"b16e382d9b983fdb9ac6a36b37fdeb84ce3ea81f749febfee3463cfa7f24275e"
 "checksum csv 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = 
"6d54f6b0fd69128a2894b1a3e57af5849a0963c1cc77b165d30b896e40296452"
 "checksum csv-core 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = 
"4dd8e6d86f7ba48b4276ef1317edc8cc36167546d8972feb4a2b5fec0b374105"
+"checksum data-url 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = 
"d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c"
 "checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = 
"18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c"
 "checksum dtoa 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = 
"6d301140eb411af13d3115f9a562c85cc6b541ade9dfa314132244aaee7489dd"
 "checksum dtoa-short 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = 
"59020b8513b76630c49d918c33db9f4c91638e7d3404a28084083b87e33f76f2"
diff --git a/Makefile.am b/Makefile.am
index 2b5a2827..31987c62 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -84,6 +84,7 @@ RUST_SRC =                                                    \
        rsvg_internals/src/gradient.rs                          \
        rsvg_internals/src/handle.rs                            \
        rsvg_internals/src/image.rs                             \
+       rsvg_internals/src/io.rs                                \
        rsvg_internals/src/iri.rs                               \
        rsvg_internals/src/length.rs                            \
        rsvg_internals/src/lib.rs                               \
diff --git a/librsvg/rsvg-io.c b/librsvg/rsvg-io.c
index de6d61f8..965be423 100644
--- a/librsvg/rsvg-io.c
+++ b/librsvg/rsvg-io.c
@@ -28,101 +28,12 @@
 
 #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 char *
+/* Defined in rsvg_internals/src/io.rs */
+extern char *
 rsvg_decode_data_uri (const char *uri,
                       char **out_mime_type,
                       gsize *out_len,
-                      GError **error)
-{
-    const char *comma, *start, *end;
-    char *mime_type;
-    char *data;
-    gsize data_len;
-    gboolean base64 = FALSE;
-
-    g_assert (out_len != NULL);
-    g_assert (strncmp (uri, "data:", 5) == 0);
-
-    mime_type = NULL;
-    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) {
-            mime_type = uri_decoded_copy (start, end - start);
-        }
-    }
-
-    if (comma)
-        start = comma + 1;
-
-    if (*start) {
-       data = uri_decoded_copy (start, strlen (start));
-
-        if (base64)
-            data = (char *) g_base64_decode_inplace (data, &data_len);
-        else
-            data_len = strlen (data);
-    } else {
-        data = NULL;
-        data_len = 0;
-    }
-
-    if (out_mime_type)
-        *out_mime_type = mime_type;
-    else
-        g_free (mime_type);
-
-    *out_len = data_len;
-    return data;
-}
+                      GError **error);
 
 static char *
 rsvg_acquire_file_data (const char *uri,
diff --git a/rsvg_internals/Cargo.toml b/rsvg_internals/Cargo.toml
index 56826122..b6e8d44f 100644
--- a/rsvg_internals/Cargo.toml
+++ b/rsvg_internals/Cargo.toml
@@ -23,6 +23,7 @@ phf_codegen = "0.7.21"
 cairo-rs = "0.5.0"
 cairo-sys-rs = "0.7.0"
 cssparser = "0.25.1"
+data-url = "0.1"
 downcast-rs = "^1.0.0"
 encoding = "0.2.33"
 float-cmp = "0.4.0"
diff --git a/rsvg_internals/src/error.rs b/rsvg_internals/src/error.rs
index 4ced1898..5e5505ea 100644
--- a/rsvg_internals/src/error.rs
+++ b/rsvg_internals/src/error.rs
@@ -4,6 +4,9 @@ use std::fmt;
 use cairo;
 use cssparser::BasicParseError;
 use glib;
+use glib::translate::*;
+use glib_sys;
+use libc;
 
 use attributes::Attribute;
 use parsers::ParseError;
@@ -103,6 +106,8 @@ impl From<cairo::Status> for RenderingError {
 
 #[derive(Clone)]
 pub enum LoadingError {
+    // Could not parse data: URL
+    BadDataUrl,
     Cairo(cairo::Status),
     EmptyData,
     Glib(glib::Error),
@@ -123,6 +128,24 @@ impl From<glib::Error> for LoadingError {
     }
 }
 
+extern "C" {
+    fn rsvg_error_quark() -> glib_sys::GQuark;
+}
+
+pub fn set_gerror(err: *mut *mut glib_sys::GError, code: u32, msg: &str) {
+    unsafe {
+        // this is RSVG_ERROR_FAILED, the only error code available in RsvgError
+        assert!(code == 0);
+
+        glib_sys::g_set_error_literal(
+            err,
+            rsvg_error_quark(),
+            code as libc::c_int,
+            msg.to_glib_none().0,
+        );
+    }
+}
+
 #[cfg(test)]
 pub fn is_parse_error<T>(r: &Result<T, ValueErrorKind>) -> bool {
     match *r {
diff --git a/rsvg_internals/src/io.rs b/rsvg_internals/src/io.rs
new file mode 100644
index 00000000..d534ae2e
--- /dev/null
+++ b/rsvg_internals/src/io.rs
@@ -0,0 +1,63 @@
+use data_url;
+use glib_sys;
+use libc;
+
+use glib::translate::*;
+use std::ptr;
+
+use error::{set_gerror, LoadingError};
+use handle::BinaryData;
+use util::utf8_cstr;
+
+fn decode_data_uri(uri: &str) -> Result<BinaryData, LoadingError> {
+    let data_url = data_url::DataUrl::process(uri).map_err(|_| LoadingError::BadDataUrl)?;
+
+    let mime_type = data_url.mime_type().to_string();
+
+    let (bytes, _fragment_id) = data_url
+        .decode_to_vec()
+        .map_err(|_| LoadingError::BadDataUrl)?;
+
+    Ok(BinaryData {
+        data: bytes,
+        content_type: Some(mime_type),
+    })
+}
+
+#[no_mangle]
+pub fn rsvg_decode_data_uri(
+    uri: *const libc::c_char,
+    out_mime_type: *mut *mut libc::c_char,
+    out_size: *mut usize,
+    error: *mut *mut glib_sys::GError,
+) -> *mut libc::c_char {
+    unsafe {
+        assert!(!out_mime_type.is_null());
+        assert!(!out_size.is_null());
+
+        let uri = utf8_cstr(uri);
+
+        match decode_data_uri(uri) {
+            Ok(binary_data) => {
+                *out_mime_type = binary_data.content_type.to_glib_full();
+                *out_size = binary_data.data.len();
+
+                if !error.is_null() {
+                    *error = ptr::null_mut();
+                }
+
+                ToGlibContainerFromSlice::to_glib_full_from_slice(&binary_data.data)
+                    as *mut libc::c_char
+            }
+
+            Err(_) => {
+                *out_mime_type = ptr::null_mut();
+                *out_size = 0;
+
+                set_gerror(error, 0, "could not decode data: URL");
+
+                ptr::null_mut()
+            }
+        }
+    }
+}
diff --git a/rsvg_internals/src/lib.rs b/rsvg_internals/src/lib.rs
index 520d7e68..627ddf0f 100644
--- a/rsvg_internals/src/lib.rs
+++ b/rsvg_internals/src/lib.rs
@@ -5,6 +5,7 @@
 extern crate cairo;
 extern crate cairo_sys;
 extern crate cssparser;
+extern crate data_url;
 extern crate downcast_rs;
 extern crate encoding;
 extern crate float_cmp;
@@ -42,6 +43,8 @@ pub use drawing_ctx::{
     rsvg_drawing_ctx_new,
 };
 
+pub use io::rsvg_decode_data_uri;
+
 pub use node::rsvg_node_unref;
 
 pub use tree::{
@@ -99,6 +102,7 @@ mod font_props;
 mod gradient;
 mod handle;
 mod image;
+mod io;
 mod iri;
 mod length;
 mod link;


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