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 * 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 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 * 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 * 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 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 * 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 * 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 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 * 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 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 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 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 * 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 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 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 * 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 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 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 * 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