Coverage Report

Created: 2025-11-24 06:59

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}