1 | /* GStreamer data:// uri source element |
2 | * Copyright (C) 2009 Igalia S.L |
3 | * Copyright (C) 2009 Sebastian Dröge <sebastian.droege@collabora.co.uk> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Library General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Library General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Library General Public |
16 | * License along with this library; if not, write to the |
17 | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
18 | * Boston, MA 02110-1301, USA. |
19 | */ |
20 | |
21 | /*<private> |
22 | * # Data URLs |
23 | * |
24 | * These function allow encoding and decoding of data: URLs, see |
25 | * [RFC 2397](http://tools.ietf.org/html/rfc2397) for more information. |
26 | */ |
27 | |
28 | #include "config.h" |
29 | |
30 | #include "gtkcssdataurlprivate.h" |
31 | |
32 | #include "../gtkintl.h" |
33 | |
34 | #include <string.h> |
35 | |
36 | /*<private> |
37 | * gtk_css_data_url_parse: |
38 | * @url: the URL to parse |
39 | * @out_mimetype: (out nullable optional): Return location to set the contained |
40 | * mime type to. If no mime type was specified, this value is set to %NULL. |
41 | * @error: error location |
42 | * |
43 | * Decodes a data URL according to RFC2397 and returns the decoded data. |
44 | * |
45 | * Returns: a new `GBytes` with the decoded data |
46 | */ |
47 | GBytes * |
48 | gtk_css_data_url_parse (const char *url, |
49 | char **out_mimetype, |
50 | GError **error) |
51 | { |
52 | char *mimetype = NULL; |
53 | const char *parameters_start; |
54 | const char *data_start; |
55 | GBytes *bytes; |
56 | gboolean base64 = FALSE; |
57 | char *charset = NULL; |
58 | gpointer bdata; |
59 | gsize bsize; |
60 | |
61 | /* url must be an URI as defined in RFC 2397 |
62 | * data:[<mediatype>][;base64],<data> |
63 | */ |
64 | if (g_ascii_strncasecmp (s1: "data:" , s2: url, n: 5) != 0) |
65 | { |
66 | g_set_error (err: error, |
67 | G_IO_ERROR, |
68 | code: G_IO_ERROR_INVALID_FILENAME, |
69 | _("Not a data: URL" )); |
70 | return NULL; |
71 | } |
72 | |
73 | url += 5; |
74 | |
75 | parameters_start = strchr (s: url, c: ';'); |
76 | data_start = strchr (s: url, c: ','); |
77 | if (data_start == NULL) |
78 | { |
79 | g_set_error (err: error, |
80 | G_IO_ERROR, |
81 | code: G_IO_ERROR_INVALID_FILENAME, |
82 | _("Malformed data: URL" )); |
83 | return NULL; |
84 | } |
85 | if (parameters_start > data_start) |
86 | parameters_start = NULL; |
87 | |
88 | if (data_start != url && parameters_start != url) |
89 | { |
90 | mimetype = g_strndup (str: url, |
91 | n: (parameters_start ? parameters_start |
92 | : data_start) - url); |
93 | } |
94 | else |
95 | { |
96 | mimetype = NULL; |
97 | } |
98 | |
99 | if (parameters_start != NULL) |
100 | { |
101 | char *parameters_str; |
102 | char **parameters; |
103 | guint i; |
104 | |
105 | parameters_str = g_strndup (str: parameters_start + 1, n: data_start - parameters_start - 1); |
106 | parameters = g_strsplit (string: parameters_str, delimiter: ";" , max_tokens: -1); |
107 | |
108 | for (i = 0; parameters[i] != NULL; i++) |
109 | { |
110 | if (g_ascii_strcasecmp (s1: "base64" , s2: parameters[i]) == 0) |
111 | { |
112 | base64 = TRUE; |
113 | } |
114 | else if (g_ascii_strncasecmp (s1: "charset=" , s2: parameters[i], n: 8) == 0) |
115 | { |
116 | g_free (mem: charset); |
117 | charset = g_strdup (str: parameters[i] + 8); |
118 | } |
119 | } |
120 | g_free (mem: parameters_str); |
121 | g_strfreev (str_array: parameters); |
122 | } |
123 | |
124 | /* Skip comma */ |
125 | data_start += 1; |
126 | if (base64) |
127 | { |
128 | bdata = g_base64_decode (text: data_start, out_len: &bsize); |
129 | } |
130 | else |
131 | { |
132 | /* URI encoded, i.e. "percent" encoding */ |
133 | /* XXX: This doesn't allow nul bytes */ |
134 | bdata = g_uri_unescape_string (escaped_string: data_start, NULL); |
135 | if (bdata == NULL) |
136 | { |
137 | g_set_error (err: error, |
138 | G_IO_ERROR, |
139 | code: G_IO_ERROR_INVALID_FILENAME, |
140 | _("Could not unescape string" )); |
141 | g_free (mem: mimetype); |
142 | return NULL; |
143 | } |
144 | bsize = strlen (s: bdata); |
145 | } |
146 | |
147 | /* Convert to UTF8 */ |
148 | if ((mimetype == NULL || g_ascii_strcasecmp (s1: "text/plain" , s2: mimetype) == 0) && |
149 | charset && g_ascii_strcasecmp (s1: "US-ASCII" , s2: charset) != 0 |
150 | && g_ascii_strcasecmp (s1: "UTF-8" , s2: charset) != 0) |
151 | { |
152 | gsize read; |
153 | gsize written; |
154 | gpointer data; |
155 | GError *local_error = NULL; |
156 | |
157 | data = g_convert_with_fallback (str: bdata, len: bsize, |
158 | to_codeset: "UTF-8" , from_codeset: charset, |
159 | fallback: (char *) "*" , |
160 | bytes_read: &read, bytes_written: &written, error: &local_error); |
161 | g_free (mem: bdata); |
162 | |
163 | if (local_error) |
164 | { |
165 | g_propagate_error (dest: error, src: local_error); |
166 | g_free (mem: charset); |
167 | g_free (mem: data); |
168 | g_free (mem: mimetype); |
169 | return NULL; |
170 | } |
171 | |
172 | |
173 | bdata = data; |
174 | bsize = written; |
175 | } |
176 | bytes = g_bytes_new_take (data: bdata, size: bsize); |
177 | |
178 | g_free (mem: charset); |
179 | if (out_mimetype) |
180 | *out_mimetype = mimetype; |
181 | else |
182 | g_free (mem: mimetype); |
183 | |
184 | return bytes; |
185 | } |
186 | |