diff options
author | Benjamin Otte <otte@redhat.com> | 2019-05-09 03:04:21 +0200 |
---|---|---|
committer | Benjamin Otte <otte@redhat.com> | 2019-05-12 17:27:01 +0200 |
commit | 5da58ba47d3e4e6b5ba49b96f17360f2c214a96f (patch) | |
tree | 7ab3de2f0e25d8afd685c12bac53360e674cbccb | |
parent | 4505f4f17b147921dc7838ce3f9234bc73451d98 (diff) | |
download | gtk+-5da58ba47d3e4e6b5ba49b96f17360f2c214a96f.tar.gz |
css: Add gtk_css_data_url_parse()
This surprisingly decodes data URLs.
-rw-r--r-- | gtk/css/gtkcssdataurl.c | 175 | ||||
-rw-r--r-- | gtk/css/gtkcssdataurlprivate.h | 35 | ||||
-rw-r--r-- | gtk/css/meson.build | 1 | ||||
-rw-r--r-- | testsuite/css/data.c | 100 | ||||
-rw-r--r-- | testsuite/css/meson.build | 16 |
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) |