FOSSology  4.7.0
Open Source License Compliance by Open Source Software
spdx.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2015-2016,2023 Siemens AG
4  SPDX-FileCopyrightText: © 2017 TNG Technology Consulting GmbH
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
43 namespace Fossology\Spdx;
44 
66 use Twig\Environment;
67 
68 include_once(__DIR__ . "/spdxutils.php");
69 
70 include_once(__DIR__ . "/version.php");
71 include_once(__DIR__ . "/services.php");
72 
77 class SpdxAgent extends Agent
78 {
79 
80  const OUTPUT_FORMAT_KEY = "outputFormat";
81  const DEFAULT_OUTPUT_FORMAT = "spdx2";
82  const AVAILABLE_OUTPUT_FORMATS = "spdx2,spdx2tv,dep5,spdx2csv,spdx3jsonld,spdx3json,spdx3rdf,spdx3tv";
83  const UPLOAD_ADDS = "uploadsAdd";
84  const DATA_LICENSE = "CC0-1.0";
85 
89  private $uploadDao;
93  private $clearingDao;
97  private $licenseDao;
101  protected $dbManager;
105  protected $renderer;
109  private $licenseMap;
129  private $reportutils;
133  protected $agentNames = AgentRef::AGENT_LIST;
137  protected $filebasename = null;
141  protected $uri;
145  protected $filename;
150  private $licensesInDocument = [];
166  protected $dep5MainLicense = '';
170  protected $dep5LicenseComments = '';
171 
172  function __construct()
173  {
174  // deduce the agent name from the command line arguments
175  $args = getopt("", array(self::OUTPUT_FORMAT_KEY.'::'));
176  $agentName = "";
177  if (array_key_exists(self::OUTPUT_FORMAT_KEY, $args)) {
178  $agentName = trim($args[self::OUTPUT_FORMAT_KEY]);
179  }
180  if (empty($agentName)) {
182  }
183 
184  parent::__construct($agentName, AGENT_VERSION, AGENT_REV);
185 
186  $this->uploadDao = $this->container->get('dao.upload');
187  $this->clearingDao = $this->container->get('dao.clearing');
188  $this->licenseDao = $this->container->get('dao.license');
189  $this->dbManager = $this->container->get('db.manager');
190  $this->renderer = $this->container->get('twig.environment');
191  $this->renderer->setCache(false);
192 
193  $this->agentSpecifLongOptions[] = self::UPLOAD_ADDS.':';
194  $this->agentSpecifLongOptions[] = self::OUTPUT_FORMAT_KEY.':';
195 
196  $this->licenseClearedGetter = new LicenseClearedGetter();
197  $this->licenseMainGetter = new LicenseMainGetter();
198  $this->obligationsGetter = new ObligationsGetter();
199  $this->reportutils = new ReportUtils();
200  }
201 
207  protected function preWorkOnArgs($args)
208  {
209  if ((!array_key_exists(self::OUTPUT_FORMAT_KEY, $args)
210  || $args[self::OUTPUT_FORMAT_KEY] === "")
211  && array_key_exists(self::UPLOAD_ADDS,$args)) {
212  $args = SpdxUtils::preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
213  } else {
214  if (!array_key_exists(self::UPLOAD_ADDS,$args) || $args[self::UPLOAD_ADDS] === "") {
215  $args = SpdxUtils::preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
216  }
217  }
218  return $args;
219  }
220 
225  function processUploadId($uploadId)
226  {
227  $args = $this->preWorkOnArgs($this->args);
228 
229  if (array_key_exists(self::OUTPUT_FORMAT_KEY,$args)) {
230  $possibleOutputFormat = trim($args[self::OUTPUT_FORMAT_KEY]);
231  if (in_array($possibleOutputFormat, explode(',',self::AVAILABLE_OUTPUT_FORMATS))) {
232  $this->outputFormat = $possibleOutputFormat;
233  }
234  }
235  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, LicenseMap::REPORT, true);
236  $this->computeUri($uploadId);
237  $docLicense = $this->licenseDao->getLicenseByShortName(self::DATA_LICENSE);
238  $docLicenseId = $docLicense->getId() . "-" . md5($docLicense->getText());
239  $this->licensesInDocument[$docLicenseId] = (new SpdxLicenseInfo())
240  ->setLicenseObj($docLicense)
241  ->setListedLicense(true)
242  ->setCustomText(false)
243  ->setTextPrinted(true);
244  $packageNodes = $this->renderPackage($uploadId);
245  $additionalUploadIds = array_key_exists(self::UPLOAD_ADDS,$args) ? explode(',',$args[self::UPLOAD_ADDS]) : array();
246  $packageIds = array($uploadId);
247  foreach ($additionalUploadIds as $additionalId) {
248  $packageNodes .= $this->renderPackage($additionalId);
249  $packageIds[] = $additionalId;
250  }
251  $this->writeReport($packageNodes, $packageIds, $uploadId);
252  return true;
253  }
254 
260  protected function getTemplateFile($partname)
261  {
262  $prefix = $this->outputFormat . "-";
263  $postfix = ".twig";
264  switch ($this->outputFormat) {
265  case "spdx2":
266  $postfix = ".xml" . $postfix;
267  break;
268  case "spdx2csv":
269  case "spdx2tv":
270  break;
271  case "dep5":
272  $prefix .= "copyright-";
273  break;
274  case "spdx3jsonld":
275  break;
276  case "spdx3rdf":
277  $postfix = ".xml" . $postfix;
278  break;
279  case "spdx3json":
280  break;
281  case "spdx3tv":
282  break;
283  }
284  return $prefix . $partname . $postfix;
285  }
286 
294  protected function getFileBasename($packageName)
295  {
296  if ($this->filebasename == null) {
297  $fileName = strtoupper($this->outputFormat)."_".$packageName;
298  switch ($this->outputFormat) {
299  case "spdx2":
300  $fileName .= ".spdx.rdf";
301  break;
302  case "spdx2tv":
303  $fileName .= ".spdx";
304  break;
305  case "spdx2csv":
306  $fileName .= ".csv";
307  break;
308  case "dep5":
309  $fileName .= ".txt";
310  break;
311  case "spdx3jsonld":
312  $fileName .= ".jsonld";
313  break;
314  case "spdx3json":
315  $fileName .= ".json";
316  break;
317  case "spdx3rdf":
318  $fileName .= ".spdx.rdf";
319  break;
320  case "spdx3tv":
321  $fileName .= ".spdx";
322  break;
323  }
324  $this->filebasename = $fileName;
325  }
326  return $this->filebasename;
327  }
328 
334  protected function getFileName($packageName)
335  {
336  global $SysConf;
337  $fileBase = $SysConf['FOSSOLOGY']['path']."/report/";
338  return $fileBase. $this->getFileBasename($packageName);
339  }
340 
346  protected function getUri($packageName)
347  {
348  global $SysConf;
349  $url=$SysConf['SYSCONFIG']['FOSSologyURL'];
350  if (substr( $url, 0, 4 ) !== "http") {
351  $url="http://".$url;
352  }
353 
354  return rtrim($url, '/') . '/' . $this->getFileBasename($packageName);
355  }
356 
362  protected function renderPackage($uploadId)
363  {
364  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
365  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
366  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId,$uploadTreeTableName);
367  $this->heartbeat(0);
368 
369  $filesWithLicenses = $this->reportutils
370  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
371  $this, $this->licensesInDocument);
372  $this->heartbeat(0);
373 
374  $this->reportutils->addClearingStatus($filesWithLicenses,$itemTreeBounds, $this->groupId);
375  $this->heartbeat(0);
376 
377  $scannerIDs = $this->reportutils->addScannerResults($filesWithLicenses, $itemTreeBounds, $this->groupId, $this->licensesInDocument);
378  $licenseComment = "";
379  if (!empty($scannerIDs)) {
380  $licenseComment = $this->getLicenseComment($scannerIDs);
381  }
382  if ($this->outputFormat === 'dep5') {
383  $this->dep5LicenseComments = $licenseComment;
384  }
385  $this->heartbeat(0);
386 
387  $this->reportutils->addCopyrightResults($filesWithLicenses, $uploadId);
388  $this->heartbeat(0);
389 
390  $upload = $this->uploadDao->getUpload($uploadId);
391  $fileNodes = $this->generateFileNodes($filesWithLicenses, $upload->getTreeTableName(), $uploadId);
392 
393  $mainLicenseIds = $this->clearingDao->getMainLicenseIds($uploadId, $this->groupId);
394  $customMainLicenseTexts = $this->clearingDao->getMainLicenseReportInfos($uploadId, $this->groupId);
395  $mainLicenses = array();
396  foreach ($mainLicenseIds as $licId) {
397  $reportedLicenseId = $this->licenseMap->getProjectedId($licId);
398  $mainLicense = $this->licenseDao->getLicenseById($reportedLicenseId, $this->groupId);
399  if ($mainLicense === null) {
400  error_log(
401  "spdx: Warning: main license ID {$reportedLicenseId} not found; skipping."
402  );
403  continue; // Skip this license and continue with the next one
404  }
405  $customText = $customMainLicenseTexts[$licId] ?? null;
406  if (!empty($customText)) {
407  $reportLicId = $mainLicense->getId() . "-" . md5($customText);
408  $prefix = $stateOsselot ? LicenseRef::SPDXREF_PREFIX : LicenseRef::SPDXREF_PREFIX_FOSSOLOGY;
409  $customShortName = $prefix . $mainLicense->getShortName() . "-" . md5($customText);
410  $customLicense = new License(
411  $mainLicense->getId(),
412  $customShortName,
413  $mainLicense->getFullName(),
414  $mainLicense->getRisk(),
415  $customText,
416  $mainLicense->getUrl(),
417  $mainLicense->getDetectorType(),
418  ''
419  );
420  $this->licensesInDocument[$reportLicId] = (new SpdxLicenseInfo())
421  ->setLicenseObj($customLicense)
422  ->setCustomText(true)
423  ->setListedLicense(false);
424  } else {
425  $reportLicId = $mainLicense->getId() . "-" . md5($mainLicense->getText());
426  if (!array_key_exists($reportLicId, $this->licensesInDocument)) {
427  $listedLicense = stripos($mainLicense->getSpdxId(),
428  LicenseRef::SPDXREF_PREFIX) !== 0;
429  $this->licensesInDocument[$reportLicId] = (new SpdxLicenseInfo())
430  ->setLicenseObj($mainLicense)
431  ->setCustomText(false)
432  ->setListedLicense($listedLicense);
433  }
434  }
435  $mainLicenses[] = $reportLicId;
436  }
437  $mainLicenseString = [];
438  if ($this->outputFormat == "spdx2tv" ||
439  $this->outputFormat == "spdx2csv" ||
440  $this->outputFormat == "spdx3tv" ||
441  $this->outputFormat == "dep5") {
442  foreach ($mainLicenses as $mainLicense) {
443  $shortName = $this->licensesInDocument[$mainLicense]
444  ->getLicenseObj()->getShortName();
445  if (StringOperation::stringStartsWith($shortName,
446  LicenseRef::SPDXREF_PREFIX)) {
447  $mainLicenseString[] = $shortName;
448  } else {
449  $mainLicenseString[] = $this->licensesInDocument[$mainLicense]
450  ->getLicenseObj()->getSpdxId();
451  }
452  }
453  if ($this->outputFormat == "spdx2tv") {
454  if ($stateOsselot) {
455  $mainLicenseString = SpdxUtils::removeEmptyLicenses($mainLicenseString);
456  foreach ($mainLicenseString as $i => $licId) {
457  if (StringOperation::stringStartsWith($licId, LicenseRef::SPDXREF_PREFIX)
458  || StringOperation::stringStartsWith($licId, "Dual-license")) {
459  continue;
460  }
461  $mainLicenseString[$i] = "LicenseRef-$licId";
462  }
463  }
464  }
465  $mainLicenseString = SpdxUtils::implodeLicenses(
466  SpdxUtils::removeEmptyLicenses($mainLicenseString));
467  if ($this->outputFormat == "dep5") {
468  $this->dep5MainLicense = $mainLicenseString;
469  }
470  }
471 
472  $hashes = $this->uploadDao->getUploadHashes($uploadId);
473 
474  $reportInfo = $this->uploadDao->getReportInfo($uploadId);
475  $componentId = $reportInfo['ri_component_id'];
476  $componentType = $reportInfo['ri_component_type'];
477  $componentVersion = $reportInfo['ri_version'];
478  $generalAssessment = $reportInfo['ri_general_assesment'];
479  $releaseDate = $reportInfo['ri_release_date'];
480  if ($componentId == "NA") {
481  $componentId = "";
482  }
483  if ($componentVersion == "NA") {
484  $componentVersion = "";
485  }
486  if ($generalAssessment == "NA") {
487  $generalAssessment = "";
488  }
489  if ($releaseDate == "NA") {
490  $releaseDate = "";
491  } else {
492  $timeStamp = strtotime($releaseDate);
493  if ($timeStamp != -1) {
494  $releaseDate = date("Y-m-d\\T00:00:00\\Z", $timeStamp);
495  } else {
496  $releaseDate = "";
497  }
498  }
499  if ($componentType == ComponentType::MAVEN) {
500  $componentType = "maven-central";
501  } elseif ($componentType == ComponentType::PACKAGEURL) {
502  $componentType = "purl";
503  } else {
504  if (!empty($componentType)) {
505  $componentType = ComponentType::TYPE_MAP[$componentType];
506  } else {
508  }
509  }
510  $obligations = $this->getObligations($uploadId, $this->groupId);
511 
512  return $this->renderString($this->getTemplateFile('package'), [
513  'packageId' => $uploadId,
514  'uri' => $this->uri,
515  'packageName' => $upload->getFilename(),
516  'packageVersion' => $componentVersion,
517  'releaseDate' => $releaseDate,
518  'generalAssessment' => $generalAssessment,
519  'uploadName' => $upload->getFilename(),
520  'componentType' => $componentType,
521  'componentId' => htmlspecialchars($componentId),
522  'sha1' => $hashes['sha1'],
523  'md5' => $hashes['md5'],
524  'sha256' => $hashes['sha256'],
525  'verificationCode' => $this->getVerificationCode($upload),
526  'mainLicenses' => $mainLicenses,
527  'mainLicenseString' => $mainLicenseString,
528  'licenseComments' => $licenseComment,
529  'fileNodes' => $fileNodes,
530  'obligations' => $obligations,
531  'licenseList' => $this->licensesInDocument,
532  'EnableOsselotExport' => $stateOsselot
533  ]);
534  }
535 
540  protected function getLicenseComment($scannerIds)
541  {
543  $func = function($scannerId) use ($agentDao)
544  {
545  return $agentDao->getAgentName($scannerId)." (".$agentDao->getAgentRev($scannerId).")";
546  };
547  $scannerNames = array_map($func, $scannerIds);
548  return "licenseInfoInFile determined by Scanners:\n - ".implode("\n - ",$scannerNames);
549  }
550 
559  protected function toLicensesWithFilesAdder(&$filesWithLicenses, $licenses, $copyrights, $file, $fullPath)
560  {
561  if (!array_key_exists($licenses, $filesWithLicenses)) {
562  $filesWithLicenses[$licenses]['files']=array();
563  $filesWithLicenses[$licenses]['copyrights']=array();
564  }
565  if (empty($copyrights)) {
566  $copyrights = array();
567  }
568  $filesWithLicenses[$licenses]['files'][$file] = $fullPath;
569  foreach ($copyrights as $copyright) {
570  if (!in_array($copyright, $filesWithLicenses[$licenses]['copyrights'])) {
571  $filesWithLicenses[$licenses]['copyrights'][] = $copyright;
572  }
573  }
574  }
575 
582  protected function toLicensesWithFiles(&$filesWithLicenses, $treeTableName)
583  {
584  $licensesWithFiles = array();
585  $treeDao = $this->container->get('dao.tree');
586  $filesProceeded = 0;
587  foreach ($filesWithLicenses as $fileId => $fileNode) {
588  $filesProceeded += 1;
589  if (($filesProceeded&2047)==0) {
590  $this->heartbeat(0);
591  }
592  $fullPath = $treeDao->getFullPath($fileId, $treeTableName, 0);
593  if (! empty($fileNode->getConcludedLicenses())) {
594  $licenses = [];
595  foreach ($fileNode->getConcludedLicenses() as $license) {
596  $licenses[] = $this->licensesInDocument[$license]
597  ->getLicenseObj()->getSpdxId();
598  }
599  $licenses = SpdxUtils::implodeLicenses(
600  SpdxUtils::removeEmptyLicenses(array_unique($licenses)));
601  $this->toLicensesWithFilesAdder($licensesWithFiles,
602  $licenses, $fileNode->getCopyrights(), $fileId, $fullPath);
603  } else {
604  if (! empty($fileNode->getScanners())) {
605  $implodedLicenses = [];
606  foreach ($fileNode->getScanners() as $license) {
607  $implodedLicenses[] = $this->licensesInDocument[$license]
608  ->getLicenseObj()->getSpdxId();
609  }
610  $implodedLicenses = SpdxUtils::implodeLicenses(
611  SpdxUtils::removeEmptyLicenses(array_unique($implodedLicenses)));
612  if ($fileNode->isCleared()) {
613  $msgLicense = "None (scanners found: " . $implodedLicenses . ")";
614  } else {
615  $msgLicense = "NoLicenseConcluded (scanners found: " . $implodedLicenses . ")";
616  }
617  } else {
618  if ($fileNode->isCleared()) {
619  $msgLicense = "None";
620  } else {
621  $msgLicense = "NoLicenseConcluded";
622  }
623  }
624  $this->toLicensesWithFilesAdder($licensesWithFiles, $msgLicense,
625  $fileNode->getCopyrights(), $fileId, $fullPath);
626  }
627  }
628  return $licensesWithFiles;
629  }
630 
635  protected function computeUri($uploadId)
636  {
637  $upload = $this->uploadDao->getUpload($uploadId);
638  $packageName = $upload->getFilename();
639 
640  $this->uri = $this->getUri($packageName);
641  $this->filename = $this->getFileName($packageName);
642  }
643 
650  protected function writeReport(&$packageNodes, $packageIds, $uploadId)
651  {
652  global $SysConf;
653  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
654  $fileBase = dirname($this->filename);
655 
656  if (!is_dir($fileBase)) {
657  mkdir($fileBase, 0777, true);
658  }
659  umask(0133);
660 
661  $organizationName = $SysConf['SYSCONFIG']["ReportHeaderText"] ?? 'FOSSology';
662  $version = $SysConf['BUILD']['VERSION'];
663 
664  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
665  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId,$uploadTreeTableName);
666  $this->heartbeat(0);
667 
668  $filesWithLicenses = $this->reportutils
669  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
670  $this, $this->licensesInDocument);
671  $this->heartbeat(0);
672  $upload = $this->uploadDao->getUpload($uploadId);
673  $this->generateFileNodes($filesWithLicenses, $upload->getTreeTableName(), $uploadId);
674  $this->declaredLicenseFileIds = array_unique(array_diff($this->declaredLicenseFileIds, $this->concludedLicenseFileIds));
675  $this->concludedLicenseFileIds = array_unique($this->concludedLicenseFileIds);
676  $this->validateLicenseIdentifiers();
677  $message = $this->renderString($this->getTemplateFile('document'),array(
678  'documentName' => $upload->getFilename(),
679  'uri' => $this->uri,
680  'mainLicense' => $this->dep5MainLicense,
681  'licenseComments' => $this->dep5LicenseComments,
682  'userName' => $this->container->get('dao.user')->getUserName($this->userId) . " (" . $this->container->get('dao.user')->getUserEmail($this->userId) . ")",
683  'organisation' => $organizationName,
684  'concludedLicenseFileIds'=>$this->concludedLicenseFileIds,
685  'declaredLicenseFileIds'=>$this->declaredLicenseFileIds,
686  'toolVersion' => 'fossology-' . $version,
687  'packageNodes' => $packageNodes,
688  'packageIds' => $packageIds,
689  'dataLicense' => $this->getSPDXDataLicense(),
690  'licenseList' => $this->licensesInDocument,
691  'EnableOsselotExport' => $stateOsselot,
692  )
693  );
694 
695  // To ensure the file is valid, replace any non-printable characters with a question mark.
696  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F (delete).
697  $message = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/','?',$message);
698 
699  file_put_contents($this->filename, $message);
700  $this->updateReportTable($uploadId, $this->jobId, $this->filename);
701  }
702 
709  protected function updateReportTable($uploadId, $jobId, $fileName)
710  {
711  $this->reportutils->updateOrInsertReportgenEntry($uploadId, $jobId, $fileName);
712  }
713 
714  protected function validateLicenseIdentifiers()
715  {
716  $issues = [];
717  foreach ($this->licensesInDocument as $licenseInfo) {
718  $license = $licenseInfo->getLicenseObj();
719  $spdxId = $license->getSpdxId();
720  $shortName = $license->getShortName();
721 
722  if (!SpdxLicenseValidator::isLicenseRef($spdxId)) {
723  continue;
724  }
725  if (preg_match('/[^a-zA-Z0-9.\-\s]/', $shortName)) {
726  $idstring = SpdxLicenseValidator::extractIdstring($spdxId);
727  $fossologyPrefix = substr(LicenseRef::SPDXREF_PREFIX_FOSSOLOGY,
728  strlen(LicenseRef::SPDXREF_PREFIX));
729  if (strpos($idstring, $fossologyPrefix) === 0) {
730  $idstring = substr($idstring, strlen($fossologyPrefix));
731  }
732  $issues[] = sprintf(" \"%s\" -> sanitized to \"%s\"",
733  $shortName, $idstring);
734  }
735  }
736 
737  if (!empty($issues)) {
738  error_log("spdx: License shortnames with invalid SPDX characters were sanitized:\n"
739  . implode("\n", $issues));
740  }
741  }
742 
749  protected function renderString($templateName, $vars)
750  {
751  return $this->renderer->load($templateName)->render($vars);
752  }
753 
761  protected function generateFileNodes($filesWithLicenses, $treeTableName, $uploadId)
762  {
763  $this->deduplicateLicenseList();
764  if (strcmp($this->outputFormat, "dep5") !== 0) {
765  return $this->generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId);
766  } else {
767  return $this->generateFileNodesByLicenses($filesWithLicenses, $treeTableName);
768  }
769  }
770 
778  protected function generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId)
779  {
780  /* @var $treeDao TreeDao */
781  $treeDao = $this->container->get('dao.tree');
782  $filesProceeded = 0;
783  $lastValue = 0;
784  $content = '';
785  $textToBePrinted = [];
786  foreach ($filesWithLicenses as $fileId => $fileData) {
787  $filesProceeded += 1;
788  if (($filesProceeded & 2047) == 0) {
789  $this->heartbeat($filesProceeded - $lastValue);
790  $lastValue = $filesProceeded;
791  }
792  $hashes = $treeDao->getItemHashes($fileId);
793  $fileName = $treeDao->getFullPath($fileId, $treeTableName, 0);
794  $stateComment = $this->getSPDXReportConf($uploadId, 0);
795  $stateWoInfos = $this->getSPDXReportConf($uploadId, 1);
796  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
797  foreach ($fileData->getConcludedLicenses() as $license) {
798  $this->concludedLicenseFileIds[] = $fileId;
799  if (! $this->licensesInDocument[$license]->isTextPrinted()) {
800  $textToBePrinted[] = $license;
801  }
802  }
803  foreach ($fileData->getScanners() as $license) {
804  $this->declaredLicenseFileIds[] = $fileId;
805  if (! $this->licensesInDocument[$license]->isTextPrinted()) {
806  $textToBePrinted[] = $license;
807  }
808  }
809  $concludedLicensesString = [];
810  if ($this->outputFormat == "spdx2tv" ||
811  $this->outputFormat == "spdx2csv" ||
812  $this->outputFormat == "spdx3tv") {
813  foreach ($fileData->getConcludedLicenses() as $license) {
814  $shortName = $this->licensesInDocument[$license]
815  ->getLicenseObj()->getShortName();
816  if (StringOperation::stringStartsWith($shortName,
817  LicenseRef::SPDXREF_PREFIX)) {
818  $concludedLicensesString[] = $shortName;
819  } else {
820  $concludedLicensesString[] = $this->licensesInDocument[$license]
821  ->getLicenseObj()->getSpdxId();
822  }
823  }
824  if ($this->outputFormat == "spdx2tv") {
825  if ($stateOsselot) {
826  foreach ($concludedLicensesString as $j => $licId) {
827  if (StringOperation::stringStartsWith($licId, LicenseRef::SPDXREF_PREFIX)
828  || StringOperation::stringStartsWith($licId, "Dual-license")) {
829  continue;
830  }
831  $concludedLicensesString[$j] = "LicenseRef-$licId";
832  }
833  }
834  $concludedLicensesString = SpdxUtils::implodeLicenses(
835  SpdxUtils::removeEmptyLicenses($concludedLicensesString)
836  );
837  } else {
838  $concludedLicensesString = SpdxUtils::implodeLicenses(
839  SpdxUtils::removeEmptyLicenses($concludedLicensesString));
840  }
841  }
842  if (!$stateWoInfos ||
843  ($stateWoInfos && (!empty($fileData->getConcludedLicenses()) ||
844  !empty($fileData->getScanners()) || !empty($fileData->getCopyrights())))) {
845  $fileData->setAcknowledgements(
846  SpdxUtils::cleanTextArray($fileData->getAcknowledgements()));
847  $fileData->setComments(
848  SpdxUtils::cleanTextArray($fileData->getComments()));
849  $dataTemplate = array(
850  'fileId' => $fileId,
851  'sha1' => $hashes['sha1'],
852  'md5' => $hashes['md5'],
853  'sha256' => $hashes['sha256'],
854  'uri' => $this->uri,
855  'fileName' => $fileName,
856  'fileDirName' => dirname($fileName),
857  'fileBaseName' => basename($fileName),
858  'fileData' => $fileData,
859  'licenseList' => $this->licensesInDocument,
860  'concludedLicensesString' => $concludedLicensesString,
861  'licenseCommentState' => $stateComment,
862  'EnableOsselotExport' =>$stateOsselot,
863  );
864  $content .= $this->renderString($this->getTemplateFile('file'),
865  $dataTemplate);
866  }
867  foreach ($textToBePrinted as $license) {
868  $this->licensesInDocument[$license]->setTextPrinted(true);
869  }
870  }
871  $this->heartbeat($filesProceeded - $lastValue);
872  return $content;
873  }
874 
881  protected function generateFileNodesByLicenses($filesWithLicenses, $treeTableName)
882  {
883  $licensesWithFiles = $this->toLicensesWithFiles($filesWithLicenses, $treeTableName);
884 
885  $content = '';
886  $filesProceeded = 0;
887  $lastStep = 0;
888  $lastValue = 0;
889  foreach ($licensesWithFiles as $licenseId=>$entry) {
890  $filesProceeded += count($entry['files']);
891  if ($filesProceeded&(~2047) > $lastStep) {
892  $this->heartbeat($filesProceeded - $lastValue);
893  $lastStep = $filesProceeded&(~2047) + 2048;
894  $lastValue = $filesProceeded;
895  }
896 
897  $comment = "";
898  if (strrpos($licenseId, "NoLicenseConcluded (scanners found: ", -strlen($licenseId)) !== false) {
899  $comment = substr($licenseId,20,strlen($licenseId)-21);
900  $licenseId = "NoLicenseConcluded";
901  } elseif (strrpos($licenseId, "None (scanners found: ", -strlen($licenseId)) !== false) {
902  $comment = substr($licenseId,6,strlen($licenseId)-7);
903  $licenseId = "None";
904  }
905  if ($this->outputFormat === 'dep5' &&
906  ($licenseId === 'NoLicenseConcluded' || $licenseId === 'None')) {
907  $licenseId = 'UNKNOWN';
908  }
909 
910  $content .= $this->renderString($this->getTemplateFile('file'),array(
911  'fileNames'=>$entry['files'],
912  'license'=>$licenseId,
913  'copyrights'=>$entry['copyrights'],
914  'comment'=>$comment));
915  }
916  $this->heartbeat($filesProceeded - $lastValue);
917  return $content;
918  }
919 
928  protected function getVerificationCode(Upload $upload)
929  {
930  $stmt = __METHOD__;
931  $param = array();
932  if ($upload->getTreeTableName()=='uploadtree_a') {
933  $sql = $upload->getTreeTableName().' WHERE upload_fk=$1 AND';
934  $param[] = $upload->getId();
935  } else {
936  $sql = $upload->getTreeTableName().' WHERE';
937  $stmt .= '.'.$upload->getTreeTableName();
938  }
939 
940  $sql = "SELECT STRING_AGG(lower_sha1,'') concat_sha1 FROM
941  (SELECT LOWER(pfile_sha1) lower_sha1 FROM pfile, $sql pfile_fk=pfile_pk AND parent IS NOT NULL ORDER BY pfile_sha1) templist";
942  $filelistPack = $this->dbManager->getSingleRow($sql,$param,$stmt);
943 
944  return sha1($filelistPack['concat_sha1']);
945  }
946 
954  protected function getSPDXReportConf($uploadId, $key)
955  {
956  $sql = "SELECT ri_spdx_selection FROM report_info WHERE upload_fk = $1";
957  $getCommentState = $this->dbManager->getSingleRow($sql, array($uploadId), __METHOD__.'.SPDX_license_comment');
958  if (!empty($getCommentState['ri_spdx_selection'])) {
959  $getCommentStateSingle = explode(',', $getCommentState['ri_spdx_selection']);
960  if ($getCommentStateSingle[$key] === "checked") {
961  return true;
962  }
963  }
964  return false;
965  }
972  private function getObligations(int $uploadId, int $groupId): array
973  {
974  $licenses = $this->licenseClearedGetter->getCleared($uploadId, $this,
975  $groupId, true, "license", false);
976  $this->heartbeat(0);
977  $licensesMain = $this->licenseMainGetter->getCleared($uploadId, $this,
978  $groupId, true, null, false);
979  $this->heartbeat(0);
980  list($obligations, $_) = $this->obligationsGetter->getObligations(
981  $licenses['statements'], $licensesMain['statements'], $uploadId,
982  $groupId);
983  if (empty($obligations)) {
984  return [];
985  } else {
986  return array_column($obligations, "text");
987  }
988  }
989 
994  protected function getSPDXDataLicense()
995  {
996  $dataLic = $this->licenseDao->getLicenseByShortName(self::DATA_LICENSE);
997  return $dataLic->getId() . "-" . md5($dataLic->getText());
998  }
999 
1011  private function deduplicateLicenseList()
1012  {
1013  $localList = array_values($this->licensesInDocument);
1014  usort($localList,
1015  function(SpdxLicenseInfo $a, SpdxLicenseInfo $b) {
1016  return strcmp(
1017  $a->getLicenseObj()->getSpdxId() . $a->getLicenseObj()->getShortName(),
1018  $b->getLicenseObj()->getSpdxId() . $b->getLicenseObj()->getShortName());
1019  }
1020  );
1021  for ($i = 0; $i < count($localList) - 1; $i++) {
1022  if ((! $localList[$i]->isCustomText() && ! $localList[$i + 1]->isCustomText()) &&
1023  $localList[$i]->getLicenseObj()->getSpdxId() ===
1024  $localList[$i + 1]->getLicenseObj()->getSpdxId()) {
1025  $newShortName = $localList[$i + 1]->getLicenseObj()->getShortName();
1027  $localList[$i + 1]->getLicenseObj()->getShortName(),
1028  LicenseRef::SPDXREF_PREFIX)) {
1029  $newShortName = LicenseRef::SPDXREF_PREFIX .
1030  $localList[$i + 1]->getLicenseObj()->getShortName();
1031  $newShortName = preg_replace('/\+$/', '-or-later', $newShortName);
1032  }
1033  $md5 = md5($localList[$i + 1]->getLicenseObj()->getText());
1034  $reportedLicenseShortname = "$newShortName-$md5";
1035  $licIndex = $localList[$i + 1]->getLicenseObj()->getId() . "-$md5";
1036  $oldLicObj = $this->licensesInDocument[$licIndex]->getLicenseObj();
1037  $this->licensesInDocument[$licIndex]->setLicenseObj(
1038  new License($oldLicObj->getId(), $reportedLicenseShortname,
1039  $oldLicObj->getFullName(), $oldLicObj->getRisk(),
1040  $oldLicObj->getText(), $oldLicObj->getUrl(),
1041  $oldLicObj->getDetectorType(), $oldLicObj->getSpdxId()));
1042  }
1043  }
1044  }
1045 }
1046 
1047 $agent = new SpdxAgent();
1048 $agent->scheduler_connect();
1049 $agent->run_scheduler_event_loop();
1050 $agent->scheduler_disconnect(0);
Structure of an Agent with all required parameters.
Definition: Agent.php:41
heartbeat($newProcessed)
Send hear beat to the scheduler.
Definition: Agent.php:203
Contains the constants and helpers for authentication of user.
Definition: Auth.php:24
Wrapper class for license map.
Definition: LicenseMap.php:19
Validate and sanitize SPDX LicenseRef identifiers.
static stringStartsWith($haystack, $needle)
SPDX2 agent.
Definition: spdx.php:78
const DEFAULT_OUTPUT_FORMAT
Default output format.
Definition: spdx.php:81
getLicenseComment($scannerIds)
Definition: spdx.php:540
getFileName($packageName)
Get absolute path for report.
Definition: spdx.php:334
getVerificationCode(Upload $upload)
Get a unique identifier for a given upload.
Definition: spdx.php:928
getTemplateFile($partname)
Get TWIG template file based on output format.
Definition: spdx.php:260
const UPLOAD_ADDS
Argument for additional uploads.
Definition: spdx.php:83
getObligations(int $uploadId, int $groupId)
Definition: spdx.php:972
processUploadId($uploadId)
Given an upload ID, process the items in it.
Definition: spdx.php:225
const DATA_LICENSE
Data license for SPDX reports.
Definition: spdx.php:84
updateReportTable($uploadId, $jobId, $fileName)
Update the reportgen table with new report path.
Definition: spdx.php:709
preWorkOnArgs($args)
Parse arguments.
Definition: spdx.php:207
renderString($templateName, $vars)
Render a twig template.
Definition: spdx.php:749
const AVAILABLE_OUTPUT_FORMATS
Output formats available.
Definition: spdx.php:82
renderPackage($uploadId)
Given an upload id, render the report string.
Definition: spdx.php:362
getFileBasename($packageName)
Generate report basename based on upload name.
Definition: spdx.php:294
generateFileNodesByLicenses($filesWithLicenses, $treeTableName)
For each file, generate the nodes by licenses.
Definition: spdx.php:881
toLicensesWithFiles(&$filesWithLicenses, $treeTableName)
Map findings to the files.
Definition: spdx.php:582
generateFileNodes($filesWithLicenses, $treeTableName, $uploadId)
Generate report nodes for files.
Definition: spdx.php:761
getSPDXReportConf($uploadId, $key)
Get spdx license comment state for a given upload.
Definition: spdx.php:954
toLicensesWithFilesAdder(&$filesWithLicenses, $licenses, $copyrights, $file, $fullPath)
Map licenses, copyrights, files and full path to filesWithLicenses array.
Definition: spdx.php:559
generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId)
For each file, generate the nodes by files.
Definition: spdx.php:778
writeReport(&$packageNodes, $packageIds, $uploadId)
Write the report the file and update report table.
Definition: spdx.php:650
computeUri($uploadId)
For a given upload, compute the URI and filename for the report.
Definition: spdx.php:635
const OUTPUT_FORMAT_KEY
Argument key for output format.
Definition: spdx.php:80
getUri($packageName)
Get the URI for the given package.
Definition: spdx.php:346
deduplicateLicenseList()
De-duplicate license list by comparing licenses with the same SPDX ID.
Definition: spdx.php:1011
static preWorkOnArgsFlp($args, $key1, $key2)
For a given set of arguments assign $args[$key1] and $args[$key2].
Definition: spdxutils.php:29
static implodeLicenses($licenses)
Implode licenses with "AND" or "OR".
Definition: spdxutils.php:97
static removeEmptyLicenses($licenses)
Definition: spdxutils.php:148
static cleanTextArray($texts)
Definition: spdxutils.php:124
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
int jobId
The id of the job.
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
FUNCTION char * strtoupper(char *s)
Helper function to upper case a string.
Definition: utils.c:39
Namespace used by SPDX2 agent.