summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@redhat.com>2019-05-09 03:04:21 +0200
committerBenjamin Otte <otte@redhat.com>2019-05-12 17:27:01 +0200
commit5da58ba47d3e4e6b5ba49b96f17360f2c214a96f (patch)
tree7ab3de2f0e25d8afd685c12bac53360e674cbccb
parent4505f4f17b147921dc7838ce3f9234bc73451d98 (diff)
downloadgtk+-5da58ba47d3e4e6b5ba49b96f17360f2c214a96f.tar.gz
css: Add gtk_css_data_url_parse()
This surprisingly decodes data URLs.
-rw-r--r--gtk/css/gtkcssdataurl.c175
-rw-r--r--gtk/css/gtkcssdataurlprivate.h35
-rw-r--r--gtk/css/meson.build1
-rw-r--r--testsuite/css/data.c100
-rw-r--r--testsuite/css/meson.build16
5 files changed, 327 insertions, 0 deletions
diff --git a/gtk/css/gtkcssdataurl.c b/gtk/css/gtkcssdataurl.c
new file mode 100644
index 0000000000..e772775fca
--- /dev/null
+++ b/gtk/css/gtkcssdataurl.c
@@ -0,0 +1,175 @@
+/* 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.
+ */
+
+/*<private>
+ * 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>
+
+/*<private>
+ * gtk_css_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 decoded data.
+ *
+ * Returns: a new #GBytes with the decoded data or %NULL on error
+ **/
+GBytes *
+gtk_css_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..5d0d6a8591
--- /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_css_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..2f623df128
--- /dev/null
+++ b/testsuite/css/data.c
@@ -0,0 +1,100 @@
+/*
+ * 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_css_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");
+
+ 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)