/src/fwupd/libfwupdplugin/fu-device-event.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 2024 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "FuDeviceEvent" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include "fu-device-event-private.h" |
12 | | #include "fu-mem.h" |
13 | | #include "fu-string.h" |
14 | | |
15 | | /** |
16 | | * FuDeviceEvent: |
17 | | * |
18 | | * A device event, used to enumulate hardware. |
19 | | * |
20 | | * See also: [class@FuDevice] |
21 | | */ |
22 | | |
23 | | typedef struct { |
24 | | GType gtype; |
25 | | GRefString *key; |
26 | | gpointer data; |
27 | | GDestroyNotify data_destroy; |
28 | | } FuDeviceEventBlob; |
29 | | |
30 | | struct _FuDeviceEvent { |
31 | | GObject parent_instance; |
32 | | gchar *id; |
33 | | gchar *id_uncompressed; |
34 | | GPtrArray *values; /* element-type FuDeviceEventBlob */ |
35 | | }; |
36 | | |
37 | | static void |
38 | | fu_device_event_codec_iface_init(FwupdCodecInterface *iface); |
39 | | |
40 | 2.26k | G_DEFINE_TYPE_WITH_CODE(FuDeviceEvent, |
41 | 2.26k | fu_device_event, |
42 | 2.26k | G_TYPE_OBJECT, |
43 | 2.26k | G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_device_event_codec_iface_init)) |
44 | 2.26k | |
45 | 2.26k | /* |
46 | 2.26k | * NOTE: We use an event *counter* that gets the next event in the emulation, and this ID is only |
47 | 2.26k | * used as a sanity check in case we have to skip an entry. |
48 | 2.26k | */ |
49 | 2.26k | #define FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE 8 |
50 | | |
51 | | static void |
52 | | fu_device_event_blob_free(FuDeviceEventBlob *blob) |
53 | 282 | { |
54 | 282 | g_ref_string_release(blob->key); |
55 | 282 | if (blob->data_destroy != NULL) |
56 | 282 | blob->data_destroy(blob->data); |
57 | 282 | g_free(blob); |
58 | 282 | } |
59 | | |
60 | | static FuDeviceEventBlob * |
61 | | fu_device_event_blob_new_internal(GType gtype, |
62 | | GRefString *key, |
63 | | gpointer data, |
64 | | GDestroyNotify data_destroy) |
65 | 282 | { |
66 | 282 | FuDeviceEventBlob *blob = g_new0(FuDeviceEventBlob, 1); |
67 | 282 | blob->key = g_ref_string_acquire(key); |
68 | 282 | blob->gtype = gtype; |
69 | 282 | blob->data = data; |
70 | 282 | blob->data_destroy = data_destroy; |
71 | 282 | return blob; |
72 | 282 | } |
73 | | |
74 | | static FuDeviceEventBlob * |
75 | | fu_device_event_blob_new(GType gtype, const gchar *key, gpointer data, GDestroyNotify data_destroy) |
76 | 282 | { |
77 | 282 | g_autoptr(GRefString) key_ref = NULL; |
78 | 282 | const gchar *known_keys[] = { |
79 | 282 | "Data", |
80 | 282 | "DataOut", |
81 | 282 | "Error", |
82 | 282 | "ErrorMsg", |
83 | 282 | "Rc", |
84 | 282 | NULL, |
85 | 282 | }; |
86 | 282 | key_ref = g_strv_contains(known_keys, key) ? g_ref_string_new_intern(key) |
87 | 282 | : g_ref_string_new(key); |
88 | 282 | return fu_device_event_blob_new_internal(gtype, key_ref, data, data_destroy); |
89 | 282 | } |
90 | | |
91 | | /** |
92 | | * fu_device_event_build_id: |
93 | | * @id: a string |
94 | | * |
95 | | * Return the hash of the event ID. |
96 | | * |
97 | | * Returns: string hash prefix |
98 | | * |
99 | | * Since: 2.0.3 |
100 | | **/ |
101 | | gchar * |
102 | | fu_device_event_build_id(const gchar *id) |
103 | 0 | { |
104 | 0 | guint8 buf[20] = {0}; |
105 | 0 | gsize bufsz = sizeof(buf); |
106 | 0 | g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA1); |
107 | 0 | g_autoptr(GString) id_hash = g_string_sized_new(FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE + 1); |
108 | |
|
109 | 0 | g_return_val_if_fail(id != NULL, NULL); |
110 | | |
111 | | /* IMPORTANT: if you're reading this we're not using the SHA1 prefix for any kind of secure |
112 | | * hash, just because it is a tiny string that takes up less memory than the full ID. */ |
113 | 0 | g_checksum_update(csum, (const guchar *)id, strlen(id)); |
114 | 0 | g_checksum_get_digest(csum, buf, &bufsz); |
115 | 0 | g_string_append_c(id_hash, '#'); |
116 | 0 | for (guint i = 0; i < FU_DEVICE_EVENT_KEY_HASH_PREFIX_SIZE / 2; i++) |
117 | 0 | g_string_append_printf(id_hash, "%02x", buf[i]); |
118 | 0 | return g_string_free(g_steal_pointer(&id_hash), FALSE); |
119 | 0 | } |
120 | | |
121 | | /** |
122 | | * fu_device_event_get_id: |
123 | | * @self: a #FuDeviceEvent |
124 | | * |
125 | | * Return the truncated SHA1 of the #FuDeviceEvent key, which is normally set when creating the |
126 | | * object. |
127 | | * |
128 | | * Returns: (nullable): string |
129 | | * |
130 | | * Since: 2.0.0 |
131 | | **/ |
132 | | const gchar * |
133 | | fu_device_event_get_id(FuDeviceEvent *self) |
134 | 0 | { |
135 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
136 | 0 | return self->id; |
137 | 0 | } |
138 | | |
139 | | /** |
140 | | * fu_device_event_set_str: |
141 | | * @self: a #FuDeviceEvent |
142 | | * @key: (not nullable): a unique key, e.g. `Name` |
143 | | * @value: (nullable): a string |
144 | | * |
145 | | * Sets a string value on the event. |
146 | | * |
147 | | * Since: 2.0.0 |
148 | | **/ |
149 | | void |
150 | | fu_device_event_set_str(FuDeviceEvent *self, const gchar *key, const gchar *value) |
151 | 0 | { |
152 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
153 | 0 | g_return_if_fail(key != NULL); |
154 | 0 | g_ptr_array_add(self->values, |
155 | 0 | fu_device_event_blob_new(G_TYPE_STRING, key, g_strdup(value), g_free)); |
156 | 0 | } |
157 | | |
158 | | /** |
159 | | * fu_device_event_set_i64: |
160 | | * @self: a #FuDeviceEvent |
161 | | * @key: (not nullable): a unique key, e.g. `Name` |
162 | | * @value: a string |
163 | | * |
164 | | * Sets an integer value on the string. |
165 | | * |
166 | | * Since: 2.0.0 |
167 | | **/ |
168 | | void |
169 | | fu_device_event_set_i64(FuDeviceEvent *self, const gchar *key, gint64 value) |
170 | 0 | { |
171 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
172 | 0 | g_return_if_fail(key != NULL); |
173 | | |
174 | 0 | g_ptr_array_add( |
175 | 0 | self->values, |
176 | 0 | fu_device_event_blob_new(G_TYPE_INT, key, g_memdup2(&value, sizeof(value)), g_free)); |
177 | 0 | } |
178 | | |
179 | | /** |
180 | | * fu_device_event_set_bytes: |
181 | | * @self: a #FuDeviceEvent |
182 | | * @key: (not nullable): a unique key, e.g. `Name` |
183 | | * @value: (not nullable): a #GBytes |
184 | | * |
185 | | * Sets a blob on the event. Note: blobs are stored internally as BASE-64 strings. |
186 | | * |
187 | | * Since: 2.0.0 |
188 | | **/ |
189 | | void |
190 | | fu_device_event_set_bytes(FuDeviceEvent *self, const gchar *key, GBytes *value) |
191 | 282 | { |
192 | 282 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
193 | 282 | g_return_if_fail(key != NULL); |
194 | 282 | g_return_if_fail(value != NULL); |
195 | 282 | g_ptr_array_add(self->values, |
196 | 282 | fu_device_event_blob_new( |
197 | 282 | G_TYPE_STRING, |
198 | 282 | key, |
199 | 282 | g_base64_encode(g_bytes_get_data(value, NULL), g_bytes_get_size(value)), |
200 | 282 | g_free)); |
201 | 282 | } |
202 | | |
203 | | /** |
204 | | * fu_device_event_set_byte_array: |
205 | | * @self: a #FuDeviceEvent |
206 | | * @key: (not nullable): a unique key, e.g. `Name` |
207 | | * @value: (not nullable): a #GByteArray |
208 | | * |
209 | | * Sets a blob on the event. Note: blobs are stored internally as BASE-64 strings. |
210 | | * |
211 | | * Since: 2.1.1 |
212 | | **/ |
213 | | void |
214 | | fu_device_event_set_byte_array(FuDeviceEvent *self, const gchar *key, GByteArray *value) |
215 | 0 | { |
216 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
217 | 0 | g_return_if_fail(key != NULL); |
218 | 0 | g_return_if_fail(value != NULL); |
219 | 0 | g_ptr_array_add(self->values, |
220 | 0 | fu_device_event_blob_new(G_TYPE_STRING, |
221 | 0 | key, |
222 | 0 | g_base64_encode(value->data, value->len), |
223 | 0 | g_free)); |
224 | 0 | } |
225 | | |
226 | | /** |
227 | | * fu_device_event_set_data: |
228 | | * @self: a #FuDeviceEvent |
229 | | * @key: (not nullable): a unique key, e.g. `Name` |
230 | | * @buf: (nullable): a buffer |
231 | | * @bufsz: size of @buf |
232 | | * |
233 | | * Sets a memory buffer on the event. Note: memory buffers are stored internally as BASE-64 strings. |
234 | | * |
235 | | * Since: 2.0.0 |
236 | | **/ |
237 | | void |
238 | | fu_device_event_set_data(FuDeviceEvent *self, const gchar *key, const guint8 *buf, gsize bufsz) |
239 | 0 | { |
240 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
241 | 0 | g_return_if_fail(key != NULL); |
242 | 0 | g_ptr_array_add( |
243 | 0 | self->values, |
244 | 0 | fu_device_event_blob_new(G_TYPE_STRING, key, g_base64_encode(buf, bufsz), g_free)); |
245 | 0 | } |
246 | | |
247 | | /** |
248 | | * fu_device_event_set_error: |
249 | | * @self: a #FuDeviceEvent |
250 | | * @error: (not nullable): a #GError with domain #FwupdError |
251 | | * |
252 | | * Sets an error on the event. |
253 | | * |
254 | | * Since: 2.0.6 |
255 | | **/ |
256 | | void |
257 | | fu_device_event_set_error(FuDeviceEvent *self, const GError *error) |
258 | 0 | { |
259 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
260 | 0 | g_return_if_fail(error != NULL); |
261 | 0 | g_return_if_fail(error->domain == FWUPD_ERROR); |
262 | 0 | fu_device_event_set_i64(self, "Error", error->code); |
263 | 0 | fu_device_event_set_str(self, "ErrorMsg", error->message); |
264 | 0 | } |
265 | | |
266 | | /** |
267 | | * fu_device_event_check_error: |
268 | | * @self: a #FuDeviceEvent |
269 | | * @error: (nullable): optional return location for an error |
270 | | * |
271 | | * Sets an error from the event if possible. |
272 | | * |
273 | | * Returns: %FALSE if @error was set |
274 | | * |
275 | | * Since: 2.0.6 |
276 | | **/ |
277 | | gboolean |
278 | | fu_device_event_check_error(FuDeviceEvent *self, GError **error) |
279 | 0 | { |
280 | 0 | gint64 code; |
281 | 0 | const gchar *message; |
282 | |
|
283 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE); |
284 | | |
285 | | /* nothing to do */ |
286 | 0 | if (error == NULL) |
287 | 0 | return TRUE; |
288 | | |
289 | | /* anything set */ |
290 | 0 | code = fu_device_event_get_i64(self, "Error", NULL); |
291 | 0 | if (code == G_MAXINT64) |
292 | 0 | return TRUE; |
293 | 0 | message = fu_device_event_get_str(self, "ErrorMsg", NULL); |
294 | 0 | if (message == NULL) |
295 | 0 | message = fwupd_error_to_string(code); |
296 | | |
297 | | /* success, in a way */ |
298 | 0 | g_set_error_literal(error, FWUPD_ERROR, code, message); |
299 | 0 | return FALSE; |
300 | 0 | } |
301 | | |
302 | | static gpointer |
303 | | fu_device_event_lookup(FuDeviceEvent *self, const gchar *key, GType gtype, GError **error) |
304 | 852 | { |
305 | 852 | FuDeviceEventBlob *blob = NULL; |
306 | | |
307 | 1.42k | for (guint i = 0; i < self->values->len; i++) { |
308 | 852 | FuDeviceEventBlob *blob_tmp = g_ptr_array_index(self->values, i); |
309 | 852 | if (g_strcmp0(blob_tmp->key, key) == 0) { |
310 | 284 | blob = blob_tmp; |
311 | 284 | break; |
312 | 284 | } |
313 | 852 | } |
314 | 852 | if (blob == NULL) { |
315 | 568 | g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no event for key %s", key); |
316 | 568 | return NULL; |
317 | 568 | } |
318 | 284 | if (blob->gtype != gtype) { |
319 | 0 | g_set_error(error, |
320 | 0 | FWUPD_ERROR, |
321 | 0 | FWUPD_ERROR_INVALID_DATA, |
322 | 0 | "invalid event type for key %s", |
323 | 0 | key); |
324 | 0 | return NULL; |
325 | 0 | } |
326 | 284 | return blob->data; |
327 | 284 | } |
328 | | |
329 | | /** |
330 | | * fu_device_event_get_str: |
331 | | * @self: a #FuDeviceEvent |
332 | | * @key: (not nullable): a unique key, e.g. `Name` |
333 | | * @error: (nullable): optional return location for an error |
334 | | * |
335 | | * Gets a string value from the event. |
336 | | * |
337 | | * Returns: (nullable): string, or %NULL on error |
338 | | * |
339 | | * Since: 2.0.0 |
340 | | **/ |
341 | | const gchar * |
342 | | fu_device_event_get_str(FuDeviceEvent *self, const gchar *key, GError **error) |
343 | 0 | { |
344 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
345 | 0 | g_return_val_if_fail(key != NULL, NULL); |
346 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
347 | 0 | return (const gchar *)fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
348 | 0 | } |
349 | | |
350 | | /** |
351 | | * fu_device_event_get_i64: |
352 | | * @self: a #FuDeviceEvent |
353 | | * @key: (not nullable): a unique key, e.g. `Name` |
354 | | * @error: (nullable): optional return location for an error |
355 | | * |
356 | | * Gets an integer value from the event. |
357 | | * |
358 | | * Returns: integer, or %G_MAXINT64 on error |
359 | | * |
360 | | * Since: 2.0.0 |
361 | | **/ |
362 | | gint64 |
363 | | fu_device_event_get_i64(FuDeviceEvent *self, const gchar *key, GError **error) |
364 | 568 | { |
365 | 568 | gint64 *val; |
366 | 568 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), G_MAXINT64); |
367 | 568 | g_return_val_if_fail(key != NULL, G_MAXINT64); |
368 | 568 | g_return_val_if_fail(error == NULL || *error == NULL, G_MAXINT64); |
369 | 568 | val = fu_device_event_lookup(self, key, G_TYPE_INT, error); |
370 | 568 | if (val == NULL) |
371 | 568 | return G_MAXINT64; |
372 | 0 | return *val; |
373 | 568 | } |
374 | | |
375 | | /** |
376 | | * fu_device_event_get_bytes: |
377 | | * @self: a #FuDeviceEvent |
378 | | * @key: (not nullable): a unique key, e.g. `Name` |
379 | | * @error: (nullable): optional return location for an error |
380 | | * |
381 | | * Gets a memory blob from the event. |
382 | | * |
383 | | * Returns: (transfer full) (nullable): byes data, or %NULL on error |
384 | | * |
385 | | * Since: 2.0.0 |
386 | | **/ |
387 | | GBytes * |
388 | | fu_device_event_get_bytes(FuDeviceEvent *self, const gchar *key, GError **error) |
389 | 0 | { |
390 | 0 | const gchar *blobstr; |
391 | 0 | gsize bufsz = 0; |
392 | 0 | g_autofree guchar *buf = NULL; |
393 | |
|
394 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
395 | 0 | g_return_val_if_fail(key != NULL, NULL); |
396 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
397 | | |
398 | 0 | blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
399 | 0 | if (blobstr == NULL) |
400 | 0 | return NULL; |
401 | 0 | if (blobstr[0] == '\0') |
402 | 0 | return g_bytes_new(NULL, 0); |
403 | 0 | buf = g_base64_decode(blobstr, &bufsz); |
404 | 0 | return g_bytes_new_take(g_steal_pointer(&buf), bufsz); |
405 | 0 | } |
406 | | |
407 | | /** |
408 | | * fu_device_event_get_byte_array: |
409 | | * @self: a #FuDeviceEvent |
410 | | * @key: (not nullable): a unique key, e.g. `Name` |
411 | | * @error: (nullable): optional return location for an error |
412 | | * |
413 | | * Gets a memory blob from the event. |
414 | | * |
415 | | * Returns: (transfer full) (nullable): a #GByteArray, or %NULL on error |
416 | | * |
417 | | * Since: 2.1.1 |
418 | | **/ |
419 | | GByteArray * |
420 | | fu_device_event_get_byte_array(FuDeviceEvent *self, const gchar *key, GError **error) |
421 | 0 | { |
422 | 0 | const gchar *blobstr; |
423 | 0 | gsize bufsz = 0; |
424 | 0 | g_autofree guchar *buf = NULL; |
425 | |
|
426 | 0 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), NULL); |
427 | 0 | g_return_val_if_fail(key != NULL, NULL); |
428 | 0 | g_return_val_if_fail(error == NULL || *error == NULL, NULL); |
429 | | |
430 | 0 | blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
431 | 0 | if (blobstr == NULL) |
432 | 0 | return NULL; |
433 | 0 | if (blobstr[0] == '\0') |
434 | 0 | return g_byte_array_new(); |
435 | 0 | buf = g_base64_decode(blobstr, &bufsz); |
436 | 0 | return g_byte_array_new_take(g_steal_pointer(&buf), bufsz); |
437 | 0 | } |
438 | | |
439 | | /** |
440 | | * fu_device_event_copy_data: |
441 | | * @self: a #FuDeviceEvent |
442 | | * @key: (not nullable): a unique key, e.g. `Name` |
443 | | * @buf: (nullable): a buffer |
444 | | * @bufsz: size of @buf |
445 | | * @actual_length: (out) (optional): the actual number of bytes sent, or %NULL |
446 | | * @error: (nullable): optional return location for an error |
447 | | * |
448 | | * Copies memory from the event. |
449 | | * |
450 | | * Returns: %TRUE if the buffer was copied |
451 | | * |
452 | | * Since: 2.0.0 |
453 | | **/ |
454 | | gboolean |
455 | | fu_device_event_copy_data(FuDeviceEvent *self, |
456 | | const gchar *key, |
457 | | guint8 *buf, |
458 | | gsize bufsz, |
459 | | gsize *actual_length, |
460 | | GError **error) |
461 | 284 | { |
462 | 284 | const gchar *blobstr; |
463 | 284 | gsize bufsz_src = 0; |
464 | 284 | g_autofree guchar *buf_src = NULL; |
465 | | |
466 | 284 | g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE); |
467 | 284 | g_return_val_if_fail(key != NULL, FALSE); |
468 | 284 | g_return_val_if_fail(error == NULL || *error == NULL, FALSE); |
469 | | |
470 | 284 | blobstr = fu_device_event_lookup(self, key, G_TYPE_STRING, error); |
471 | 284 | if (blobstr == NULL) |
472 | 0 | return FALSE; |
473 | 284 | buf_src = g_base64_decode(blobstr, &bufsz_src); |
474 | 284 | if (actual_length != NULL) |
475 | 284 | *actual_length = bufsz_src; |
476 | 284 | if (buf != NULL) |
477 | 284 | return fu_memcpy_safe(buf, bufsz, 0x0, buf_src, bufsz_src, 0x0, bufsz_src, error); |
478 | 0 | return TRUE; |
479 | 284 | } |
480 | | |
481 | | static void |
482 | | fu_device_event_add_json(FwupdCodec *codec, FwupdJsonObject *json_obj, FwupdCodecFlags flags) |
483 | 0 | { |
484 | 0 | FuDeviceEvent *self = FU_DEVICE_EVENT(codec); |
485 | |
|
486 | 0 | if (self->id_uncompressed != NULL && (flags & FWUPD_CODEC_FLAG_COMPRESSED) == 0) { |
487 | 0 | fwupd_json_object_add_string(json_obj, "Id", self->id_uncompressed); |
488 | 0 | } else if (self->id != NULL) { |
489 | 0 | fwupd_json_object_add_string(json_obj, "Id", self->id); |
490 | 0 | } |
491 | |
|
492 | 0 | for (guint i = 0; i < self->values->len; i++) { |
493 | 0 | FuDeviceEventBlob *blob = g_ptr_array_index(self->values, i); |
494 | 0 | if (blob->gtype == G_TYPE_INT) { |
495 | 0 | fwupd_json_object_add_integer(json_obj, blob->key, *((gint64 *)blob->data)); |
496 | 0 | } else if (blob->gtype == G_TYPE_BYTES || blob->gtype == G_TYPE_STRING) { |
497 | 0 | fwupd_json_object_add_string(json_obj, |
498 | 0 | blob->key, |
499 | 0 | (const gchar *)blob->data); |
500 | 0 | } else { |
501 | 0 | g_warning("invalid GType %s, ignoring", g_type_name(blob->gtype)); |
502 | 0 | } |
503 | 0 | } |
504 | 0 | } |
505 | | |
506 | | static void |
507 | | fu_device_event_set_id(FuDeviceEvent *self, const gchar *id) |
508 | 0 | { |
509 | 0 | g_return_if_fail(FU_IS_DEVICE_EVENT(self)); |
510 | 0 | g_return_if_fail(id != NULL); |
511 | | |
512 | 0 | g_clear_pointer(&self->id, g_free); |
513 | 0 | g_clear_pointer(&self->id_uncompressed, g_free); |
514 | | |
515 | | /* already a truncated SHA1 hash? */ |
516 | 0 | if (g_str_has_prefix(id, "#")) { |
517 | 0 | self->id = g_strdup(id); |
518 | 0 | } else { |
519 | 0 | self->id_uncompressed = g_strdup(id); |
520 | 0 | self->id = fu_device_event_build_id(id); |
521 | 0 | } |
522 | 0 | } |
523 | | |
524 | | static void |
525 | | fu_device_event_set_id_from_json(FuDeviceEvent *self, const gchar *id) |
526 | 0 | { |
527 | 0 | g_clear_pointer(&self->id, g_free); |
528 | 0 | if (g_str_has_prefix(id, "#")) { |
529 | 0 | self->id = g_strdup(id); |
530 | 0 | } else { |
531 | 0 | self->id = fu_device_event_build_id(id); |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | static gboolean |
536 | | fu_device_event_from_json(FwupdCodec *codec, FwupdJsonObject *json_obj, GError **error) |
537 | 0 | { |
538 | 0 | FuDeviceEvent *self = FU_DEVICE_EVENT(codec); |
539 | 0 | for (guint i = 0; i < fwupd_json_object_get_size(json_obj); i++) { |
540 | 0 | GRefString *key = fwupd_json_object_get_key_for_index(json_obj, i, NULL); |
541 | 0 | g_autoptr(FwupdJsonNode) json_node = NULL; |
542 | |
|
543 | 0 | json_node = fwupd_json_object_get_node_for_index(json_obj, i, error); |
544 | 0 | if (json_node == NULL) |
545 | 0 | return FALSE; |
546 | 0 | if (fwupd_json_node_get_kind(json_node) == FWUPD_JSON_NODE_KIND_STRING) { |
547 | 0 | GRefString *str = fwupd_json_node_get_string(json_node, NULL); |
548 | 0 | if (g_strcmp0(key, "Id") == 0) { |
549 | 0 | fu_device_event_set_id_from_json(self, str); |
550 | 0 | } else if (str != NULL) { |
551 | 0 | g_ptr_array_add(self->values, |
552 | 0 | fu_device_event_blob_new_internal( |
553 | 0 | G_TYPE_STRING, |
554 | 0 | key, |
555 | 0 | g_ref_string_acquire(str), |
556 | 0 | (GDestroyNotify)g_ref_string_release)); |
557 | 0 | } else { |
558 | 0 | g_ptr_array_add(self->values, |
559 | 0 | fu_device_event_blob_new_internal(G_TYPE_STRING, |
560 | 0 | key, |
561 | 0 | NULL, |
562 | 0 | NULL)); |
563 | 0 | } |
564 | 0 | } else if (fwupd_json_node_get_kind(json_node) == FWUPD_JSON_NODE_KIND_RAW) { |
565 | 0 | GRefString *str; |
566 | 0 | gint64 value = 0; |
567 | |
|
568 | 0 | str = fwupd_json_node_get_raw(json_node, error); |
569 | 0 | if (str == NULL) |
570 | 0 | return FALSE; |
571 | 0 | if (!fu_strtoll(str, |
572 | 0 | &value, |
573 | 0 | G_MININT64, |
574 | 0 | G_MAXINT64, |
575 | 0 | FU_INTEGER_BASE_AUTO, |
576 | 0 | error)) |
577 | 0 | return FALSE; |
578 | 0 | g_ptr_array_add( |
579 | 0 | self->values, |
580 | 0 | fu_device_event_blob_new_internal(G_TYPE_INT, |
581 | 0 | key, |
582 | 0 | g_memdup2(&value, sizeof(value)), |
583 | 0 | g_free)); |
584 | 0 | } |
585 | 0 | } |
586 | | |
587 | | /* we do not need this again, so avoid keeping all the tree data in memory */ |
588 | 0 | fwupd_json_object_clear(json_obj); |
589 | | |
590 | | /* success */ |
591 | 0 | return TRUE; |
592 | 0 | } |
593 | | |
594 | | static void |
595 | | fu_device_event_init(FuDeviceEvent *self) |
596 | 282 | { |
597 | 282 | self->values = g_ptr_array_new_with_free_func((GDestroyNotify)fu_device_event_blob_free); |
598 | 282 | } |
599 | | |
600 | | static void |
601 | | fu_device_event_finalize(GObject *object) |
602 | 282 | { |
603 | 282 | FuDeviceEvent *self = FU_DEVICE_EVENT(object); |
604 | 282 | g_free(self->id); |
605 | 282 | g_free(self->id_uncompressed); |
606 | 282 | g_ptr_array_unref(self->values); |
607 | 282 | G_OBJECT_CLASS(fu_device_event_parent_class)->finalize(object); |
608 | 282 | } |
609 | | |
610 | | static void |
611 | | fu_device_event_class_init(FuDeviceEventClass *klass) |
612 | 1 | { |
613 | 1 | GObjectClass *object_class = G_OBJECT_CLASS(klass); |
614 | 1 | object_class->finalize = fu_device_event_finalize; |
615 | 1 | } |
616 | | |
617 | | static void |
618 | | fu_device_event_codec_iface_init(FwupdCodecInterface *iface) |
619 | 1 | { |
620 | 1 | iface->add_json = fu_device_event_add_json; |
621 | 1 | iface->from_json = fu_device_event_from_json; |
622 | 1 | } |
623 | | |
624 | | /** |
625 | | * fu_device_event_new: |
626 | | * @id: a cache key, which is converted to a truncated SHA1 hash if required |
627 | | * |
628 | | * Return value: (transfer full): a new #FuDeviceEvent object. |
629 | | * |
630 | | * Since: 2.0.0 |
631 | | **/ |
632 | | FuDeviceEvent * |
633 | | fu_device_event_new(const gchar *id) |
634 | 282 | { |
635 | 282 | FuDeviceEvent *self = g_object_new(FU_TYPE_DEVICE_EVENT, NULL); |
636 | 282 | if (id != NULL) |
637 | 0 | fu_device_event_set_id(self, id); |
638 | 282 | return FU_DEVICE_EVENT(self); |
639 | 282 | } |