/src/libxmlb/src/xb-builder-source.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2018 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "XbSilo" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <gio/gio.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "xb-builder-fixup-private.h" |
15 | | #include "xb-builder-source-ctx-private.h" |
16 | | #include "xb-builder-source-private.h" |
17 | | #ifdef HAVE_LZMA |
18 | | #include "xb-lzma-decompressor.h" |
19 | | #endif |
20 | | #ifdef HAVE_ZSTD |
21 | | #include "xb-zstd-decompressor.h" |
22 | | #endif |
23 | | typedef struct { |
24 | | GInputStream *istream; |
25 | | GFile *file; |
26 | | GPtrArray *fixups; /* of XbBuilderFixup */ |
27 | | GPtrArray *adapters; /* of XbBuilderSourceAdapter */ |
28 | | XbBuilderNode *info; |
29 | | gchar *guid; |
30 | | gchar *prefix; |
31 | | gchar *content_type; |
32 | | XbBuilderSourceFlags flags; |
33 | | } XbBuilderSourcePrivate; |
34 | | |
35 | 0 | G_DEFINE_TYPE_WITH_PRIVATE(XbBuilderSource, xb_builder_source, G_TYPE_OBJECT) |
36 | 0 | #define GET_PRIVATE(o) (xb_builder_source_get_instance_private(o)) |
37 | | |
38 | | typedef struct { |
39 | | gchar *content_type; |
40 | | XbBuilderSourceAdapterFunc func_adapter; |
41 | | gpointer user_data; |
42 | | GDestroyNotify user_data_free; |
43 | | gboolean is_simple; |
44 | | } XbBuilderSourceAdapter; |
45 | | |
46 | | static XbBuilderSourceAdapter * |
47 | | xb_builder_source_get_adapter_by_mime(XbBuilderSource *self, const gchar *content_type) |
48 | 0 | { |
49 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
50 | 0 | for (guint i = 0; i < priv->adapters->len; i++) { |
51 | 0 | XbBuilderSourceAdapter *item = g_ptr_array_index(priv->adapters, i); |
52 | 0 | if (item->func_adapter == NULL) |
53 | 0 | continue; |
54 | 0 | if (g_strcmp0(item->content_type, content_type) == 0) |
55 | 0 | return item; |
56 | 0 | } |
57 | 0 | return NULL; |
58 | 0 | } |
59 | | |
60 | | /** |
61 | | * xb_builder_source_load_file: |
62 | | * @self: a #XbBuilderSource |
63 | | * @file: a #GFile |
64 | | * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT |
65 | | * @cancellable: a #GCancellable, or %NULL |
66 | | * @error: the #GError, or %NULL |
67 | | * |
68 | | * Loads an optionally compressed XML file to build a #XbSilo. |
69 | | * |
70 | | * Returns: %TRUE for success |
71 | | * |
72 | | * Since: 0.1.1 |
73 | | **/ |
74 | | gboolean |
75 | | xb_builder_source_load_file(XbBuilderSource *self, |
76 | | GFile *file, |
77 | | XbBuilderSourceFlags flags, |
78 | | GCancellable *cancellable, |
79 | | GError **error) |
80 | 0 | { |
81 | 0 | const gchar *content_type = NULL; |
82 | 0 | guint32 ctime_usec; |
83 | 0 | guint64 ctime; |
84 | 0 | g_autofree gchar *fn = NULL; |
85 | 0 | g_autoptr(GFileInfo) fileinfo = NULL; |
86 | 0 | g_autoptr(GString) guid = NULL; |
87 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
88 | |
|
89 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), FALSE); |
90 | 0 | g_return_val_if_fail(G_IS_FILE(file), FALSE); |
91 | 0 | g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); |
92 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
93 | | |
94 | | /* what kind of file is this */ |
95 | 0 | fileinfo = g_file_query_info(file, |
96 | 0 | G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE |
97 | 0 | "," G_FILE_ATTRIBUTE_TIME_CHANGED |
98 | 0 | "," G_FILE_ATTRIBUTE_TIME_CHANGED_USEC, |
99 | 0 | G_FILE_QUERY_INFO_NONE, |
100 | 0 | cancellable, |
101 | 0 | error); |
102 | 0 | if (fileinfo == NULL) |
103 | 0 | return FALSE; |
104 | | |
105 | | /* add data to GUID */ |
106 | 0 | fn = g_file_get_path(file); |
107 | 0 | guid = g_string_new(fn); |
108 | 0 | ctime = g_file_info_get_attribute_uint64(fileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED); |
109 | 0 | if (ctime != 0) |
110 | 0 | g_string_append_printf(guid, ":ctime=%" G_GUINT64_FORMAT, ctime); |
111 | 0 | ctime_usec = g_file_info_get_attribute_uint32(fileinfo, G_FILE_ATTRIBUTE_TIME_CHANGED_USEC); |
112 | 0 | if (ctime_usec != 0) |
113 | 0 | g_string_append_printf(guid, ".%" G_GUINT32_FORMAT, ctime_usec); |
114 | 0 | priv->guid = g_string_free(g_steal_pointer(&guid), FALSE); |
115 | | |
116 | | /* check content type of file */ |
117 | 0 | content_type = |
118 | 0 | g_file_info_get_attribute_string(fileinfo, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE); |
119 | 0 | if (content_type == NULL) { |
120 | 0 | g_set_error_literal(error, |
121 | 0 | G_IO_ERROR, |
122 | 0 | G_IO_ERROR_NOT_SUPPORTED, |
123 | 0 | "cannot get content type for file"); |
124 | 0 | return FALSE; |
125 | 0 | } |
126 | | |
127 | | /* success */ |
128 | 0 | priv->flags = flags; |
129 | 0 | priv->content_type = g_strdup(content_type); |
130 | 0 | priv->file = g_object_ref(file); |
131 | 0 | return TRUE; |
132 | 0 | } |
133 | | |
134 | | /** |
135 | | * xb_builder_source_set_info: |
136 | | * @self: a #XbBuilderSource |
137 | | * @info: (allow-none): a #XbBuilderNode |
138 | | * |
139 | | * Sets an optional information metadata node on the root node. |
140 | | * |
141 | | * Since: 0.1.0 |
142 | | **/ |
143 | | void |
144 | | xb_builder_source_set_info(XbBuilderSource *self, XbBuilderNode *info) |
145 | 0 | { |
146 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
147 | 0 | g_return_if_fail(XB_IS_BUILDER_SOURCE(self)); |
148 | 0 | g_set_object(&priv->info, info); |
149 | 0 | } |
150 | | |
151 | | /** |
152 | | * xb_builder_source_set_prefix: |
153 | | * @self: a #XbBuilderSource |
154 | | * @prefix: (allow-none): an XPath prefix, e.g. `installed` |
155 | | * |
156 | | * Sets an optional prefix on the root node. This makes any nodes added |
157 | | * using this source reside under a common shared parent node. |
158 | | * |
159 | | * Since: 0.1.0 |
160 | | **/ |
161 | | void |
162 | | xb_builder_source_set_prefix(XbBuilderSource *self, const gchar *prefix) |
163 | 0 | { |
164 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
165 | 0 | g_return_if_fail(XB_IS_BUILDER_SOURCE(self)); |
166 | 0 | g_free(priv->prefix); |
167 | 0 | priv->prefix = g_strdup(prefix); |
168 | 0 | } |
169 | | |
170 | | /** |
171 | | * xb_builder_source_load_xml: |
172 | | * @self: a #XbBuilderSource |
173 | | * @xml: XML data |
174 | | * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT |
175 | | * @error: the #GError, or %NULL |
176 | | * |
177 | | * Loads XML data and begins to build a #XbSilo. |
178 | | * |
179 | | * Returns: %TRUE for success |
180 | | * |
181 | | * Since: 0.1.1 |
182 | | **/ |
183 | | gboolean |
184 | | xb_builder_source_load_xml(XbBuilderSource *self, |
185 | | const gchar *xml, |
186 | | XbBuilderSourceFlags flags, |
187 | | GError **error) |
188 | 0 | { |
189 | 0 | g_autoptr(GBytes) blob = NULL; |
190 | 0 | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA1); |
191 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
192 | |
|
193 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), FALSE); |
194 | 0 | g_return_val_if_fail(xml != NULL, FALSE); |
195 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
196 | | |
197 | | /* add a GUID of the SHA1 hash of the entire string */ |
198 | 0 | g_checksum_update(csum, (const guchar *)xml, -1); |
199 | 0 | priv->guid = g_strdup(g_checksum_get_string(csum)); |
200 | | |
201 | | /* create input stream */ |
202 | 0 | blob = g_bytes_new(xml, strlen(xml)); |
203 | 0 | priv->istream = g_memory_input_stream_new_from_bytes(blob); |
204 | 0 | if (priv->istream == NULL) |
205 | 0 | return FALSE; |
206 | | |
207 | | /* success */ |
208 | 0 | priv->flags = flags; |
209 | 0 | return TRUE; |
210 | 0 | } |
211 | | |
212 | | /** |
213 | | * xb_builder_source_load_bytes: |
214 | | * @self: a #XbBuilderSource |
215 | | * @bytes: a #GBytes |
216 | | * @flags: some #XbBuilderSourceFlags, e.g. %XB_BUILDER_SOURCE_FLAG_LITERAL_TEXT |
217 | | * @error: the #GError, or %NULL |
218 | | * |
219 | | * Loads XML data and begins to build a #XbSilo. |
220 | | * |
221 | | * Returns: %TRUE for success |
222 | | * |
223 | | * Since: 0.1.2 |
224 | | **/ |
225 | | gboolean |
226 | | xb_builder_source_load_bytes(XbBuilderSource *self, |
227 | | GBytes *bytes, |
228 | | XbBuilderSourceFlags flags, |
229 | | GError **error) |
230 | 0 | { |
231 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
232 | 0 | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA1); |
233 | |
|
234 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), FALSE); |
235 | 0 | g_return_val_if_fail(bytes != NULL, FALSE); |
236 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
237 | | |
238 | | /* add a GUID of the SHA1 hash of the entire blob */ |
239 | 0 | g_checksum_update(csum, |
240 | 0 | (const guchar *)g_bytes_get_data(bytes, NULL), |
241 | 0 | (gssize)g_bytes_get_size(bytes)); |
242 | 0 | priv->guid = g_strdup(g_checksum_get_string(csum)); |
243 | | |
244 | | /* create input stream */ |
245 | 0 | priv->istream = g_memory_input_stream_new_from_bytes(bytes); |
246 | 0 | if (priv->istream == NULL) |
247 | 0 | return FALSE; |
248 | | |
249 | | /* success */ |
250 | 0 | priv->flags = flags; |
251 | 0 | return TRUE; |
252 | 0 | } |
253 | | |
254 | | /** |
255 | | * xb_builder_source_add_fixup: |
256 | | * @self: a #XbBuilderSource |
257 | | * @fixup: a #XbBuilderFixup |
258 | | * |
259 | | * Adds a function that will get run on every #XbBuilderNode compile creates |
260 | | * with this source. |
261 | | * |
262 | | * Since: 0.1.3 |
263 | | **/ |
264 | | void |
265 | | xb_builder_source_add_fixup(XbBuilderSource *self, XbBuilderFixup *fixup) |
266 | 0 | { |
267 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
268 | 0 | g_return_if_fail(XB_IS_BUILDER_SOURCE(self)); |
269 | 0 | g_return_if_fail(XB_IS_BUILDER_FIXUP(fixup)); |
270 | 0 | g_ptr_array_add(priv->fixups, g_object_ref(fixup)); |
271 | 0 | } |
272 | | |
273 | | static void |
274 | | xb_builder_source_init_adapter(XbBuilderSource *self, |
275 | | const gchar *content_types, |
276 | | XbBuilderSourceAdapterFunc func, |
277 | | gpointer user_data, |
278 | | GDestroyNotify user_data_free, |
279 | | gboolean is_simple) |
280 | 0 | { |
281 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
282 | 0 | g_auto(GStrv) split = NULL; |
283 | |
|
284 | 0 | g_return_if_fail(XB_IS_BUILDER_SOURCE(self)); |
285 | 0 | g_return_if_fail(content_types != NULL); |
286 | 0 | g_return_if_fail(func != NULL); |
287 | | |
288 | | /* add each */ |
289 | 0 | split = g_strsplit(content_types, ",", -1); |
290 | 0 | for (guint i = 0; split[i] != NULL; i++) { |
291 | 0 | XbBuilderSourceAdapter *item; |
292 | 0 | item = g_slice_new0(XbBuilderSourceAdapter); |
293 | 0 | item->content_type = g_strdup(split[i]); |
294 | 0 | item->func_adapter = func; |
295 | 0 | item->user_data = user_data; |
296 | 0 | item->user_data_free = user_data_free; |
297 | 0 | item->is_simple = is_simple; |
298 | 0 | g_ptr_array_add(priv->adapters, item); |
299 | 0 | } |
300 | 0 | } |
301 | | |
302 | | /** |
303 | | * xb_builder_source_add_adapter: |
304 | | * @self: a #XbBuilderSource |
305 | | * @content_types: mimetypes, e.g. `application/x-desktop,application/gzip` |
306 | | * @func: a callback, or %NULL |
307 | | * @user_data: user pointer to pass to @func, or %NULL |
308 | | * @user_data_free: a function which gets called to free @user_data, or %NULL |
309 | | * |
310 | | * Adds a function that can be used to convert streams loaded with |
311 | | * xb_builder_source_load_xml(). |
312 | | * |
313 | | * This will decompress multiple layers of content, for instance decompressing a gzip stream into a |
314 | | * different content type that can then be parsed. Use xb_builder_source_add_simple_adapter() when |
315 | | * this recursive behaviour is not desired. |
316 | | * |
317 | | * Since: 0.1.7 |
318 | | **/ |
319 | | void |
320 | | xb_builder_source_add_adapter(XbBuilderSource *self, |
321 | | const gchar *content_types, |
322 | | XbBuilderSourceAdapterFunc func, |
323 | | gpointer user_data, |
324 | | GDestroyNotify user_data_free) |
325 | 0 | { |
326 | 0 | xb_builder_source_init_adapter(self, content_types, func, user_data, user_data_free, FALSE); |
327 | 0 | } |
328 | | |
329 | | /** |
330 | | * xb_builder_source_add_simple_adapter: |
331 | | * @self: a #XbBuilderSource |
332 | | * @content_types: mimetypes, e.g. `application/x-desktop,application/gzip` |
333 | | * @func: a callback, or %NULL |
334 | | * @user_data: user pointer to pass to @func, or %NULL |
335 | | * @user_data_free: a function which gets called to free @user_data, or %NULL |
336 | | * |
337 | | * Adds a function that can be used to convert streams loaded with |
338 | | * xb_builder_source_load_xml(). |
339 | | * |
340 | | * This function is similar to xb_builder_source_add_adapter() but is limited to one "layer" of |
341 | | * content, for instance handling application/xml or a single simple type added using |
342 | | * xb_builder_source_add_adapter(). |
343 | | * |
344 | | * Since: 0.1.15 |
345 | | **/ |
346 | | void |
347 | | xb_builder_source_add_simple_adapter(XbBuilderSource *self, |
348 | | const gchar *content_types, |
349 | | XbBuilderSourceAdapterFunc func, |
350 | | gpointer user_data, |
351 | | GDestroyNotify user_data_free) |
352 | 0 | { |
353 | 0 | xb_builder_source_init_adapter(self, content_types, func, user_data, user_data_free, TRUE); |
354 | 0 | } |
355 | | |
356 | | gboolean |
357 | | xb_builder_source_fixup(XbBuilderSource *self, XbBuilderNode *bn, GError **error) |
358 | 0 | { |
359 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
360 | 0 | for (guint i = 0; i < priv->fixups->len; i++) { |
361 | 0 | XbBuilderFixup *fixup = g_ptr_array_index(priv->fixups, i); |
362 | 0 | if (!xb_builder_fixup_node(fixup, bn, error)) |
363 | 0 | return FALSE; |
364 | 0 | } |
365 | 0 | return TRUE; |
366 | 0 | } |
367 | | |
368 | | static gboolean |
369 | | xb_builder_source_info_guid_cb(XbBuilderNode *bn, gpointer data) |
370 | 0 | { |
371 | 0 | GString *str = (GString *)data; |
372 | 0 | if (xb_builder_node_get_text(bn) != NULL) { |
373 | 0 | g_string_append_printf(str, |
374 | 0 | ":%s=%s", |
375 | 0 | xb_builder_node_get_element(bn), |
376 | 0 | xb_builder_node_get_text(bn)); |
377 | 0 | } |
378 | 0 | return FALSE; |
379 | 0 | } |
380 | | |
381 | | gchar * |
382 | | xb_builder_source_get_guid(XbBuilderSource *self) |
383 | 0 | { |
384 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
385 | 0 | g_autoptr(GString) str = g_string_new(priv->guid); |
386 | |
|
387 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), NULL); |
388 | | |
389 | | /* append function IDs */ |
390 | 0 | for (guint i = 0; i < priv->fixups->len; i++) { |
391 | 0 | XbBuilderFixup *fixup = g_ptr_array_index(priv->fixups, i); |
392 | 0 | g_autofree gchar *tmp = xb_builder_fixup_get_guid(fixup); |
393 | 0 | g_string_append_printf(str, ":%s", tmp); |
394 | 0 | } |
395 | | |
396 | | /* append any info */ |
397 | 0 | if (priv->info != NULL) { |
398 | 0 | xb_builder_node_traverse(priv->info, |
399 | 0 | G_PRE_ORDER, |
400 | 0 | G_TRAVERSE_ALL, |
401 | 0 | -1, |
402 | 0 | xb_builder_source_info_guid_cb, |
403 | 0 | str); |
404 | 0 | } |
405 | | |
406 | | /* append prefix */ |
407 | 0 | if (priv->prefix != NULL) |
408 | 0 | g_string_append_printf(str, ":prefix=%s", priv->prefix); |
409 | 0 | return g_string_free(g_steal_pointer(&str), FALSE); |
410 | 0 | } |
411 | | |
412 | | const gchar * |
413 | | xb_builder_source_get_prefix(XbBuilderSource *self) |
414 | 0 | { |
415 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
416 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), NULL); |
417 | 0 | return priv->prefix; |
418 | 0 | } |
419 | | |
420 | | XbBuilderNode * |
421 | | xb_builder_source_get_info(XbBuilderSource *self) |
422 | 0 | { |
423 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
424 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), NULL); |
425 | 0 | return priv->info; |
426 | 0 | } |
427 | | |
428 | | /* converts foo.xml.gz to foo.xml */ |
429 | | static void |
430 | | xb_builder_source_remove_last_extension(gchar *basename) |
431 | 0 | { |
432 | 0 | gchar *tmp = g_strrstr(basename, "."); |
433 | 0 | if (tmp != NULL) |
434 | 0 | *tmp = '\0'; |
435 | 0 | } |
436 | | |
437 | | GInputStream * |
438 | | xb_builder_source_get_istream(XbBuilderSource *self, GCancellable *cancellable, GError **error) |
439 | 0 | { |
440 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
441 | 0 | g_autofree gchar *basename = NULL; |
442 | 0 | g_autoptr(GInputStream) istream = NULL; |
443 | 0 | GFile *file; |
444 | |
|
445 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), NULL); |
446 | | |
447 | | /* nothing required */ |
448 | 0 | if (priv->istream != NULL) |
449 | 0 | return g_object_ref(priv->istream); |
450 | | |
451 | | /* convert the file to a GFileInputStream */ |
452 | 0 | istream = G_INPUT_STREAM(g_file_read(priv->file, cancellable, error)); |
453 | 0 | if (istream == NULL) |
454 | 0 | return NULL; |
455 | | |
456 | | /* run the content type handlers until we get application/xml */ |
457 | 0 | basename = g_file_get_basename(priv->file); |
458 | 0 | file = priv->file; |
459 | |
|
460 | 0 | do { |
461 | 0 | XbBuilderSourceAdapter *item; |
462 | 0 | g_autofree gchar *content_type = NULL; |
463 | 0 | g_autoptr(GInputStream) istream_tmp = NULL; |
464 | 0 | g_autoptr(XbBuilderSourceCtx) ctx = xb_builder_source_ctx_new(file, istream); |
465 | | |
466 | | /* get the content type of the stream */ |
467 | 0 | xb_builder_source_ctx_set_filename(ctx, basename); |
468 | 0 | content_type = xb_builder_source_ctx_get_content_type(ctx, cancellable, error); |
469 | 0 | g_debug("detected content type of %s to be %s", basename, content_type); |
470 | 0 | if (content_type == NULL) |
471 | 0 | return NULL; |
472 | 0 | if (g_strcmp0(content_type, "application/xml") == 0) |
473 | 0 | break; |
474 | | /* Also accept the text/xml alias, just in case the user’s content-type database is |
475 | | * slightly broken (application/xml should normally be what’s used): */ |
476 | 0 | if (g_strcmp0(content_type, "text/xml") == 0) |
477 | 0 | break; |
478 | | |
479 | | /* convert the stream */ |
480 | 0 | item = xb_builder_source_get_adapter_by_mime(self, content_type); |
481 | 0 | if (item == NULL || item->func_adapter == NULL) { |
482 | 0 | g_set_error(error, |
483 | 0 | G_IO_ERROR, |
484 | 0 | G_IO_ERROR_NOT_SUPPORTED, |
485 | 0 | "cannot process content type %s", |
486 | 0 | content_type); |
487 | 0 | return NULL; |
488 | 0 | } |
489 | 0 | istream_tmp = item->func_adapter(self, ctx, item->user_data, cancellable, error); |
490 | 0 | if (istream_tmp == NULL) |
491 | 0 | return NULL; |
492 | 0 | xb_builder_source_remove_last_extension(basename); |
493 | 0 | g_set_object(&istream, istream_tmp); |
494 | | |
495 | | /* the #GFile is only useful for the outermost input stream, |
496 | | * for example it points to the .tar.gz file, while inner input |
497 | | * streams are the .xml output of decompressing the .gz in |
498 | | * memory and can’t be represented as a #GFile */ |
499 | 0 | file = NULL; |
500 | |
|
501 | 0 | if (item->is_simple) |
502 | 0 | break; |
503 | 0 | } while (TRUE); |
504 | 0 | return g_steal_pointer(&istream); |
505 | 0 | } |
506 | | |
507 | | GFile * |
508 | | xb_builder_source_get_file(XbBuilderSource *self) |
509 | 0 | { |
510 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
511 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), NULL); |
512 | 0 | return priv->file; |
513 | 0 | } |
514 | | |
515 | | XbBuilderSourceFlags |
516 | | xb_builder_source_get_flags(XbBuilderSource *self) |
517 | 0 | { |
518 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
519 | 0 | g_return_val_if_fail(XB_IS_BUILDER_SOURCE(self), 0); |
520 | 0 | return priv->flags; |
521 | 0 | } |
522 | | |
523 | | static GInputStream * |
524 | | xb_builder_source_load_gzip_cb(XbBuilderSource *self, |
525 | | XbBuilderSourceCtx *ctx, |
526 | | gpointer user_data, |
527 | | GCancellable *cancellable, |
528 | | GError **error) |
529 | 0 | { |
530 | 0 | GInputStream *istream = xb_builder_source_ctx_get_stream(ctx); |
531 | 0 | g_autoptr(GConverter) conv = NULL; |
532 | 0 | conv = G_CONVERTER(g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP)); |
533 | 0 | return g_converter_input_stream_new(istream, conv); |
534 | 0 | } |
535 | | |
536 | | #ifdef HAVE_LZMA |
537 | | static GInputStream * |
538 | | xb_builder_source_load_lzma_cb(XbBuilderSource *self, |
539 | | XbBuilderSourceCtx *ctx, |
540 | | gpointer user_data, |
541 | | GCancellable *cancellable, |
542 | | GError **error) |
543 | 0 | { |
544 | 0 | GInputStream *istream = xb_builder_source_ctx_get_stream(ctx); |
545 | 0 | g_autoptr(GConverter) conv = G_CONVERTER(xb_lzma_decompressor_new()); |
546 | 0 | return g_converter_input_stream_new(istream, conv); |
547 | 0 | } |
548 | | #endif |
549 | | |
550 | | #ifdef HAVE_ZSTD |
551 | | static GInputStream * |
552 | | xb_builder_source_load_zstd_cb(XbBuilderSource *self, |
553 | | XbBuilderSourceCtx *ctx, |
554 | | gpointer user_data, |
555 | | GCancellable *cancellable, |
556 | | GError **error) |
557 | 0 | { |
558 | 0 | GInputStream *istream = xb_builder_source_ctx_get_stream(ctx); |
559 | 0 | g_autoptr(GConverter) conv = G_CONVERTER(xb_zstd_decompressor_new()); |
560 | 0 | return g_converter_input_stream_new(istream, conv); |
561 | 0 | } |
562 | | #endif |
563 | | static void |
564 | | xb_builder_source_adapter_free(XbBuilderSourceAdapter *item) |
565 | 0 | { |
566 | 0 | if (item->user_data_free != NULL) |
567 | 0 | item->user_data_free(item->user_data); |
568 | 0 | g_free(item->content_type); |
569 | 0 | g_slice_free(XbBuilderSourceAdapter, item); |
570 | 0 | } |
571 | | |
572 | | static void |
573 | | xb_builder_source_finalize(GObject *obj) |
574 | 0 | { |
575 | 0 | XbBuilderSource *self = XB_BUILDER_SOURCE(obj); |
576 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
577 | |
|
578 | 0 | if (priv->istream != NULL) |
579 | 0 | g_object_unref(priv->istream); |
580 | 0 | if (priv->info != NULL) |
581 | 0 | g_object_unref(priv->info); |
582 | 0 | if (priv->file != NULL) |
583 | 0 | g_object_unref(priv->file); |
584 | 0 | g_ptr_array_unref(priv->fixups); |
585 | 0 | g_ptr_array_unref(priv->adapters); |
586 | 0 | g_free(priv->guid); |
587 | 0 | g_free(priv->prefix); |
588 | 0 | g_free(priv->content_type); |
589 | |
|
590 | 0 | G_OBJECT_CLASS(xb_builder_source_parent_class)->finalize(obj); |
591 | 0 | } |
592 | | |
593 | | static void |
594 | | xb_builder_source_class_init(XbBuilderSourceClass *klass) |
595 | 0 | { |
596 | 0 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
597 | 0 | object_class->finalize = xb_builder_source_finalize; |
598 | 0 | } |
599 | | |
600 | | static void |
601 | | xb_builder_source_init(XbBuilderSource *self) |
602 | 0 | { |
603 | 0 | XbBuilderSourcePrivate *priv = GET_PRIVATE(self); |
604 | 0 | priv->fixups = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); |
605 | 0 | priv->adapters = |
606 | 0 | g_ptr_array_new_with_free_func((GDestroyNotify)xb_builder_source_adapter_free); |
607 | 0 | xb_builder_source_add_adapter(self, |
608 | 0 | "application/gzip,application/x-gzip,org.gnu.gnu-zip-archive", |
609 | 0 | xb_builder_source_load_gzip_cb, |
610 | 0 | NULL, |
611 | 0 | NULL); |
612 | 0 | #ifdef HAVE_LZMA |
613 | 0 | xb_builder_source_add_adapter(self, |
614 | 0 | "application/x-xz,org.tukaani.xz-archive", |
615 | 0 | xb_builder_source_load_lzma_cb, |
616 | 0 | NULL, |
617 | 0 | NULL); |
618 | 0 | #endif |
619 | 0 | #ifdef HAVE_ZSTD |
620 | 0 | xb_builder_source_add_adapter(self, |
621 | 0 | "application/zstd", |
622 | 0 | xb_builder_source_load_zstd_cb, |
623 | 0 | NULL, |
624 | 0 | NULL); |
625 | 0 | #endif |
626 | 0 | } |
627 | | |
628 | | /** |
629 | | * xb_builder_source_new: |
630 | | * |
631 | | * Creates a new builder source. |
632 | | * |
633 | | * Returns: a new #XbBuilderSource |
634 | | * |
635 | | * Since: 0.1.1 |
636 | | **/ |
637 | | XbBuilderSource * |
638 | | xb_builder_source_new(void) |
639 | 0 | { |
640 | 0 | return g_object_new(XB_TYPE_BUILDER_SOURCE, NULL); |
641 | 0 | } |