1 /*
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2025 The FreeBSD Foundation
5 *
6 * Portions of this software were developed by
7 * Tuukka Pasanen <tuukka.pasanen@ilmi.fi> under sponsorship from
8 * the FreeBSD Foundation
9 */
10
11 #include <stdlib.h>
12 #include <string.h>
13 #include "util.h"
14 #include "serialize.h"
15 #include "core.h"
16 #include "software.h"
17 #include "simplelicensing.h"
18
19 /*
20 * !doc
21 *
22 * .. c:function:: spdxtool_core_agent_t *spdxtool_core_agent_new(pkgconf_client_t *client, const char *creation_info_id, const char *name)
23 *
24 * Create new /Core/Agent struct
25 *
26 * :param pkgconf_client_t *client: The pkgconf client being accessed.
27 * :param char *creation_info_id: CreationInfo spdxId
28 * :param char *name: Name of agent
29 * :return: NULL if some problem occurs and Agent struct if not
30 */
31 spdxtool_core_agent_t *
spdxtool_core_agent_new(pkgconf_client_t * client,const char * creation_info_id,const char * name)32 spdxtool_core_agent_new(pkgconf_client_t *client, const char *creation_info_id, const char *name)
33 {
34 if (!client || !creation_info_id || !name)
35 return NULL;
36
37 spdxtool_core_agent_t *agent = calloc(1, sizeof(spdxtool_core_agent_t));
38 if (!agent)
39 goto err;
40
41 agent->type = "Agent";
42
43 char *spdx_id_name = strdup(name);
44 if (!spdx_id_name)
45 goto err;
46
47 spdxtool_util_string_correction(spdx_id_name);
48
49 agent->spdx_id = spdxtool_util_get_spdx_id_string(client, agent->type, spdx_id_name);
50 free(spdx_id_name);
51
52 agent->creation_info = strdup(creation_info_id);
53 agent->name = strdup(name);
54
55 if (!agent->spdx_id || !agent->creation_info || !agent->name)
56 goto err;
57
58 return agent;
59
60 err:
61 pkgconf_error(client, "spdxtool_core_agent_new: out of memory");
62 spdxtool_core_agent_free(agent);
63 return NULL;
64 }
65
66 /*
67 * !doc
68 *
69 * .. c:function:: void spdxtool_core_agent_free(spdxtool_core_agent_t *agent)
70 *
71 * Free /Core/Agent struct
72 *
73 * :param spdxtool_core_agent_t *agent: Agent struct to be freed.
74 * :return: nothing
75 */
76 void
spdxtool_core_agent_free(spdxtool_core_agent_t * agent)77 spdxtool_core_agent_free(spdxtool_core_agent_t *agent)
78 {
79 if (!agent)
80 return;
81
82 free(agent->creation_info);
83 free(agent->spdx_id);
84 free(agent->name);
85
86 free(agent);
87 }
88
89 /*
90 * !doc
91 *
92 * .. c:function:: spdxtool_serialize_value_t *spdxtool_core_agent_to_object(pkgconf_client_t *client, const spdxtool_core_agent_t *agent)
93 *
94 * Serialize /Core/Agent struct to a JSON value tree.
95 *
96 * :param pkgconf_client_t *client: The pkgconf client being accessed.
97 * :param const spdxtool_core_agent_t *agent: Agent struct to be serialized.
98 * :return: spdxtool_serialize_value_t * representing the Agent object.
99 */
100 spdxtool_serialize_value_t *
spdxtool_core_agent_to_object(pkgconf_client_t * client,const spdxtool_core_agent_t * agent)101 spdxtool_core_agent_to_object(pkgconf_client_t *client, const spdxtool_core_agent_t *agent)
102 {
103 spdxtool_serialize_value_t *ret = NULL;
104 spdxtool_serialize_object_list_t *object_list = spdxtool_serialize_object_list_new();
105 if (!object_list)
106 goto err;
107
108 if (!(spdxtool_serialize_object_add_string(object_list, "type", agent->type) &&
109 spdxtool_serialize_object_add_string(object_list, "creationInfo", agent->creation_info) &&
110 spdxtool_serialize_object_add_string(object_list, "spdxId", agent->spdx_id) &&
111 spdxtool_serialize_object_add_string(object_list, "name", agent->name)))
112 {
113 goto err;
114 }
115
116 ret = spdxtool_serialize_value_object(object_list);
117 object_list = NULL;
118
119 err:
120 if (!ret)
121 pkgconf_error(client, "spdxtool_core_agent_to_object: out of memory");
122
123 spdxtool_serialize_object_list_free(object_list);
124 return ret;
125 }
126
127 /*
128 * !doc
129 *
130 * .. c:function:: spdxtool_core_creation_info_t *spdxtool_core_creation_info_new(pkgconf_client_t *client, const char *agent_id, const char *id, const char *time)
131 *
132 * Create new /Core/CreationInfo struct
133 *
134 * :param pkgconf_client_t *client: The pkgconf client being accessed.
135 * :param const char *agent_id: Agent spdxId
136 * :param const char *id: Id for creation info
137 * :param const char *time: If NULL current time is used if not then
138 * this time string is used. Time string should be
139 * in ISO8601 format: YYYY-MM-DDTHH:MM:SSZ
140 * :return: NULL if some problem occurs and CreationInfo struct if not
141 */
142 spdxtool_core_creation_info_t *
spdxtool_core_creation_info_new(pkgconf_client_t * client,const char * agent_id,const char * id,const char * time)143 spdxtool_core_creation_info_new(pkgconf_client_t *client, const char *agent_id, const char *id, const char *time)
144 {
145 if (!client || !agent_id || !id)
146 return NULL;
147
148 spdxtool_core_creation_info_t *creation = calloc(1, sizeof(spdxtool_core_creation_info_t));
149 if (!creation)
150 goto err;
151
152 creation->type = "CreationInfo";
153 creation->created_using = "pkgconf spdxtool";
154 creation->id = strdup(id);
155 creation->created = time ? strdup(time) : spdxtool_util_get_current_iso8601_time();
156 creation->created_by = strdup(agent_id);
157 creation->spec_version = strdup(spdxtool_util_get_spdx_version(client));
158
159 if (!creation->id || !creation->created || !creation->created_by || !creation->spec_version)
160 goto err;
161
162 return creation;
163
164 err:
165 pkgconf_error(client, "spdxtool_core_creation_info_new: out of memory");
166 spdxtool_core_creation_info_free(creation);
167 return NULL;
168 }
169
170 /*
171 * !doc
172 *
173 * .. c:function:: void spdxtool_core_creation_info_free(spdxtool_core_creation_info_t *creation)
174 *
175 * Free /Core/CreationInfo struct
176 *
177 * :param spdxtool_core_creation_info_t *creation: CreationInfo struct to be freed.
178 * :return: nothing
179 */
180 void
spdxtool_core_creation_info_free(spdxtool_core_creation_info_t * creation)181 spdxtool_core_creation_info_free(spdxtool_core_creation_info_t *creation)
182 {
183 if (!creation)
184 return;
185
186 free(creation->id);
187 free(creation->created);
188 free(creation->created_by);
189 free(creation->spec_version);
190
191 free(creation);
192 }
193
194 /*
195 * !doc
196 *
197 * .. c:function:: spdxtool_serialize_value_t *spdxtool_core_creation_info_to_object(pkgconf_client_t *client, const spdxtool_core_creation_info_t *creation)
198 *
199 * Serialize /Core/CreationInfo struct to a JSON value tree.
200 *
201 * :param pkgconf_client_t *client: The pkgconf client being accessed.
202 * :param const spdxtool_core_creation_info_t *creation: CreationInfo struct to be serialized.
203 * :return: spdxtool_serialize_value_t * representing the CreationInfo object.
204 */
205 spdxtool_serialize_value_t *
spdxtool_core_creation_info_to_object(pkgconf_client_t * client,const spdxtool_core_creation_info_t * creation)206 spdxtool_core_creation_info_to_object(pkgconf_client_t *client, const spdxtool_core_creation_info_t *creation)
207 {
208 spdxtool_serialize_value_t *ret = NULL;
209 spdxtool_serialize_object_list_t *object_list = spdxtool_serialize_object_list_new();
210 if (!object_list)
211 goto err;
212
213 spdxtool_serialize_array_t *created_by = spdxtool_serialize_array_new();
214 if (!created_by)
215 goto err;
216
217 if (!spdxtool_serialize_array_add_string(created_by, creation->created_by))
218 {
219 spdxtool_serialize_array_free(created_by);
220 goto err;
221 }
222
223 if (!(spdxtool_serialize_object_add_string(object_list, "type", creation->type) &&
224 spdxtool_serialize_object_add_string(object_list, "@id", creation->id) &&
225 spdxtool_serialize_object_add_string(object_list, "created", creation->created)))
226 {
227 /* created_by has not been handed to the object list yet */
228 spdxtool_serialize_array_free(created_by);
229 goto err;
230 }
231
232 /* object_add_array takes ownership of created_by, freeing it on failure */
233 if (!(spdxtool_serialize_object_add_array(object_list, "createdBy", created_by) &&
234 spdxtool_serialize_object_add_string(object_list, "specVersion", creation->spec_version)))
235 {
236 goto err;
237 }
238
239 ret = spdxtool_serialize_value_object(object_list);
240 object_list = NULL;
241
242 err:
243 if (!ret)
244 pkgconf_error(client, "spdxtool_core_creation_info_to_object: out of memory");
245
246 spdxtool_serialize_object_list_free(object_list);
247 return ret;
248 }
249
250 /*
251 * !doc
252 *
253 * .. c:function:: spdxtool_core_creation_info_t *spdxtool_core_creation_info_new(pkgconf_client_t *client, const char *spdx_id, const char *creation_id, const char *agent)
254 *
255 * Create new /Core/SpdxDocument struct
256 * In SPDX Lite SBOM there can be only one SpdxDocument
257 *
258 * :param pkgconf_client_t *client: The pkgconf client being accessed.
259 * :param const char *spdx_id: Id of this SpdxDocument
260 * :param const char *creation_id: Id for creation info
261 * :param const char *agent_id: Agent for this document
262 * :return: NULL if some problem occurs and SpdxDocument struct if not
263 */
264 spdxtool_core_spdx_document_t *
spdxtool_core_spdx_document_new(pkgconf_client_t * client,const char * spdx_id,const char * creation_id,const char * agent_id)265 spdxtool_core_spdx_document_new(pkgconf_client_t *client, const char *spdx_id, const char *creation_id, const char *agent_id)
266 {
267 if (!client || !spdx_id || !creation_id || !agent_id)
268 return NULL;
269
270 spdxtool_core_spdx_document_t *spdx = calloc(1, sizeof(spdxtool_core_spdx_document_t));
271 if (!spdx)
272 goto err;
273
274 spdx->type = "SpdxDocument";
275 spdx->spdx_id = strdup(spdx_id);
276 spdx->agent = strdup(agent_id);
277 spdx->creation_info = strdup(creation_id);
278
279 if (!spdx->spdx_id || !spdx->agent || !spdx->creation_info)
280 goto err;
281
282 return spdx;
283
284 err:
285 pkgconf_error(client, "spdxtool_core_spdx_document_new: out of memory");
286 spdxtool_core_spdx_document_free(spdx);
287 return NULL;
288 }
289
290 /*
291 * !doc
292 *
293 * .. c:function:: void spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx)
294 *
295 * Free /Core/SpdxDocument struct
296 *
297 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct to be freed.
298 * :return: nothing
299 */
300 void
spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t * spdx)301 spdxtool_core_spdx_document_free(spdxtool_core_spdx_document_t *spdx)
302 {
303 pkgconf_node_t *iter = NULL, *iter_next = NULL;
304
305 if (!spdx)
306 return;
307
308 free(spdx->spdx_id);
309 free(spdx->creation_info);
310 free(spdx->agent);
311
312 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->rootElement.head, iter_next, iter)
313 {
314 spdxtool_software_sbom_t *sbom = iter->data;
315 spdxtool_software_sbom_free(sbom);
316 free(iter);
317 }
318
319 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->element.head, iter_next, iter)
320 {
321 free(iter->data);
322 free(iter);
323 }
324
325 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->licenses.head, iter_next, iter)
326 {
327 spdxtool_simplelicensing_license_expression_t *expression = iter->data;
328 spdxtool_simplelicensing_licenseExpression_free(expression);
329 free(iter);
330 }
331
332 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->relationships.head, iter_next, iter)
333 {
334 spdxtool_core_relationship_t *relationship = iter->data;
335 spdxtool_core_relationship_free(relationship);
336 free(iter);
337 }
338
339 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->maintainers.head, iter_next, iter)
340 {
341 spdxtool_core_agent_t *maintainer = iter->data;
342 spdxtool_core_agent_free(maintainer);
343 free(iter);
344 }
345
346 PKGCONF_FOREACH_LIST_ENTRY_SAFE(spdx->packages.head, iter_next, iter)
347 {
348 free(iter);
349 }
350
351 free(spdx);
352 }
353
354 /*
355 * !doc
356 *
357 * .. c:function:: spdxtool_serialize_value_t *spdxtool_core_spdx_document_to_object(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx)
358 *
359 * Serialize /Core/SpdxDocument struct to a JSON value tree. This function
360 * should be called after all SBOMs and packages have been serialized so that
361 * the document's element and rootElement lists are fully populated.
362 *
363 * :param pkgconf_client_t *client: The pkgconf client being accessed.
364 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct to be serialized.
365 * :return: spdxtool_serialize_value_t * representing the SpdxDocument object.
366 */
367 spdxtool_serialize_value_t *
spdxtool_core_spdx_document_to_object(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx)368 spdxtool_core_spdx_document_to_object(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx)
369 {
370 spdxtool_serialize_value_t *ret = NULL;
371 spdxtool_serialize_object_list_t *object_list = NULL;
372 spdxtool_serialize_array_t *root_element_array = NULL;
373 spdxtool_serialize_array_t *element_array = NULL;
374
375 object_list = spdxtool_serialize_object_list_new();
376 if (!object_list)
377 goto err;
378
379 root_element_array = spdxtool_serialize_array_new();
380 if (!root_element_array)
381 goto err;
382
383 pkgconf_node_t *iter = NULL;
384 PKGCONF_FOREACH_LIST_ENTRY(spdx->rootElement.head, iter)
385 {
386 spdxtool_software_sbom_t *sbom = iter->data;
387 if (!spdxtool_serialize_array_add_string(root_element_array, sbom->spdx_id))
388 goto err;
389 }
390
391 element_array = spdxtool_serialize_array_new();
392 if (!element_array)
393 goto err;
394
395 if (!spdxtool_serialize_array_add_string(element_array, spdx->agent))
396 goto err;
397
398 PKGCONF_FOREACH_LIST_ENTRY(spdx->element.head, iter)
399 {
400 char *element_id = iter->data;
401 if (!spdxtool_serialize_array_add_string(element_array, element_id))
402 goto err;
403 }
404
405 PKGCONF_FOREACH_LIST_ENTRY(spdx->rootElement.head, iter)
406 {
407 spdxtool_software_sbom_t *sbom = iter->data;
408 char *pkg_spdx_id = spdxtool_util_tuple_lookup(client, &sbom->rootElement->vars, "spdxId");
409 if (!pkg_spdx_id)
410 goto err;
411
412 bool ok = spdxtool_serialize_array_add_string(element_array, sbom->spdx_id) &&
413 spdxtool_serialize_array_add_string(element_array, pkg_spdx_id);
414 free(pkg_spdx_id);
415
416 if (!ok)
417 goto err;
418 }
419
420 if (!(spdxtool_serialize_object_add_string(object_list, "type", spdx->type) &&
421 spdxtool_serialize_object_add_string(object_list, "creationInfo", spdx->creation_info) &&
422 spdxtool_serialize_object_add_string(object_list, "spdxId", spdx->spdx_id)))
423 {
424 goto err;
425 }
426
427 /* object_add_array always takes ownership of the array (it is freed even on
428 * failure), so clear our reference before checking the result to avoid a
429 * double free at the error label.
430 */
431 bool ok = spdxtool_serialize_object_add_array(object_list, "rootElement", root_element_array);
432 root_element_array = NULL;
433 if (!ok)
434 goto err;
435
436 ok = spdxtool_serialize_object_add_array(object_list, "element", element_array);
437 element_array = NULL;
438 if (!ok)
439 goto err;
440
441 ret = spdxtool_serialize_value_object(object_list);
442 object_list = NULL;
443
444 err:
445 if (!ret)
446 pkgconf_error(client, "spdxtool_core_spdx_document_to_object: out of memory");
447
448 spdxtool_serialize_object_list_free(object_list);
449 spdxtool_serialize_array_free(root_element_array);
450 spdxtool_serialize_array_free(element_array);
451 return ret;
452 }
453
454 /*
455 * !doc
456 *
457 * .. c:function:: bool spdxtool_core_spdx_document_is_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *license)
458 *
459 * Find out if specific license is already there.
460 *
461 * :param pkgconf_client_t *client: The pkgconf client being accessed.
462 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
463 * :param const char *license: SPDX name of license
464 * :return: true is license is there and false if not
465 */
466 bool
spdxtool_core_spdx_document_is_license(pkgconf_client_t * client,const spdxtool_core_spdx_document_t * spdx,const char * license)467 spdxtool_core_spdx_document_is_license(pkgconf_client_t *client, const spdxtool_core_spdx_document_t *spdx, const char *license)
468 {
469 pkgconf_node_t *iter = NULL;
470 spdxtool_simplelicensing_license_expression_t *expression = NULL;
471
472 (void) client;
473
474 if (!license || !spdx)
475 {
476 return false;
477 }
478
479 PKGCONF_FOREACH_LIST_ENTRY(spdx->licenses.head, iter)
480 {
481 expression = iter->data;
482 if (!strcmp(expression->license_expression, license))
483 {
484 return true;
485 }
486 }
487
488 return false;
489 }
490
491 /*
492 * !doc
493 *
494 * .. c:function:: bool spdxtool_core_spdx_document_add_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *license)
495 *
496 * Add license to SpdxDocument and make sure that specific license is not already there.
497 *
498 * :param pkgconf_client_t *client: The pkgconf client being accessed.
499 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
500 * :param const char *license: SPDX name of license
501 * :return: true on success, false on failure
502 */
503 bool
spdxtool_core_spdx_document_add_license(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx,const char * license)504 spdxtool_core_spdx_document_add_license(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *license)
505 {
506 if (!license || !spdx)
507 return false;
508
509 if (spdxtool_core_spdx_document_is_license(client, spdx, license))
510 return true;
511
512 pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
513 if (!node)
514 {
515 pkgconf_error(client, "spdxtool_core_spdx_document_add_license: out of memory");
516 return false;
517 }
518
519 spdxtool_simplelicensing_license_expression_t *expression = spdxtool_simplelicensing_licenseExpression_new(client, license);
520 if (!expression)
521 {
522 free(node);
523 return false;
524 }
525
526 pkgconf_node_insert_tail(node, expression, &spdx->licenses);
527 if (!spdxtool_core_spdx_document_add_element(client, spdx, expression->spdx_id))
528 return false;
529
530 return true;
531 }
532
533 /*
534 * !doc
535 *
536 * .. c:function:: bool spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *element)
537 *
538 * Add element spdxId to SpdxDocument
539 *
540 * :param pkgconf_client_t *client: The pkgconf client being accessed.
541 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
542 * :param char *element: spdxId of element
543 * :return: true on success, false on failure
544 */
545 bool
spdxtool_core_spdx_document_add_element(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx,const char * element)546 spdxtool_core_spdx_document_add_element(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *element)
547 {
548 if (!element || !spdx)
549 return false;
550
551 pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
552 if (!node)
553 {
554 pkgconf_error(client, "spdxtool_core_spdx_document_add_element: out of memory");
555 return false;
556 }
557
558 char *nelement = strdup(element);
559 if (!nelement)
560 {
561 pkgconf_error(client, "spdxtool_core_spdx_document_add_element: out of memory");
562 free(node);
563 return false;
564 }
565
566 pkgconf_node_insert_tail(node, nelement, &spdx->element);
567 return true;
568 }
569
570 /*
571 * !doc
572 *
573 * .. c:function:: const char *spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *name)
574 *
575 * Register a package maintainer as an Agent and add it to the SpdxDocument so that
576 * packages may reference it via their ``suppliedBy`` field. Maintainers are
577 * deduplicated by their spdxId, so a maintainer shared between packages is only
578 * emitted once. The first time a maintainer is seen, its spdxId is also added to
579 * the document element list.
580 *
581 * :param pkgconf_client_t *client: The pkgconf client being accessed.
582 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
583 * :param const char *name: Maintainer name as declared in the package.
584 * :return: the maintainer Agent spdxId (owned by the document) on success, NULL on failure
585 */
586 const char *
spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx,const char * name)587 spdxtool_core_spdx_document_add_maintainer(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, const char *name)
588 {
589 pkgconf_node_t *iter = NULL;
590
591 if (!client || !spdx || !name)
592 return NULL;
593
594 spdxtool_core_agent_t *agent = spdxtool_core_agent_new(client, spdx->creation_info, name);
595 if (!agent)
596 return NULL;
597
598 PKGCONF_FOREACH_LIST_ENTRY(spdx->maintainers.head, iter)
599 {
600 spdxtool_core_agent_t *existing = iter->data;
601 if (!strcmp(existing->spdx_id, agent->spdx_id))
602 {
603 spdxtool_core_agent_free(agent);
604 return existing->spdx_id;
605 }
606 }
607
608 pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
609 if (!node)
610 {
611 pkgconf_error(client, "spdxtool_core_spdx_document_add_maintainer: out of memory");
612 spdxtool_core_agent_free(agent);
613 return NULL;
614 }
615
616 pkgconf_node_insert_tail(node, agent, &spdx->maintainers);
617
618 if (!spdxtool_core_spdx_document_add_element(client, spdx, agent->spdx_id))
619 return NULL;
620
621 return agent->spdx_id;
622 }
623
624 /*
625 * !doc
626 *
627 * .. c:function:: bool spdxtool_core_spdx_document_add_relationship(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, spdxtool_core_relationship_t *relationship)
628 *
629 * Add relationship rel to SpdxDocument
630 *
631 * :param pkgconf_client_t *client: The pkgconf client being accessed.
632 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct being used.
633 * :param spdxtool_core_relationship_t *relationship: relationship to add.
634 * :return: true on success, false on failure
635 */
636 bool
spdxtool_core_spdx_document_add_relationship(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx,spdxtool_core_relationship_t * relationship)637 spdxtool_core_spdx_document_add_relationship(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, spdxtool_core_relationship_t *relationship)
638 {
639 if (!client || !spdx || !relationship)
640 return false;
641
642 pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
643 if (!node)
644 {
645 pkgconf_error(client, "spdxtool_core_spdx_document_add_relationship: out of memory");
646 return false;
647 }
648
649 pkgconf_node_insert_tail(node, relationship, &spdx->relationships);
650 return true;
651 }
652
653 /*
654 * !doc
655 *
656 * .. c:function:: bool spdxtool_core_spdx_document_add_package(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, pkgconf_pkg_t *pkg)
657 *
658 * Register a package with the SpdxDocument for later serialization. The document
659 * does not take ownership of the package pointer; the package must outlive the
660 * document and will not be freed by spdxtool_core_spdx_document_free.
661 *
662 * :param pkgconf_client_t *client: The pkgconf client being accessed.
663 * :param spdxtool_core_spdx_document_t *spdx: SpdxDocument struct to register the package with.
664 * :param pkgconf_pkg_t *pkg: Package to register. Ownership is NOT transferred.
665 * :return: true on success, false on failure
666 */
667 bool
spdxtool_core_spdx_document_add_package(pkgconf_client_t * client,spdxtool_core_spdx_document_t * spdx,pkgconf_pkg_t * pkg)668 spdxtool_core_spdx_document_add_package(pkgconf_client_t *client, spdxtool_core_spdx_document_t *spdx, pkgconf_pkg_t *pkg)
669 {
670 if (!client || !spdx || !pkg)
671 return false;
672
673 pkgconf_node_t *node = calloc(1, sizeof(pkgconf_node_t));
674 if (!node)
675 {
676 pkgconf_error(client, "spdxtool_core_spdx_document_add_package: out of memory");
677 return false;
678 }
679
680 pkgconf_node_insert_tail(node, pkg, &spdx->packages);
681 return true;
682 }
683
684 /*
685 * !doc
686 *
687 * .. c:function:: spdxtool_core_relationship_t *spdxtool_core_relationship_new(pkgconf_client_t *client, const char *creation_info_id, const char *spdx_id, const char *from, const char *to, const char *relationship_type)
688 *
689 * Create new /Core/Relationship struct
690 *
691 * :param pkgconf_client_t *client: The pkgconf client being accessed.
692 * :param const char *creation_id: Id for creation info
693 * :param const char *spdx_id: Id of this SpdxDocument
694 * :param const char *from: from spdxId
695 * :param const char *to: to spdxId
696 * :param const char *relationship_type: These can be found on SPDX documentation
697 * :return: NULL if some problem occurs and SpdxDocument struct if not
698 */
699 spdxtool_core_relationship_t *
spdxtool_core_relationship_new(pkgconf_client_t * client,const char * creation_info_id,const char * spdx_id,const char * from,pkgconf_list_t * to,const char * relationship_type)700 spdxtool_core_relationship_new(pkgconf_client_t *client, const char *creation_info_id, const char *spdx_id, const char *from, pkgconf_list_t *to, const char *relationship_type)
701 {
702 if (!client || !creation_info_id || !spdx_id || !from || !to || !relationship_type)
703 return NULL;
704
705 spdxtool_core_relationship_t *relationship = calloc(1, sizeof(spdxtool_core_relationship_t));
706 if (!relationship)
707 goto err;
708
709 relationship->type = "Relationship";
710 relationship->creation_info = strdup(creation_info_id);
711 relationship->spdx_id = strdup(spdx_id);
712 relationship->from = strdup(from);
713 relationship->to = to;
714 relationship->relationship_type = strdup(relationship_type);
715
716 if (!relationship->creation_info || !relationship->spdx_id || !relationship->from || !relationship->relationship_type)
717 goto err;
718
719 return relationship;
720
721 err:
722 pkgconf_error(client, "spdxtool_core_relationship_new: out of memory");
723 spdxtool_core_relationship_free(relationship);
724 return NULL;
725 }
726
727 /*
728 * !doc
729 *
730 * .. c:function:: bool spdxtool_core_relationship_set_scope(pkgconf_client_t *client, spdxtool_core_relationship_t *relationship, const char *scope)
731 *
732 * Promote a relationship to a /Core/LifecycleScopedRelationship by attaching a lifecycle
733 * scope (for example, ``development`` for a private build dependency). This is the SPDX 3.0
734 * representation of SPDX 2.x's ``*_DEPENDENCY_OF`` relationship variants.
735 *
736 * :param pkgconf_client_t *client: The pkgconf client being accessed.
737 * :param spdxtool_core_relationship_t *relationship: Relationship struct to scope.
738 * :param const char *scope: LifecycleScopeType value, e.g. "development".
739 * :return: true on success, false on failure
740 */
741 bool
spdxtool_core_relationship_set_scope(pkgconf_client_t * client,spdxtool_core_relationship_t * relationship,const char * scope)742 spdxtool_core_relationship_set_scope(pkgconf_client_t *client, spdxtool_core_relationship_t *relationship, const char *scope)
743 {
744 if (!relationship || !scope)
745 return false;
746
747 char *scope_copy = strdup(scope);
748 if (!scope_copy)
749 {
750 pkgconf_error(client, "spdxtool_core_relationship_set_scope: out of memory");
751 return false;
752 }
753
754 free(relationship->scope);
755 relationship->scope = scope_copy;
756 relationship->type = "LifecycleScopedRelationship";
757
758 return true;
759 }
760
761 /*
762 * !doc
763 *
764 * .. c:function:: void spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship)
765 *
766 * Free /Core/Relationship struct
767 *
768 * :param spdxtool_core_relationship_t *relationship: Relationship struct to be freed.
769 * :return: nothing
770 */
771 void
spdxtool_core_relationship_free(spdxtool_core_relationship_t * relationship)772 spdxtool_core_relationship_free(spdxtool_core_relationship_t *relationship)
773 {
774 if (!relationship)
775 return;
776
777 free(relationship->spdx_id);
778 free(relationship->creation_info);
779 free(relationship->from);
780 pkgconf_license_free(relationship->to);
781 free(relationship->to);
782 free(relationship->relationship_type);
783 free(relationship->scope);
784
785 free(relationship);
786 }
787
788 /*
789 * !doc
790 *
791 * .. c:function:: spdxtool_serialize_value_t *spdxtool_core_relationship_to_object(pkgconf_client_t *client, const spdxtool_core_relationship_t *relationship)
792 *
793 * Serialize /Core/Relationship struct to a JSON value tree.
794 *
795 * :param pkgconf_client_t *client: The pkgconf client being accessed.
796 * :param const spdxtool_core_relationship_t *relationship: Relationship struct to be serialized.
797 * :return: spdxtool_serialize_value_t * representing the Relationship object.
798 */
799 spdxtool_serialize_value_t *
spdxtool_core_relationship_to_object(pkgconf_client_t * client,const spdxtool_core_relationship_t * relationship)800 spdxtool_core_relationship_to_object(pkgconf_client_t *client, const spdxtool_core_relationship_t *relationship)
801 {
802 spdxtool_serialize_value_t *ret = NULL;
803 spdxtool_serialize_object_list_t *object_list = spdxtool_serialize_object_list_new();
804 if (!object_list)
805 goto err;
806
807 spdxtool_serialize_array_t *to = spdxtool_serialize_array_new();
808 if (!to)
809 goto err;
810
811 pkgconf_node_t *node = NULL;
812 PKGCONF_FOREACH_LIST_ENTRY(relationship->to->head, node)
813 {
814 const pkgconf_license_t *license = node->data;
815 if (!spdxtool_serialize_array_add_string(to, license->data))
816 {
817 spdxtool_serialize_array_free(to);
818 goto err;
819 }
820 }
821
822 if (!(spdxtool_serialize_object_add_string(object_list, "type", relationship->type) &&
823 spdxtool_serialize_object_add_string(object_list, "creationInfo", relationship->creation_info) &&
824 spdxtool_serialize_object_add_string(object_list, "spdxId", relationship->spdx_id) &&
825 spdxtool_serialize_object_add_string(object_list, "from", relationship->from)))
826 {
827 /* none of the above transfers ownership of `to` */
828 spdxtool_serialize_array_free(to);
829 goto err;
830 }
831
832 /* object_add_array always takes ownership of `to` (it is freed even on failure) */
833 if (!spdxtool_serialize_object_add_array(object_list, "to", to))
834 goto err;
835
836 if (!spdxtool_serialize_object_add_string(object_list, "relationshipType", relationship->relationship_type))
837 goto err;
838
839 if (relationship->scope != NULL &&
840 !spdxtool_serialize_object_add_string(object_list, "scope", relationship->scope))
841 {
842 goto err;
843 }
844
845 ret = spdxtool_serialize_value_object(object_list);
846 object_list = NULL;
847
848 err:
849 if (!ret)
850 pkgconf_error(client, "spdxtool_core_relationship_to_object: out of memory");
851
852 spdxtool_serialize_object_list_free(object_list);
853 return ret;
854 }
855