1*592efe25SPierre Pronchery /*
2*592efe25SPierre Pronchery * SPDX-License-Identifier: BSD-2-Clause
3*592efe25SPierre Pronchery *
4*592efe25SPierre Pronchery * Copyright (c) 2025 The FreeBSD Foundation
5*592efe25SPierre Pronchery *
6*592efe25SPierre Pronchery * Portions of this software were developed by
7*592efe25SPierre Pronchery * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8*592efe25SPierre Pronchery * the FreeBSD Foundation
9*592efe25SPierre Pronchery *
10*592efe25SPierre Pronchery * Copyright (C) 2026 Elizabeth Ashford.
11*592efe25SPierre Pronchery */
12*592efe25SPierre Pronchery
13*592efe25SPierre Pronchery #include <stdlib.h>
14*592efe25SPierre Pronchery #include <string.h>
15*592efe25SPierre Pronchery #include "util.h"
16*592efe25SPierre Pronchery #include "core.h"
17*592efe25SPierre Pronchery #include "software.h"
18*592efe25SPierre Pronchery #include "simplelicensing.h"
19*592efe25SPierre Pronchery #include "serialize.h"
20*592efe25SPierre Pronchery
21*592efe25SPierre Pronchery static void
serialize_escape_string(pkgconf_buffer_t * buffer,const char * s)22*592efe25SPierre Pronchery serialize_escape_string(pkgconf_buffer_t *buffer, const char *s)
23*592efe25SPierre Pronchery {
24*592efe25SPierre Pronchery for (const char *p = s; *p; p++)
25*592efe25SPierre Pronchery {
26*592efe25SPierre Pronchery switch (*p)
27*592efe25SPierre Pronchery {
28*592efe25SPierre Pronchery case '\"':
29*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\\"");
30*592efe25SPierre Pronchery break;
31*592efe25SPierre Pronchery case '\\':
32*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\\\");
33*592efe25SPierre Pronchery break;
34*592efe25SPierre Pronchery case '\b':
35*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\b");
36*592efe25SPierre Pronchery break;
37*592efe25SPierre Pronchery case '\f':
38*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\f");
39*592efe25SPierre Pronchery break;
40*592efe25SPierre Pronchery case '\n':
41*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\n");
42*592efe25SPierre Pronchery break;
43*592efe25SPierre Pronchery case '\r':
44*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\r");
45*592efe25SPierre Pronchery break;
46*592efe25SPierre Pronchery case '\t':
47*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "\\t");
48*592efe25SPierre Pronchery break;
49*592efe25SPierre Pronchery default:
50*592efe25SPierre Pronchery if ((unsigned char) *p < 0x20)
51*592efe25SPierre Pronchery pkgconf_buffer_append_fmt(buffer, "\\u%04x", (unsigned int)(unsigned char) *p);
52*592efe25SPierre Pronchery else
53*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, *p);
54*592efe25SPierre Pronchery }
55*592efe25SPierre Pronchery }
56*592efe25SPierre Pronchery }
57*592efe25SPierre Pronchery
58*592efe25SPierre Pronchery static inline void
serialize_add_indent(pkgconf_buffer_t * buffer,unsigned int level)59*592efe25SPierre Pronchery serialize_add_indent(pkgconf_buffer_t *buffer, unsigned int level)
60*592efe25SPierre Pronchery {
61*592efe25SPierre Pronchery for (; level; level--)
62*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, " ");
63*592efe25SPierre Pronchery }
64*592efe25SPierre Pronchery
65*592efe25SPierre Pronchery /*
66*592efe25SPierre Pronchery * !doc
67*592efe25SPierre Pronchery *
68*592efe25SPierre Pronchery * .. c:function:: void spdxtool_serialize_value_to_buf(pkgconf_buffer_t *buffer, spdxtool_serialize_value_t *value, unsigned int indent)
69*592efe25SPierre Pronchery *
70*592efe25SPierre Pronchery * Serialize the given JSON to the buffer
71*592efe25SPierre Pronchery *
72*592efe25SPierre Pronchery * :param pkgconf_buffer_t *buffer: Buffer to add to.
73*592efe25SPierre Pronchery * :param spdxtool_serialize_value *value: Value to serialize.
74*592efe25SPierre Pronchery * :param unsigned int indent: Indent level
75*592efe25SPierre Pronchery * :return: true on success, false on failure
76*592efe25SPierre Pronchery */
77*592efe25SPierre Pronchery bool
spdxtool_serialize_value_to_buf(pkgconf_buffer_t * buffer,spdxtool_serialize_value_t * value,unsigned int indent)78*592efe25SPierre Pronchery spdxtool_serialize_value_to_buf(pkgconf_buffer_t *buffer, spdxtool_serialize_value_t *value, unsigned int indent)
79*592efe25SPierre Pronchery {
80*592efe25SPierre Pronchery if (!buffer || !value)
81*592efe25SPierre Pronchery return false;
82*592efe25SPierre Pronchery
83*592efe25SPierre Pronchery switch(value->type) {
84*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_STRING:
85*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '"');
86*592efe25SPierre Pronchery serialize_escape_string(buffer, value->value.s ? value->value.s : "");
87*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '"');
88*592efe25SPierre Pronchery break;
89*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_INT:
90*592efe25SPierre Pronchery pkgconf_buffer_append_fmt(buffer, "%d", value->value.i);
91*592efe25SPierre Pronchery break;
92*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_BOOL:
93*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, value->value.b ? "true" : "false");
94*592efe25SPierre Pronchery break;
95*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_NULL:
96*592efe25SPierre Pronchery pkgconf_buffer_append(buffer, "null");
97*592efe25SPierre Pronchery break;
98*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_OBJECT:
99*592efe25SPierre Pronchery {
100*592efe25SPierre Pronchery pkgconf_node_t *iter;
101*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '{');
102*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '\n');
103*592efe25SPierre Pronchery
104*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(value->value.o->entries.head, iter)
105*592efe25SPierre Pronchery {
106*592efe25SPierre Pronchery spdxtool_serialize_object_t *entry = iter->data;
107*592efe25SPierre Pronchery serialize_add_indent(buffer, indent + 1);
108*592efe25SPierre Pronchery pkgconf_buffer_append_fmt(buffer, "\"%s\": ", entry->key);
109*592efe25SPierre Pronchery spdxtool_serialize_value_to_buf(buffer, entry->value, indent + 1);
110*592efe25SPierre Pronchery if (iter->next)
111*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, ',');
112*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '\n');
113*592efe25SPierre Pronchery }
114*592efe25SPierre Pronchery
115*592efe25SPierre Pronchery serialize_add_indent(buffer, indent);
116*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '}');
117*592efe25SPierre Pronchery break;
118*592efe25SPierre Pronchery }
119*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_ARRAY:
120*592efe25SPierre Pronchery {
121*592efe25SPierre Pronchery pkgconf_node_t *iter;
122*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '[');
123*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '\n');
124*592efe25SPierre Pronchery
125*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(value->value.a->items.head, iter)
126*592efe25SPierre Pronchery {
127*592efe25SPierre Pronchery spdxtool_serialize_value_t *entry = iter->data;
128*592efe25SPierre Pronchery serialize_add_indent(buffer, indent + 1);
129*592efe25SPierre Pronchery spdxtool_serialize_value_to_buf(buffer, entry, indent + 1);
130*592efe25SPierre Pronchery if (iter->next)
131*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, ',');
132*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, '\n');
133*592efe25SPierre Pronchery }
134*592efe25SPierre Pronchery serialize_add_indent(buffer, indent);
135*592efe25SPierre Pronchery pkgconf_buffer_push_byte(buffer, ']');
136*592efe25SPierre Pronchery break;
137*592efe25SPierre Pronchery }
138*592efe25SPierre Pronchery }
139*592efe25SPierre Pronchery
140*592efe25SPierre Pronchery return true;
141*592efe25SPierre Pronchery }
142*592efe25SPierre Pronchery
143*592efe25SPierre Pronchery /*
144*592efe25SPierre Pronchery * !doc
145*592efe25SPierre Pronchery *
146*592efe25SPierre Pronchery * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_value_t *value)
147*592efe25SPierre Pronchery *
148*592efe25SPierre Pronchery * Add a key-value pair to a JSON object list. The key is copied internally.
149*592efe25SPierre Pronchery * The object list takes ownership of the value.
150*592efe25SPierre Pronchery *
151*592efe25SPierre Pronchery * :param spdxtool_serialize_object_list_t *object_list: Object list to add to.
152*592efe25SPierre Pronchery * :param const char *key: Key string, copied internally.
153*592efe25SPierre Pronchery * :param spdxtool_serialize_value_t *value: Value to associate with the key. Ownership transfers to the object list.
154*592efe25SPierre Pronchery * :return: The value added, not owned by the caller.
155*592efe25SPierre Pronchery */
156*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t * object_list,const char * key,spdxtool_serialize_value_t * value)157*592efe25SPierre Pronchery spdxtool_serialize_object_add_take(spdxtool_serialize_object_list_t *object_list, const char *key, spdxtool_serialize_value_t *value)
158*592efe25SPierre Pronchery {
159*592efe25SPierre Pronchery if (!object_list || !value)
160*592efe25SPierre Pronchery {
161*592efe25SPierre Pronchery spdxtool_serialize_value_free(value);
162*592efe25SPierre Pronchery return NULL;
163*592efe25SPierre Pronchery }
164*592efe25SPierre Pronchery
165*592efe25SPierre Pronchery pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
166*592efe25SPierre Pronchery spdxtool_serialize_object_t *object = calloc(1, sizeof(spdxtool_serialize_object_t));
167*592efe25SPierre Pronchery char *keycopy = key ? strdup(key) : strdup("");
168*592efe25SPierre Pronchery if (!node || !object || !keycopy)
169*592efe25SPierre Pronchery {
170*592efe25SPierre Pronchery free(node);
171*592efe25SPierre Pronchery free(keycopy);
172*592efe25SPierre Pronchery /* object->key/value are not assigned yet; free the struct itself */
173*592efe25SPierre Pronchery free(object);
174*592efe25SPierre Pronchery spdxtool_serialize_value_free(value);
175*592efe25SPierre Pronchery return NULL;
176*592efe25SPierre Pronchery }
177*592efe25SPierre Pronchery
178*592efe25SPierre Pronchery object->key = keycopy;
179*592efe25SPierre Pronchery object->value = value;
180*592efe25SPierre Pronchery pkgconf_node_insert_tail(node, object, &object_list->entries);
181*592efe25SPierre Pronchery return value;
182*592efe25SPierre Pronchery }
183*592efe25SPierre Pronchery
184*592efe25SPierre Pronchery /*
185*592efe25SPierre Pronchery * !doc
186*592efe25SPierre Pronchery *
187*592efe25SPierre Pronchery * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_array_add_take(spdxtool_serialize_array_t *array, spdxtool_serialize_value_t value)
188*592efe25SPierre Pronchery *
189*592efe25SPierre Pronchery * Add a value to a JSON array. The array takes ownership of the value.
190*592efe25SPierre Pronchery *
191*592efe25SPierre Pronchery * :param spdxtool_serialize_array_t *array: Array to add to.
192*592efe25SPierre Pronchery * :param spdxtool_serialize_value_t value: Value to append. Ownership transfers to the array.
193*592efe25SPierre Pronchery * :return: The value added, not owned by the caller.
194*592efe25SPierre Pronchery */
195*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_array_add_take(spdxtool_serialize_array_t * array,spdxtool_serialize_value_t * value)196*592efe25SPierre Pronchery spdxtool_serialize_array_add_take(spdxtool_serialize_array_t *array, spdxtool_serialize_value_t *value)
197*592efe25SPierre Pronchery {
198*592efe25SPierre Pronchery if (!array)
199*592efe25SPierre Pronchery {
200*592efe25SPierre Pronchery // Taking value, so free
201*592efe25SPierre Pronchery spdxtool_serialize_value_free(value);
202*592efe25SPierre Pronchery return NULL;
203*592efe25SPierre Pronchery }
204*592efe25SPierre Pronchery
205*592efe25SPierre Pronchery pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
206*592efe25SPierre Pronchery if (!node)
207*592efe25SPierre Pronchery {
208*592efe25SPierre Pronchery spdxtool_serialize_value_free(value);
209*592efe25SPierre Pronchery return NULL;
210*592efe25SPierre Pronchery }
211*592efe25SPierre Pronchery
212*592efe25SPierre Pronchery pkgconf_node_insert_tail(node, value, &array->items);
213*592efe25SPierre Pronchery return value;
214*592efe25SPierre Pronchery }
215*592efe25SPierre Pronchery
216*592efe25SPierre Pronchery /*
217*592efe25SPierre Pronchery * !doc
218*592efe25SPierre Pronchery *
219*592efe25SPierre Pronchery * .. c:function:: spdxtool_serialize_object_list_t *spdxtool_serialize_object_list_new(void)
220*592efe25SPierre Pronchery *
221*592efe25SPierre Pronchery * Allocate and initialize a new empty JSON object list.
222*592efe25SPierre Pronchery *
223*592efe25SPierre Pronchery * :return: Pointer to a new spdxtool_serialize_object_list_t, or NULL on allocation failure.
224*592efe25SPierre Pronchery */
225*592efe25SPierre Pronchery spdxtool_serialize_object_list_t *
spdxtool_serialize_object_list_new(void)226*592efe25SPierre Pronchery spdxtool_serialize_object_list_new(void)
227*592efe25SPierre Pronchery {
228*592efe25SPierre Pronchery return calloc(1, sizeof(spdxtool_serialize_object_list_t));
229*592efe25SPierre Pronchery }
230*592efe25SPierre Pronchery
231*592efe25SPierre Pronchery /*
232*592efe25SPierre Pronchery * !doc
233*592efe25SPierre Pronchery *
234*592efe25SPierre Pronchery * .. c:function:: spdxtool_serialize_array_t *spdxtool_serialize_array_new(void)
235*592efe25SPierre Pronchery *
236*592efe25SPierre Pronchery * Allocate and initialize a new empty JSON array.
237*592efe25SPierre Pronchery *
238*592efe25SPierre Pronchery * :return: Pointer to a new spdxtool_serialize_array_t, or NULL on allocation failure.
239*592efe25SPierre Pronchery */
240*592efe25SPierre Pronchery spdxtool_serialize_array_t *
spdxtool_serialize_array_new(void)241*592efe25SPierre Pronchery spdxtool_serialize_array_new(void)
242*592efe25SPierre Pronchery {
243*592efe25SPierre Pronchery return calloc(1, sizeof(spdxtool_serialize_array_t));
244*592efe25SPierre Pronchery }
245*592efe25SPierre Pronchery
246*592efe25SPierre Pronchery /*
247*592efe25SPierre Pronchery * !doc
248*592efe25SPierre Pronchery *
249*592efe25SPierre Pronchery * .. c:function:: void spdxtool_serialize_value_free(spdxtool_serialize_value_t *value)
250*592efe25SPierre Pronchery *
251*592efe25SPierre Pronchery * Free all resources owned by a JSON value. For strings, frees the string.
252*592efe25SPierre Pronchery * For objects and arrays, recursively frees all children. The value pointer
253*592efe25SPierre Pronchery * itself is not freed as it is assumed to be stack-allocated.
254*592efe25SPierre Pronchery *
255*592efe25SPierre Pronchery * :param spdxtool_serialize_value_t *value: Value to free. May be NULL.
256*592efe25SPierre Pronchery * :return: nothing
257*592efe25SPierre Pronchery */
258*592efe25SPierre Pronchery void
spdxtool_serialize_value_free(spdxtool_serialize_value_t * value)259*592efe25SPierre Pronchery spdxtool_serialize_value_free(spdxtool_serialize_value_t *value)
260*592efe25SPierre Pronchery {
261*592efe25SPierre Pronchery if (!value)
262*592efe25SPierre Pronchery return;
263*592efe25SPierre Pronchery
264*592efe25SPierre Pronchery switch (value->type)
265*592efe25SPierre Pronchery {
266*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_STRING:
267*592efe25SPierre Pronchery free(value->value.s);
268*592efe25SPierre Pronchery break;
269*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_ARRAY:
270*592efe25SPierre Pronchery spdxtool_serialize_array_free(value->value.a);
271*592efe25SPierre Pronchery break;
272*592efe25SPierre Pronchery case SPDXTOOL_SERIALIZE_TYPE_OBJECT:
273*592efe25SPierre Pronchery spdxtool_serialize_object_list_free(value->value.o);
274*592efe25SPierre Pronchery break;
275*592efe25SPierre Pronchery default:
276*592efe25SPierre Pronchery // Nothing to do
277*592efe25SPierre Pronchery break;
278*592efe25SPierre Pronchery }
279*592efe25SPierre Pronchery
280*592efe25SPierre Pronchery free(value);
281*592efe25SPierre Pronchery }
282*592efe25SPierre Pronchery
283*592efe25SPierre Pronchery /*
284*592efe25SPierre Pronchery * !doc
285*592efe25SPierre Pronchery *
286*592efe25SPierre Pronchery * .. c:function:: void spdxtool_serialize_object_free(spdxtool_serialize_object_t *object)
287*592efe25SPierre Pronchery *
288*592efe25SPierre Pronchery * Free a JSON object entry, including its key string and owned value.
289*592efe25SPierre Pronchery * The object pointer itself is not freed by this function.
290*592efe25SPierre Pronchery *
291*592efe25SPierre Pronchery * :param spdxtool_serialize_object_t *object: Object entry to free. May be NULL.
292*592efe25SPierre Pronchery * :return: nothing
293*592efe25SPierre Pronchery */
294*592efe25SPierre Pronchery void
spdxtool_serialize_object_free(spdxtool_serialize_object_t * object)295*592efe25SPierre Pronchery spdxtool_serialize_object_free(spdxtool_serialize_object_t *object)
296*592efe25SPierre Pronchery {
297*592efe25SPierre Pronchery if (!object)
298*592efe25SPierre Pronchery return;
299*592efe25SPierre Pronchery
300*592efe25SPierre Pronchery free(object->key);
301*592efe25SPierre Pronchery spdxtool_serialize_value_free(object->value);
302*592efe25SPierre Pronchery }
303*592efe25SPierre Pronchery
304*592efe25SPierre Pronchery /*
305*592efe25SPierre Pronchery * !doc
306*592efe25SPierre Pronchery *
307*592efe25SPierre Pronchery * .. c:function:: void spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t *object_list)
308*592efe25SPierre Pronchery *
309*592efe25SPierre Pronchery * Free a JSON object list and all of its entries, including their keys and values.
310*592efe25SPierre Pronchery *
311*592efe25SPierre Pronchery * :param spdxtool_serialize_object_list_t *object_list: Object list to free. May be NULL.
312*592efe25SPierre Pronchery * :return: nothing
313*592efe25SPierre Pronchery */
314*592efe25SPierre Pronchery void
spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t * object_list)315*592efe25SPierre Pronchery spdxtool_serialize_object_list_free(spdxtool_serialize_object_list_t *object_list)
316*592efe25SPierre Pronchery {
317*592efe25SPierre Pronchery if (!object_list)
318*592efe25SPierre Pronchery return;
319*592efe25SPierre Pronchery
320*592efe25SPierre Pronchery pkgconf_node_t *iter_next = NULL, *iter = NULL;
321*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY_SAFE(object_list->entries.head, iter_next, iter)
322*592efe25SPierre Pronchery {
323*592efe25SPierre Pronchery spdxtool_serialize_object_t *object = iter->data;
324*592efe25SPierre Pronchery spdxtool_serialize_object_free(object);
325*592efe25SPierre Pronchery free(object);
326*592efe25SPierre Pronchery free(iter);
327*592efe25SPierre Pronchery }
328*592efe25SPierre Pronchery
329*592efe25SPierre Pronchery free(object_list);
330*592efe25SPierre Pronchery }
331*592efe25SPierre Pronchery
332*592efe25SPierre Pronchery /*
333*592efe25SPierre Pronchery * !doc
334*592efe25SPierre Pronchery *
335*592efe25SPierre Pronchery * .. c:function:: void spdxtool_serialize_array_free(spdxtool_serialize_array_t *array)
336*592efe25SPierre Pronchery *
337*592efe25SPierre Pronchery * Free a JSON array and all of its elements.
338*592efe25SPierre Pronchery *
339*592efe25SPierre Pronchery * :param spdxtool_serialize_array_t *array: Array to free. May be NULL.
340*592efe25SPierre Pronchery * :return: nothing
341*592efe25SPierre Pronchery */
342*592efe25SPierre Pronchery void
spdxtool_serialize_array_free(spdxtool_serialize_array_t * array)343*592efe25SPierre Pronchery spdxtool_serialize_array_free(spdxtool_serialize_array_t *array)
344*592efe25SPierre Pronchery {
345*592efe25SPierre Pronchery if (!array)
346*592efe25SPierre Pronchery return;
347*592efe25SPierre Pronchery
348*592efe25SPierre Pronchery pkgconf_node_t *iter_next = NULL, *iter = NULL;
349*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY_SAFE(array->items.head, iter_next, iter)
350*592efe25SPierre Pronchery {
351*592efe25SPierre Pronchery spdxtool_serialize_value_t *value = iter->data;
352*592efe25SPierre Pronchery spdxtool_serialize_value_free(value);
353*592efe25SPierre Pronchery free(iter);
354*592efe25SPierre Pronchery }
355*592efe25SPierre Pronchery
356*592efe25SPierre Pronchery free(array);
357*592efe25SPierre Pronchery }
358*592efe25SPierre Pronchery
359*592efe25SPierre Pronchery /*
360*592efe25SPierre Pronchery * !doc
361*592efe25SPierre Pronchery *
362*592efe25SPierre Pronchery * .. c:function:: spdxtool_serialize_value_t *spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent, spdxtool_core_creation_info_t *creation, spdxtool_core_spdx_document_t *spdx)
363*592efe25SPierre Pronchery *
364*592efe25SPierre Pronchery * Serialize a complete SPDX SBOM document to a JSON-LD value tree. Iterates
365*592efe25SPierre Pronchery * all SBOMs, packages, relationships, and license expressions registered on
366*592efe25SPierre Pronchery * the document. The SpdxDocument object is emitted last to ensure all element
367*592efe25SPierre Pronchery * IDs have been populated by prior iteration. This function must be called
368*592efe25SPierre Pronchery * after pkgconf_pkg_traverse has completed so that all packages and their
369*592efe25SPierre Pronchery * dependencies are registered on spdx.
370*592efe25SPierre Pronchery *
371*592efe25SPierre Pronchery * :param pkgconf_client_t *client: The pkgconf client being accessed.
372*592efe25SPierre Pronchery * :param spdxtool_core_agent_t *agent: Agent struct to include in the document.
373*592efe25SPierre Pronchery * :param spdxtool_core_creation_info_t *creation: CreationInfo struct to include in the document.
374*592efe25SPierre Pronchery * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct containing all registered SBOMs, packages, relationships, and licenses.
375*592efe25SPierre Pronchery * :return: spdxtool_serialize_value_t * representing the complete JSON-LD document, or a null string value on allocation failure.
376*592efe25SPierre Pronchery */
377*592efe25SPierre Pronchery spdxtool_serialize_value_t *
spdxtool_serialize_sbom(pkgconf_client_t * client,spdxtool_core_agent_t * agent,spdxtool_core_creation_info_t * creation,spdxtool_core_spdx_document_t * spdx)378*592efe25SPierre Pronchery spdxtool_serialize_sbom(pkgconf_client_t *client, spdxtool_core_agent_t *agent, spdxtool_core_creation_info_t *creation, spdxtool_core_spdx_document_t *spdx)
379*592efe25SPierre Pronchery {
380*592efe25SPierre Pronchery const char *errstr = "out of memory";
381*592efe25SPierre Pronchery spdxtool_serialize_value_t *ret = NULL;
382*592efe25SPierre Pronchery spdxtool_serialize_array_t *graph = NULL;
383*592efe25SPierre Pronchery spdxtool_serialize_object_list_t *root = spdxtool_serialize_object_list_new();
384*592efe25SPierre Pronchery if (!root)
385*592efe25SPierre Pronchery goto err;
386*592efe25SPierre Pronchery
387*592efe25SPierre Pronchery if (!spdxtool_serialize_object_add_string(root, "@context", "https://spdx.org/rdf/3.0.1/spdx-context.jsonld"))
388*592efe25SPierre Pronchery goto err;
389*592efe25SPierre Pronchery
390*592efe25SPierre Pronchery graph = spdxtool_serialize_array_new();
391*592efe25SPierre Pronchery if (!graph)
392*592efe25SPierre Pronchery goto err;
393*592efe25SPierre Pronchery
394*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_agent_to_object(client, agent)))
395*592efe25SPierre Pronchery goto err;
396*592efe25SPierre Pronchery
397*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_creation_info_to_object(client, creation)))
398*592efe25SPierre Pronchery goto err;
399*592efe25SPierre Pronchery
400*592efe25SPierre Pronchery pkgconf_node_t *iter = NULL;
401*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(spdx->maintainers.head, iter)
402*592efe25SPierre Pronchery {
403*592efe25SPierre Pronchery spdxtool_core_agent_t *maintainer = iter->data;
404*592efe25SPierre Pronchery if (!maintainer)
405*592efe25SPierre Pronchery {
406*592efe25SPierre Pronchery errstr = "maintainers list corrupted";
407*592efe25SPierre Pronchery goto err;
408*592efe25SPierre Pronchery }
409*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_agent_to_object(client, maintainer)))
410*592efe25SPierre Pronchery goto err;
411*592efe25SPierre Pronchery }
412*592efe25SPierre Pronchery
413*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(spdx->licenses.head, iter)
414*592efe25SPierre Pronchery {
415*592efe25SPierre Pronchery spdxtool_simplelicensing_license_expression_t *expression = iter->data;
416*592efe25SPierre Pronchery if (!expression)
417*592efe25SPierre Pronchery {
418*592efe25SPierre Pronchery errstr = "licenses list corrupted";
419*592efe25SPierre Pronchery goto err;
420*592efe25SPierre Pronchery }
421*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_simplelicensing_licenseExpression_to_object(client, spdx->creation_info, expression)))
422*592efe25SPierre Pronchery goto err;
423*592efe25SPierre Pronchery }
424*592efe25SPierre Pronchery
425*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(spdx->rootElement.head, iter)
426*592efe25SPierre Pronchery {
427*592efe25SPierre Pronchery spdxtool_software_sbom_t *current_sbom = iter->data;
428*592efe25SPierre Pronchery if (!current_sbom)
429*592efe25SPierre Pronchery {
430*592efe25SPierre Pronchery errstr = "sbom list corrupted";
431*592efe25SPierre Pronchery goto err;
432*592efe25SPierre Pronchery }
433*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_software_sbom_to_object(client, current_sbom)))
434*592efe25SPierre Pronchery goto err;
435*592efe25SPierre Pronchery }
436*592efe25SPierre Pronchery
437*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(spdx->packages.head, iter)
438*592efe25SPierre Pronchery {
439*592efe25SPierre Pronchery pkgconf_pkg_t *pkg = iter->data;
440*592efe25SPierre Pronchery if (!pkg)
441*592efe25SPierre Pronchery {
442*592efe25SPierre Pronchery errstr = "pkg list corrupted";
443*592efe25SPierre Pronchery goto err;
444*592efe25SPierre Pronchery }
445*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_software_package_to_object(client, pkg, spdx)))
446*592efe25SPierre Pronchery goto err;
447*592efe25SPierre Pronchery }
448*592efe25SPierre Pronchery
449*592efe25SPierre Pronchery PKGCONF_FOREACH_LIST_ENTRY(spdx->relationships.head, iter)
450*592efe25SPierre Pronchery {
451*592efe25SPierre Pronchery spdxtool_core_relationship_t *relationship = iter->data;
452*592efe25SPierre Pronchery if (!relationship)
453*592efe25SPierre Pronchery {
454*592efe25SPierre Pronchery errstr = "relationship list corrupted";
455*592efe25SPierre Pronchery goto err;
456*592efe25SPierre Pronchery }
457*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_relationship_to_object(client, relationship)))
458*592efe25SPierre Pronchery goto err;
459*592efe25SPierre Pronchery }
460*592efe25SPierre Pronchery
461*592efe25SPierre Pronchery // SpdxDocument last — spdx->element must be fully populated first
462*592efe25SPierre Pronchery if (!spdxtool_serialize_array_add_take(graph, spdxtool_core_spdx_document_to_object(client, spdx)))
463*592efe25SPierre Pronchery goto err;
464*592efe25SPierre Pronchery
465*592efe25SPierre Pronchery bool ok = spdxtool_serialize_object_add_array(root, "@graph", graph);
466*592efe25SPierre Pronchery graph = NULL;
467*592efe25SPierre Pronchery if (!ok)
468*592efe25SPierre Pronchery goto err;
469*592efe25SPierre Pronchery
470*592efe25SPierre Pronchery ret = spdxtool_serialize_value_object(root);
471*592efe25SPierre Pronchery root = NULL;
472*592efe25SPierre Pronchery
473*592efe25SPierre Pronchery err:
474*592efe25SPierre Pronchery if (!ret)
475*592efe25SPierre Pronchery pkgconf_error(client, "spdxtool_serialize_sbom: %s", errstr);
476*592efe25SPierre Pronchery
477*592efe25SPierre Pronchery spdxtool_serialize_object_list_free(root);
478*592efe25SPierre Pronchery spdxtool_serialize_array_free(graph);
479*592efe25SPierre Pronchery return ret;
480*592efe25SPierre Pronchery }
481