[gtk/wip/otte/for-master: 2/5] css: Add gtk_css_data_url_parse()



commit dcf7804240ee419dcb6980c527f4dc9458bbf1e7
Author: Benjamin Otte <otte redhat com>
Date:   Thu May 9 03:04:21 2019 +0200

    css: Add gtk_css_data_url_parse()
    
    This surprisingly decodes data URLs.

 gtk/css/gtkcssdataurl.c        | 172 +++++++++++++++++++++++++++++++++++++++++
 gtk/css/gtkcssdataurlprivate.h |  35 +++++++++
 gtk/css/meson.build            |   1 +
 testsuite/css/data.c           | 101 ++++++++++++++++++++++++
 testsuite/css/meson.build      |  16 ++++
 5 files changed, 325 insertions(+)
---
diff --git a/gtk/css/gtkcssdataurl.c b/gtk/css/gtkcssdataurl.c
new file mode 100644
index 0000000000..b240e99bb2
--- /dev/null
+++ b/gtk/css/gtkcssdataurl.c
@@ -0,0 +1,172 @@
+/* GStreamer data:// uri source element
+ * Copyright (C) 2009 Igalia S.L
+ * Copyright (C) 2009 Sebastian Dröge <sebastian droege collabora co uk>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:data-url
+ * @title: data: URLs
+ *
+ * These function allow encoding and decoding of data: URLs, see
+ * [RFC 2397](http://tools.ietf.org/html/rfc2397) for more information.
+ */
+
+#include "config.h"
+
+#include "gtkcssdataurlprivate.h"
+
+#include "../gtkintl.h"
+
+#include <string.h>
+
+/**
+ * gtk_data_url_parse:
+ * @url: the URL to parse
+ * @out_mimetype: (out nullable optional): Return location to set the contained
+ *     mime type to. If no mime type was specified, this value is set to %NULL.
+ * @error: error location or %NULL for none
+ *
+ * Decodes a data URL according to RFC2397 and returns the decded data.
+ *
+ * Returns: a new #GBytes with the decoded data or %NULL on error
+ **/
+GBytes *
+gtk_data_url_parse (const char  *url,
+                    char       **out_mimetype,
+                    GError     **error)
+{
+  char *mimetype = NULL;
+  const char *parameters_start;
+  const char *data_start;
+  GBytes *bytes;
+  gboolean base64 = FALSE;
+  char *charset = NULL;
+  gpointer bdata;
+  gsize bsize;
+
+  /* url must be an URI as defined in RFC 2397
+   * data:[<mediatype>][;base64],<data>
+   */
+  if (g_ascii_strncasecmp ("data:", url, 5) != 0)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_FILENAME,
+                   _("Not a data: URL"));
+      return NULL;
+    }
+
+  url += 5;
+
+  parameters_start = strchr (url, ';');
+  data_start = strchr (url, ',');
+  if (data_start == NULL)
+    {
+      g_set_error (error,
+                   G_IO_ERROR,
+                   G_IO_ERROR_INVALID_FILENAME,
+                   _("Malformed data: URL"));
+      return NULL;
+    }
+  if (parameters_start > data_start)
+    parameters_start = NULL;
+
+  if (data_start != url && parameters_start != url)
+    {
+      mimetype = g_strndup (url,
+                            (parameters_start ? parameters_start
+                                              : data_start) - url);
+    }
+  else
+    {
+      mimetype = NULL;
+    }
+
+  if (parameters_start != NULL)
+    {
+      char *parameters_str;
+      char **parameters;
+      guint i;
+
+      parameters_str = g_strndup (parameters_start + 1, data_start - parameters_start - 1);
+      parameters = g_strsplit (parameters_str, ";", -1);
+
+      for (i = 0; parameters[i] != NULL; i++)
+        {
+          if (g_ascii_strcasecmp ("base64", parameters[i]) == 0)
+            {
+              base64 = TRUE;
+            }
+          else if (g_ascii_strncasecmp ("charset=", parameters[i], 8) == 0)
+            {
+              g_free (charset);
+              charset = g_strdup (parameters[i] + 8);
+            }
+        }
+    g_free (parameters_str);
+    g_strfreev (parameters);
+  }
+
+  /* Skip comma */
+  data_start += 1;
+  if (base64) {
+    bdata = g_base64_decode (data_start, &bsize);
+  } else {
+    /* URI encoded, i.e. "percent" encoding */
+    /* XXX: This doesn't allow nul bytes */
+    bdata = g_uri_unescape_string (data_start, NULL);
+    if (bdata == NULL)
+      {
+        g_set_error (error,
+                     G_IO_ERROR,
+                     G_IO_ERROR_INVALID_FILENAME,
+                     _("Could not unescape string"));
+        g_free (mimetype);
+        return NULL;
+      }
+    bsize = strlen (bdata);
+  }
+
+  /* Convert to UTF8 */
+  if ((mimetype == NULL || g_ascii_strcasecmp ("text/plain", mimetype) == 0) &&
+      charset && g_ascii_strcasecmp ("US-ASCII", charset) != 0
+      && g_ascii_strcasecmp ("UTF-8", charset) != 0)
+    {
+      gsize read;
+      gsize written;
+      gpointer data;
+
+      data = g_convert_with_fallback (bdata, bsize,
+                                      "UTF-8", charset, 
+                                      (char *) "*",
+                                      &read, &written, NULL);
+      g_free (bdata);
+
+      bdata = data;
+      bsize = written;
+    }
+  bytes = g_bytes_new_take (bdata, bsize);
+
+  g_free (charset);
+  if (out_mimetype)
+    *out_mimetype = mimetype;
+  else
+    g_free (mimetype);
+
+  return bytes;
+}
diff --git a/gtk/css/gtkcssdataurlprivate.h b/gtk/css/gtkcssdataurlprivate.h
new file mode 100644
index 0000000000..aa21e0b977
--- /dev/null
+++ b/gtk/css/gtkcssdataurlprivate.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright © 2019 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+
+#ifndef __GTK_CSS_DATA_URL_PRIVATE_H__
+#define __GTK_CSS_DATA_URL_PRIVATE_H__
+
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+GBytes *                gtk_data_url_parse                      (const char             *url,
+                                                                 char                  **out_mimetype,
+                                                                 GError                **error);
+
+G_END_DECLS
+
+#endif /* __GTK_CSS_DATA_URL_PRIVATE_H__ */
+
diff --git a/gtk/css/meson.build b/gtk/css/meson.build
index e97360f174..907b174f49 100644
--- a/gtk/css/meson.build
+++ b/gtk/css/meson.build
@@ -5,6 +5,7 @@ gtk_css_public_sources = files([
 ])
 
 gtk_css_private_sources = files([
+  'gtkcssdataurl.c',
   'gtkcssparser.c',
   'gtkcsstokenizer.c',
 ])
diff --git a/testsuite/css/data.c b/testsuite/css/data.c
new file mode 100644
index 0000000000..d790dc5256
--- /dev/null
+++ b/testsuite/css/data.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright © 2019 Benjamin Otte
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Benjamin Otte <otte gnome org>
+ */
+
+#include "../../gtk/css/gtkcssdataurlprivate.h"
+
+#include <locale.h>
+
+typedef struct _Test Test;
+
+struct _Test
+{
+  const char *name;
+  const char *url;
+  const char *mimetype;
+  const char *contents;
+  gsize contents_len;
+};
+
+#define CONTENTS(data) (data), sizeof(data) - 1
+Test tests[] = {
+  { "simple",
+    "data:,HelloWorld",
+    NULL, CONTENTS("HelloWorld") },
+  { "nodata",
+    "data:,",
+    NULL, CONTENTS("") },
+  { "case_sensitive",
+    "dATa:,HelloWorld",
+    NULL, CONTENTS("HelloWorld") },
+  { "semicolon_after_comma",
+    "data:,;base64",
+    NULL, CONTENTS(";base64") },
+  { "mimetype",
+    "data:image/png,nopng",
+    "image/png", CONTENTS("nopng") },
+  { "charset",
+    "data:text/plain;charset=ISO-8859-1,Timm B\344der",
+    "text/plain", CONTENTS("Timm Bäder") },
+  { "charset_base64",
+    "data:text/plain;charset=ISO-8859-5;base64,wOPh29DdILjW0ePb0OLe0g==",
+    "text/plain", CONTENTS("Руслан Ижбулатов") },
+};
+
+static void
+test_parse (gconstpointer data)
+{
+  const Test *test = data;
+  GError *error = NULL;
+  char *mimetype = NULL;
+  GBytes *bytes;
+
+  bytes = gtk_data_url_parse (test->url, &mimetype, &error);
+
+  g_assert (bytes != NULL);
+  g_assert_no_error (error);
+  if (test->mimetype == NULL)
+    g_assert (mimetype == NULL);
+  else
+    g_assert_cmpstr (mimetype, ==, test->mimetype);
+
+  g_assert_cmpmem (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes),
+                   test->contents, test->contents_len);
+
+  g_bytes_unref (bytes);
+}
+
+int
+main (int argc, char *argv[])
+{
+  guint i;
+
+  g_test_init (&argc, &argv, NULL);
+  setlocale (LC_ALL, "C");
+  g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s";);
+
+  for (i = 0; i < G_N_ELEMENTS (tests); i++)
+    {
+      char *name = g_strdup_printf ("/css/data/load/%s", tests[i].name);
+      g_test_add_data_func (name, &tests[i], test_parse);
+      g_free (name);
+    }
+
+  return g_test_run ();
+}
+
diff --git a/testsuite/css/meson.build b/testsuite/css/meson.build
index 077cb7d5c3..eefea5aff8 100644
--- a/testsuite/css/meson.build
+++ b/testsuite/css/meson.build
@@ -20,6 +20,22 @@ test('api', test_api,
           ],
      suite: 'css')
 
+test_data = executable('data', ['data.c', '../../gtk/css/gtkcssdataurl.c'],
+                       include_directories: [confinc, ],
+                       dependencies: gtk_deps,
+                       install: get_option('install-tests'),
+                       install_dir: testexecdir)
+test('data', test_data,
+     args: ['--tap', '-k' ],
+     env: [ 'GIO_USE_VOLUME_MONITOR=unix',
+            'GSETTINGS_BACKEND=memory',
+            'GTK_CSD=1',
+            'G_ENABLE_DIAGNOSTIC=0',
+            'G_TEST_SRCDIR=@0@'.format(meson.current_source_dir()),
+            'G_TEST_BUILDDIR=@0@'.format(meson.current_build_dir())
+          ],
+     suite: 'css')
+
 if get_option('install-tests')
   conf = configuration_data()
   conf.set('libexecdir', gtk_libexecdir)


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