Coverage Report

Created: 2026-09-03 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fwupd/libfwupd/fwupd-jcat-item.c
Line
Count
Source
1
/*
2
 * Copyright 2020 Richard Hughes <richard@hughsie.com>
3
 *
4
 * SPDX-License-Identifier: LGPL-2.1-or-later
5
 */
6
7
#include "config.h"
8
9
#include "fwupd-codec.h"
10
#include "fwupd-error.h"
11
#include "fwupd-jcat-item.h"
12
#include "fwupd-json-array.h"
13
14
struct _FwupdJcatItem {
15
  GObject parent_instance;
16
  gchar *id;
17
  GPtrArray *blobs;
18
  GPtrArray *alias_ids;
19
};
20
21
static void
22
fwupd_jcat_item_codec_iface_init(FwupdCodecInterface *iface);
23
24
0
G_DEFINE_TYPE_EXTENDED(FwupdJcatItem,
25
0
           fwupd_jcat_item,
26
0
           G_TYPE_OBJECT,
27
0
           0,
28
0
           G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fwupd_jcat_item_codec_iface_init));
29
0
30
0
static void
31
0
fwupd_jcat_item_add_string(FwupdCodec *codec, guint idt, GString *str)
32
0
{
33
0
  FwupdJcatItem *self = FWUPD_JCAT_ITEM(codec);
34
35
0
  fwupd_codec_string_append(str, idt, "ID", self->id);
36
0
  for (guint i = 0; i < self->alias_ids->len; i++) {
37
0
    const gchar *alias_id = g_ptr_array_index(self->alias_ids, i);
38
0
    fwupd_codec_string_append(str, idt, "AliasId", alias_id);
39
0
  }
40
0
  for (guint i = 0; i < self->blobs->len; i++) {
41
0
    FwupdJcatBlob *blob = g_ptr_array_index(self->blobs, i);
42
0
    fwupd_codec_add_string(FWUPD_CODEC(blob), idt, str);
43
0
  }
44
0
}
45
46
static void
47
fwupd_jcat_item_set_id(FwupdJcatItem *self, const gchar *id)
48
0
{
49
0
  g_return_if_fail(FWUPD_IS_JCAT_ITEM(self));
50
0
  g_return_if_fail(id != NULL);
51
0
  g_set_str(&self->id, id);
52
0
}
53
54
static gboolean
55
fwupd_jcat_item_from_json(FwupdCodec *codec, FwupdJsonObject *json_obj, GError **error)
56
0
{
57
0
  FwupdJcatItem *self = FWUPD_JCAT_ITEM(codec);
58
0
  const gchar *id;
59
60
  /* get ID */
61
0
  id = fwupd_json_object_get_string(json_obj, "Id", error);
62
0
  if (id == NULL)
63
0
    return FALSE;
64
0
  fwupd_jcat_item_set_id(self, id);
65
66
  /* get blobs */
67
0
  if (fwupd_json_object_has_node(json_obj, "Blobs")) {
68
0
    g_autoptr(FwupdJsonArray) json_array = NULL;
69
0
    json_array = fwupd_json_object_get_array(json_obj, "Blobs", error);
70
0
    if (json_array == NULL)
71
0
      return FALSE;
72
0
    for (guint i = 0; i < fwupd_json_array_get_size(json_array); i++) {
73
0
      g_autoptr(FwupdJsonObject) json_item = NULL;
74
0
      g_autoptr(FwupdJcatBlob) blob = g_object_new(FWUPD_TYPE_JCAT_BLOB, NULL);
75
76
0
      json_item = fwupd_json_array_get_object(json_array, i, error);
77
0
      if (json_item == NULL)
78
0
        return FALSE;
79
0
      if (!fwupd_codec_from_json(FWUPD_CODEC(blob), json_item, error))
80
0
        return FALSE;
81
0
      fwupd_jcat_item_add_blob(self, blob);
82
0
    }
83
0
  }
84
85
  /* get alias_ids */
86
0
  if (fwupd_json_object_has_node(json_obj, "AliasIds")) {
87
0
    g_autoptr(FwupdJsonArray) json_array = NULL;
88
0
    json_array = fwupd_json_object_get_array(json_obj, "AliasIds", error);
89
0
    if (json_array == NULL)
90
0
      return FALSE;
91
0
    for (guint i = 0; i < fwupd_json_array_get_size(json_array); i++) {
92
0
      const gchar *alias_id;
93
0
      alias_id = fwupd_json_array_get_string(json_array, i, error);
94
0
      if (alias_id == NULL)
95
0
        return FALSE;
96
0
      fwupd_jcat_item_add_alias_id(self, alias_id);
97
0
    }
98
0
  }
99
100
  /* success */
101
0
  return TRUE;
102
0
}
103
104
static void
105
fwupd_jcat_item_add_json(FwupdCodec *codec, FwupdJsonObject *json_obj, FwupdCodecFlags flags)
106
0
{
107
0
  FwupdJcatItem *self = FWUPD_JCAT_ITEM(codec);
108
109
  /* add metadata */
110
0
  fwupd_json_object_add_string(json_obj, "Id", self->id);
111
112
  /* add alias_ids */
113
0
  if (self->alias_ids->len > 0) {
114
0
    g_autoptr(FwupdJsonArray) json_array = fwupd_json_array_new();
115
0
    for (guint i = 0; i < self->alias_ids->len; i++) {
116
0
      const gchar *id_tmp = g_ptr_array_index(self->alias_ids, i);
117
0
      fwupd_json_array_add_string(json_array, id_tmp);
118
0
    }
119
0
    fwupd_json_object_add_array(json_obj, "AliasIds", json_array);
120
0
  }
121
122
  /* add items */
123
0
  if (self->blobs->len > 0) {
124
0
    g_autoptr(FwupdJsonArray) json_array = fwupd_json_array_new();
125
0
    for (guint i = 0; i < self->blobs->len; i++) {
126
0
      FwupdJcatBlob *blob = g_ptr_array_index(self->blobs, i);
127
0
      g_autoptr(FwupdJsonObject) json_blob = fwupd_json_object_new();
128
0
      fwupd_codec_to_json(FWUPD_CODEC(blob), json_blob, flags);
129
0
      fwupd_json_array_add_object(json_array, json_blob);
130
0
    }
131
0
    fwupd_json_object_add_array(json_obj, "Blobs", json_array);
132
0
  }
133
0
}
134
135
/**
136
 * fwupd_jcat_item_get_blobs:
137
 * @self: #FwupdJcatItem
138
 *
139
 * Gets all the blobs for this item.
140
 *
141
 * Returns: (transfer container) (element-type FwupdJcatBlob): blobs
142
 *
143
 * Since: 2.1.3
144
 **/
145
GPtrArray *
146
fwupd_jcat_item_get_blobs(FwupdJcatItem *self)
147
0
{
148
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
149
0
  return g_ptr_array_ref(self->blobs);
150
0
}
151
152
/**
153
 * fwupd_jcat_item_get_blobs_by_kind:
154
 * @self: #FwupdJcatItem
155
 * @kind: #FwupdJcatBlobKind, e.g. %FWUPD_JCAT_BLOB_KIND_SHA256
156
 *
157
 * Gets the item blobs by a specific kind.
158
 *
159
 * Returns: (transfer container) (element-type FwupdJcatBlob): blobs
160
 *
161
 * Since: 2.1.3
162
 **/
163
GPtrArray *
164
fwupd_jcat_item_get_blobs_by_kind(FwupdJcatItem *self, FwupdJcatBlobKind kind)
165
0
{
166
0
  g_autoptr(GPtrArray) blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
167
168
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
169
0
  g_return_val_if_fail(kind != FWUPD_JCAT_BLOB_KIND_UNKNOWN, NULL);
170
171
0
  for (guint i = 0; i < self->blobs->len; i++) {
172
0
    FwupdJcatBlob *blob = g_ptr_array_index(self->blobs, i);
173
0
    if (fwupd_jcat_blob_get_kind(blob) == kind)
174
0
      g_ptr_array_add(blobs, g_object_ref(blob));
175
0
  }
176
0
  return g_steal_pointer(&blobs);
177
0
}
178
179
/**
180
 * fwupd_jcat_item_get_blob_by_kind:
181
 * @self: #FwupdJcatItem
182
 * @kind: #FwupdJcatBlobKind, e.g. %FWUPD_JCAT_BLOB_KIND_SHA256
183
 * @error: #GError, or %NULL
184
 *
185
 * Gets the item blobs by a specific kind.
186
 *
187
 * Returns: (transfer full): a blob, or %NULL
188
 *
189
 * Since: 2.1.3
190
 **/
191
FwupdJcatBlob *
192
fwupd_jcat_item_get_blob_by_kind(FwupdJcatItem *self, FwupdJcatBlobKind kind, GError **error)
193
0
{
194
0
  g_autoptr(GPtrArray) target_blobs = NULL;
195
196
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
197
0
  g_return_val_if_fail(kind != FWUPD_JCAT_BLOB_KIND_UNKNOWN, NULL);
198
199
0
  target_blobs = fwupd_jcat_item_get_blobs_by_kind(self, kind);
200
0
  if (target_blobs->len == 0) {
201
0
    g_set_error(error,
202
0
          FWUPD_ERROR,
203
0
          FWUPD_ERROR_INVALID_DATA,
204
0
          "no existing checksum of type %s",
205
0
          fwupd_jcat_blob_kind_to_string(kind));
206
0
    return NULL;
207
0
  }
208
0
  if (target_blobs->len > 1) {
209
0
    g_set_error(error,
210
0
          FWUPD_ERROR,
211
0
          FWUPD_ERROR_INVALID_DATA,
212
0
          "multiple checksums of type %s",
213
0
          fwupd_jcat_blob_kind_to_string(kind));
214
0
    return NULL;
215
0
  }
216
0
  return g_object_ref(FWUPD_JCAT_BLOB(g_ptr_array_index(target_blobs, 0)));
217
0
}
218
219
/**
220
 * fwupd_jcat_item_add_blob:
221
 * @self: #FwupdJcatItem
222
 * @blob: (not nullable): #FwupdJcatBlob
223
 *
224
 * Adds a new blob to the item.
225
 *
226
 * Since: 2.1.3
227
 **/
228
void
229
fwupd_jcat_item_add_blob(FwupdJcatItem *self, FwupdJcatBlob *blob)
230
0
{
231
0
  g_return_if_fail(FWUPD_IS_JCAT_ITEM(self));
232
0
  g_return_if_fail(FWUPD_IS_JCAT_BLOB(blob));
233
234
  /* add */
235
0
  g_ptr_array_add(self->blobs, g_object_ref(blob));
236
0
}
237
238
/**
239
 * fwupd_jcat_item_get_id:
240
 * @self: #FwupdJcatItem
241
 *
242
 * Returns the item ID.
243
 *
244
 * Returns: (transfer none): string
245
 *
246
 * Since: 2.1.3
247
 **/
248
const gchar *
249
fwupd_jcat_item_get_id(FwupdJcatItem *self)
250
0
{
251
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
252
0
  return self->id;
253
0
}
254
255
/**
256
 * fwupd_jcat_item_get_id_safe:
257
 * @self: a #FwupdJcatItem
258
 * @error: (nullable): optional return location for an error
259
 *
260
 * Returns the item ID, if safe to use as a path.
261
 *
262
 * Returns: string
263
 *
264
 * Since: 2.1.3
265
 **/
266
const gchar *
267
fwupd_jcat_item_get_id_safe(FwupdJcatItem *self, GError **error)
268
0
{
269
0
  g_autofree gchar *id_basename = NULL;
270
271
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
272
0
  g_return_val_if_fail(error == NULL || *error == NULL, NULL);
273
274
  /* sanity check */
275
0
  if (self->id == NULL || self->id[0] == '\0') {
276
0
    g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "ID not set");
277
0
    return NULL;
278
0
  }
279
280
  /* verify there is no path component */
281
0
  id_basename = g_path_get_basename(self->id);
282
0
  if (g_strcmp0(self->id, id_basename) != 0 ||
283
0
      g_strcmp0(id_basename, G_DIR_SEPARATOR_S) == 0 || g_strcmp0(id_basename, "..") == 0 ||
284
0
      g_strcmp0(id_basename, ".") == 0) {
285
0
    g_set_error_literal(error,
286
0
            FWUPD_ERROR,
287
0
            FWUPD_ERROR_INVALID_FILE,
288
0
            "ID cannot contain path components");
289
0
    return NULL;
290
0
  }
291
292
  /* success */
293
0
  return self->id;
294
0
}
295
296
/**
297
 * fwupd_jcat_item_add_alias_id:
298
 * @self: #FwupdJcatItem
299
 * @id: (not nullable): An item ID alias, typically a file basename
300
 *
301
 * Adds an item alias ID. Alias IDs are matched when using functions such as
302
 * fwupd_jcat_file_get_item_by_id().
303
 *
304
 * Since: 2.1.3
305
 **/
306
void
307
fwupd_jcat_item_add_alias_id(FwupdJcatItem *self, const gchar *id)
308
0
{
309
0
  g_return_if_fail(FWUPD_IS_JCAT_ITEM(self));
310
0
  g_return_if_fail(id != NULL);
311
0
  for (guint i = 0; i < self->alias_ids->len; i++) {
312
0
    const gchar *id_tmp = g_ptr_array_index(self->alias_ids, i);
313
0
    if (g_strcmp0(id, id_tmp) == 0)
314
0
      return;
315
0
  }
316
0
  g_ptr_array_add(self->alias_ids, g_strdup(id));
317
0
}
318
319
/**
320
 * fwupd_jcat_item_remove_alias_id:
321
 * @self: #FwupdJcatItem
322
 * @id: (not nullable): An item ID alias, typically a file basename
323
 *
324
 * Removes an item alias ID.
325
 *
326
 * Since: 2.1.3
327
 **/
328
void
329
fwupd_jcat_item_remove_alias_id(FwupdJcatItem *self, const gchar *id)
330
0
{
331
0
  g_return_if_fail(FWUPD_IS_JCAT_ITEM(self));
332
0
  g_return_if_fail(id != NULL);
333
0
  for (guint i = 0; i < self->alias_ids->len; i++) {
334
0
    const gchar *id_tmp = g_ptr_array_index(self->alias_ids, i);
335
0
    if (g_strcmp0(id, id_tmp) == 0) {
336
0
      g_ptr_array_remove(self->alias_ids, (gpointer)id_tmp);
337
0
      return;
338
0
    }
339
0
  }
340
0
}
341
342
/**
343
 * fwupd_jcat_item_get_alias_ids:
344
 * @self: #FwupdJcatItem
345
 *
346
 * Gets the list of alias IDs.
347
 *
348
 * Returns: (transfer container) (element-type utf8): array
349
 *
350
 * Since: 2.1.3
351
 **/
352
GPtrArray *
353
fwupd_jcat_item_get_alias_ids(FwupdJcatItem *self)
354
0
{
355
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), NULL);
356
0
  return g_ptr_array_ref(self->alias_ids);
357
0
}
358
359
/**
360
 * fwupd_jcat_item_has_target:
361
 * @self: #FwupdJcatItem
362
 *
363
 * Finds out if any of the blobs are targeting an internal checksum.
364
 * If this returns with success then the caller might be able to use functions like
365
 * fwupd_jcat_context_verify_target() supplying some target checksums.
366
 *
367
 * Returns: %TRUE on success
368
 *
369
 * Since: 2.1.3
370
 **/
371
gboolean
372
fwupd_jcat_item_has_target(FwupdJcatItem *self)
373
0
{
374
0
  g_return_val_if_fail(FWUPD_IS_JCAT_ITEM(self), FALSE);
375
0
  for (guint i = 0; i < self->blobs->len; i++) {
376
0
    FwupdJcatBlob *blob_tmp = g_ptr_array_index(self->blobs, i);
377
0
    if (fwupd_jcat_blob_get_target(blob_tmp) != FWUPD_JCAT_BLOB_KIND_UNKNOWN)
378
0
      return TRUE;
379
0
  }
380
0
  return FALSE;
381
0
}
382
383
static void
384
fwupd_jcat_item_codec_iface_init(FwupdCodecInterface *iface)
385
0
{
386
0
  iface->add_string = fwupd_jcat_item_add_string;
387
0
  iface->add_json = fwupd_jcat_item_add_json;
388
0
  iface->from_json = fwupd_jcat_item_from_json;
389
0
}
390
391
static void
392
fwupd_jcat_item_finalize(GObject *obj)
393
0
{
394
0
  FwupdJcatItem *self = FWUPD_JCAT_ITEM(obj);
395
0
  g_free(self->id);
396
0
  if (self->blobs != NULL)
397
0
    g_ptr_array_unref(self->blobs);
398
0
  if (self->alias_ids != NULL)
399
0
    g_ptr_array_unref(self->alias_ids);
400
0
  G_OBJECT_CLASS(fwupd_jcat_item_parent_class)->finalize(obj);
401
0
}
402
403
static void
404
fwupd_jcat_item_class_init(FwupdJcatItemClass *klass)
405
0
{
406
0
  GObjectClass *object_class = G_OBJECT_CLASS(klass);
407
0
  object_class->finalize = fwupd_jcat_item_finalize;
408
0
}
409
410
static void
411
fwupd_jcat_item_init(FwupdJcatItem *self)
412
0
{
413
0
  self->blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref);
414
0
  self->alias_ids = g_ptr_array_new_with_free_func(g_free);
415
0
}
416
417
/**
418
 * fwupd_jcat_item_new:
419
 * @id: (not nullable): An item ID, typically a file basename
420
 *
421
 * Creates a new item.
422
 *
423
 * Returns: a #FwupdJcatItem
424
 *
425
 * Since: 2.1.3
426
 **/
427
FwupdJcatItem *
428
fwupd_jcat_item_new(const gchar *id)
429
0
{
430
0
  g_autoptr(FwupdJcatItem) self = g_object_new(FWUPD_TYPE_JCAT_ITEM, NULL);
431
0
  g_return_val_if_fail(id != NULL, NULL);
432
0
  self->id = g_strdup(id);
433
0
  return g_steal_pointer(&self);
434
0
}