FOSSology  4.6.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 = [];
163 
164  function __construct()
165  {
166  // deduce the agent name from the command line arguments
167  $args = getopt("", array(self::OUTPUT_FORMAT_KEY.'::'));
168  $agentName = "";
169  if (array_key_exists(self::OUTPUT_FORMAT_KEY, $args)) {
170  $agentName = trim($args[self::OUTPUT_FORMAT_KEY]);
171  }
172  if (empty($agentName)) {
174  }
175 
176  parent::__construct($agentName, AGENT_VERSION, AGENT_REV);
177 
178  $this->uploadDao = $this->container->get('dao.upload');
179  $this->clearingDao = $this->container->get('dao.clearing');
180  $this->licenseDao = $this->container->get('dao.license');
181  $this->dbManager = $this->container->get('db.manager');
182  $this->renderer = $this->container->get('twig.environment');
183  $this->renderer->setCache(false);
184 
185  $this->agentSpecifLongOptions[] = self::UPLOAD_ADDS.':';
186  $this->agentSpecifLongOptions[] = self::OUTPUT_FORMAT_KEY.':';
187 
188  $this->licenseClearedGetter = new LicenseClearedGetter();
189  $this->licenseMainGetter = new LicenseMainGetter();
190  $this->obligationsGetter = new ObligationsGetter();
191  $this->reportutils = new ReportUtils();
192  }
193 
199  protected function preWorkOnArgs($args)
200  {
201  if ((!array_key_exists(self::OUTPUT_FORMAT_KEY, $args)
202  || $args[self::OUTPUT_FORMAT_KEY] === "")
203  && array_key_exists(self::UPLOAD_ADDS,$args)) {
204  $args = SpdxUtils::preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
205  } else {
206  if (!array_key_exists(self::UPLOAD_ADDS,$args) || $args[self::UPLOAD_ADDS] === "") {
207  $args = SpdxUtils::preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
208  }
209  }
210  return $args;
211  }
212 
217  function processUploadId($uploadId)
218  {
219  $args = $this->preWorkOnArgs($this->args);
220 
221  if (array_key_exists(self::OUTPUT_FORMAT_KEY,$args)) {
222  $possibleOutputFormat = trim($args[self::OUTPUT_FORMAT_KEY]);
223  if (in_array($possibleOutputFormat, explode(',',self::AVAILABLE_OUTPUT_FORMATS))) {
224  $this->outputFormat = $possibleOutputFormat;
225  }
226  }
227  $this->licenseMap = new LicenseMap($this->dbManager, $this->groupId, LicenseMap::REPORT, true);
228  $this->computeUri($uploadId);
229  $docLicense = $this->licenseDao->getLicenseByShortName(self::DATA_LICENSE);
230  $docLicenseId = $docLicense->getId() . "-" . md5($docLicense->getText());
231  $this->licensesInDocument[$docLicenseId] = (new SpdxLicenseInfo())
232  ->setLicenseObj($docLicense)
233  ->setListedLicense(true)
234  ->setCustomText(false)
235  ->setTextPrinted(true);
236  $packageNodes = $this->renderPackage($uploadId);
237  $additionalUploadIds = array_key_exists(self::UPLOAD_ADDS,$args) ? explode(',',$args[self::UPLOAD_ADDS]) : array();
238  $packageIds = array($uploadId);
239  foreach ($additionalUploadIds as $additionalId) {
240  $packageNodes .= $this->renderPackage($additionalId);
241  $packageIds[] = $additionalId;
242  }
243  $this->writeReport($packageNodes, $packageIds, $uploadId);
244  return true;
245  }
246 
252  protected function getTemplateFile($partname)
253  {
254  $prefix = $this->outputFormat . "-";
255  $postfix = ".twig";
256  switch ($this->outputFormat) {
257  case "spdx2":
258  $postfix = ".xml" . $postfix;
259  break;
260  case "spdx2csv":
261  case "spdx2tv":
262  break;
263  case "dep5":
264  $prefix .= "copyright-";
265  break;
266  case "spdx3jsonld":
267  break;
268  case "spdx3rdf":
269  $postfix = ".xml" . $postfix;
270  break;
271  case "spdx3json":
272  break;
273  case "spdx3tv":
274  break;
275  }
276  return $prefix . $partname . $postfix;
277  }
278 
286  protected function getFileBasename($packageName)
287  {
288  if ($this->filebasename == null) {
289  $fileName = strtoupper($this->outputFormat)."_".$packageName;
290  switch ($this->outputFormat) {
291  case "spdx2":
292  $fileName .= ".spdx.rdf";
293  break;
294  case "spdx2tv":
295  $fileName .= ".spdx";
296  break;
297  case "spdx2csv":
298  $fileName .= ".csv";
299  break;
300  case "dep5":
301  $fileName .= ".txt";
302  break;
303  case "spdx3jsonld":
304  $fileName .= ".jsonld";
305  break;
306  case "spdx3json":
307  $fileName .= ".json";
308  break;
309  case "spdx3rdf":
310  $fileName .= ".spdx.rdf";
311  break;
312  case "spdx3tv":
313  $fileName .= ".spdx";
314  break;
315  }
316  $this->filebasename = $fileName;
317  }
318  return $this->filebasename;
319  }
320 
326  protected function getFileName($packageName)
327  {
328  global $SysConf;
329  $fileBase = $SysConf['FOSSOLOGY']['path']."/report/";
330  return $fileBase. $this->getFileBasename($packageName);
331  }
332 
338  protected function getUri($packageName)
339  {
340  global $SysConf;
341  $url=$SysConf['SYSCONFIG']['FOSSologyURL'];
342  if (substr( $url, 0, 4 ) !== "http") {
343  $url="http://".$url;
344  }
345 
346  return rtrim($url, '/') . '/' . $this->getFileBasename($packageName);
347  }
348 
354  protected function renderPackage($uploadId)
355  {
356  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
357  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
358  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId,$uploadTreeTableName);
359  $this->heartbeat(0);
360 
361  $filesWithLicenses = $this->reportutils
362  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
363  $this, $this->licensesInDocument);
364  $this->heartbeat(0);
365 
366  $this->reportutils->addClearingStatus($filesWithLicenses,$itemTreeBounds, $this->groupId);
367  $this->heartbeat(0);
368 
369  $scannerIDs = $this->reportutils->addScannerResults($filesWithLicenses, $itemTreeBounds, $this->groupId, $this->licensesInDocument);
370  $licenseComment = "";
371  if (!empty($scannerIDs)) {
372  $licenseComment = $this->getLicenseComment($scannerIDs);
373  }
374  $this->heartbeat(0);
375 
376  $this->reportutils->addCopyrightResults($filesWithLicenses, $uploadId);
377  $this->heartbeat(0);
378 
379  $upload = $this->uploadDao->getUpload($uploadId);
380  $fileNodes = $this->generateFileNodes($filesWithLicenses, $upload->getTreeTableName(), $uploadId);
381 
382  $mainLicenseIds = $this->clearingDao->getMainLicenseIds($uploadId, $this->groupId);
383  $customMainLicenseTexts = $this->clearingDao->getMainLicenseReportInfos($uploadId, $this->groupId);
384  $mainLicenses = array();
385  foreach ($mainLicenseIds as $licId) {
386  $reportedLicenseId = $this->licenseMap->getProjectedId($licId);
387  $mainLicense = $this->licenseDao->getLicenseById($reportedLicenseId, $this->groupId);
388  if ($mainLicense === null) {
389  error_log(
390  "spdx: Warning: main license ID {$reportedLicenseId} not found; skipping."
391  );
392  continue; // Skip this license and continue with the next one
393  }
394  $customText = $customMainLicenseTexts[$licId] ?? null;
395  if (!empty($customText)) {
396  $reportLicId = $mainLicense->getId() . "-" . md5($customText);
397  $prefix = $stateOsselot ? LicenseRef::SPDXREF_PREFIX : LicenseRef::SPDXREF_PREFIX_FOSSOLOGY;
398  $customShortName = $prefix . $mainLicense->getShortName() . "-" . md5($customText);
399  $customLicense = new License(
400  $mainLicense->getId(),
401  $customShortName,
402  $mainLicense->getFullName(),
403  $mainLicense->getRisk(),
404  $customText,
405  $mainLicense->getUrl(),
406  $mainLicense->getDetectorType(),
407  ''
408  );
409  $this->licensesInDocument[$reportLicId] = (new SpdxLicenseInfo())
410  ->setLicenseObj($customLicense)
411  ->setCustomText(true)
412  ->setListedLicense(false);
413  } else {
414  $reportLicId = $mainLicense->getId() . "-" . md5($mainLicense->getText());
415  if (!array_key_exists($reportLicId, $this->licensesInDocument)) {
416  $listedLicense = stripos($mainLicense->getSpdxId(),
417  LicenseRef::SPDXREF_PREFIX) !== 0;
418  $this->licensesInDocument[$reportLicId] = (new SpdxLicenseInfo())
419  ->setLicenseObj($mainLicense)
420  ->setCustomText(false)
421  ->setListedLicense($listedLicense);
422  }
423  }
424  $mainLicenses[] = $reportLicId;
425  }
426  $mainLicenseString = [];
427  if ($this->outputFormat == "spdx2tv" ||
428  $this->outputFormat == "spdx2csv" ||
429  $this->outputFormat == "spdx3tv") {
430  foreach ($mainLicenses as $mainLicense) {
431  $shortName = $this->licensesInDocument[$mainLicense]
432  ->getLicenseObj()->getShortName();
433  if (StringOperation::stringStartsWith($shortName,
434  LicenseRef::SPDXREF_PREFIX)) {
435  $mainLicenseString[] = $shortName;
436  } else {
437  $mainLicenseString[] = $this->licensesInDocument[$mainLicense]
438  ->getLicenseObj()->getSpdxId();
439  }
440  }
441  if ($this->outputFormat == "spdx2tv") {
442  if ($stateOsselot) {
443  $mainLicenseString = SpdxUtils::removeEmptyLicenses($mainLicenseString);
444  foreach ($mainLicenseString as $i => $licId) {
445  if (StringOperation::stringStartsWith($licId, LicenseRef::SPDXREF_PREFIX)
446  || StringOperation::stringStartsWith($licId, "Dual-license")) {
447  continue;
448  }
449  $mainLicenseString[$i] = "LicenseRef-$licId";
450  }
451  }
452  }
453  $mainLicenseString = SpdxUtils::implodeLicenses(
454  SpdxUtils::removeEmptyLicenses($mainLicenseString));
455  }
456 
457  $hashes = $this->uploadDao->getUploadHashes($uploadId);
458 
459  $reportInfo = $this->uploadDao->getReportInfo($uploadId);
460  $componentId = $reportInfo['ri_component_id'];
461  $componentType = $reportInfo['ri_component_type'];
462  $componentVersion = $reportInfo['ri_version'];
463  $generalAssessment = $reportInfo['ri_general_assesment'];
464  $releaseDate = $reportInfo['ri_release_date'];
465  if ($componentId == "NA") {
466  $componentId = "";
467  }
468  if ($componentVersion == "NA") {
469  $componentVersion = "";
470  }
471  if ($generalAssessment == "NA") {
472  $generalAssessment = "";
473  }
474  if ($releaseDate == "NA") {
475  $releaseDate = "";
476  } else {
477  $timeStamp = strtotime($releaseDate);
478  if ($timeStamp != -1) {
479  $releaseDate = date("Y-m-d\\T00:00:00\\Z", $timeStamp);
480  } else {
481  $releaseDate = "";
482  }
483  }
484  if ($componentType == ComponentType::MAVEN) {
485  $componentType = "maven-central";
486  } elseif ($componentType == ComponentType::PACKAGEURL) {
487  $componentType = "purl";
488  } else {
489  if (!empty($componentType)) {
490  $componentType = ComponentType::TYPE_MAP[$componentType];
491  } else {
493  }
494  }
495  $obligations = $this->getObligations($uploadId, $this->groupId);
496 
497  return $this->renderString($this->getTemplateFile('package'), [
498  'packageId' => $uploadId,
499  'uri' => $this->uri,
500  'packageName' => $upload->getFilename(),
501  'packageVersion' => $componentVersion,
502  'releaseDate' => $releaseDate,
503  'generalAssessment' => $generalAssessment,
504  'uploadName' => $upload->getFilename(),
505  'componentType' => $componentType,
506  'componentId' => htmlspecialchars($componentId),
507  'sha1' => $hashes['sha1'],
508  'md5' => $hashes['md5'],
509  'sha256' => $hashes['sha256'],
510  'verificationCode' => $this->getVerificationCode($upload),
511  'mainLicenses' => $mainLicenses,
512  'mainLicenseString' => $mainLicenseString,
513  'licenseComments' => $licenseComment,
514  'fileNodes' => $fileNodes,
515  'obligations' => $obligations,
516  'licenseList' => $this->licensesInDocument,
517  'EnableOsselotExport' => $stateOsselot
518  ]);
519  }
520 
525  protected function getLicenseComment($scannerIds)
526  {
528  $func = function($scannerId) use ($agentDao)
529  {
530  return $agentDao->getAgentName($scannerId)." (".$agentDao->getAgentRev($scannerId).")";
531  };
532  $scannerNames = array_map($func, $scannerIds);
533  return "licenseInfoInFile determined by Scanners:\n - ".implode("\n - ",$scannerNames);
534  }
535 
544  protected function toLicensesWithFilesAdder(&$filesWithLicenses, $licenses, $copyrights, $file, $fullPath)
545  {
546  if (!array_key_exists($licenses, $filesWithLicenses)) {
547  $filesWithLicenses[$licenses]['files']=array();
548  $filesWithLicenses[$licenses]['copyrights']=array();
549  }
550  if (empty($copyrights)) {
551  $copyrights = array();
552  }
553  $filesWithLicenses[$licenses]['files'][$file] = $fullPath;
554  foreach ($copyrights as $copyright) {
555  if (!in_array($copyright, $filesWithLicenses[$licenses]['copyrights'])) {
556  $filesWithLicenses[$licenses]['copyrights'][] = $copyright;
557  }
558  }
559  }
560 
567  protected function toLicensesWithFiles(&$filesWithLicenses, $treeTableName)
568  {
569  $licensesWithFiles = array();
570  $treeDao = $this->container->get('dao.tree');
571  $filesProceeded = 0;
572  foreach ($filesWithLicenses as $fileId => $fileNode) {
573  $filesProceeded += 1;
574  if (($filesProceeded&2047)==0) {
575  $this->heartbeat(0);
576  }
577  $fullPath = $treeDao->getFullPath($fileId, $treeTableName, 0);
578  if (! empty($fileNode->getConcludedLicenses())) {
579  $licenses = [];
580  foreach ($fileNode->getConcludedLicenses() as $license) {
581  $licenses[] = $this->licensesInDocument[$license]
582  ->getLicenseObj()->getSpdxId();
583  }
584  $licenses = SpdxUtils::implodeLicenses(
585  SpdxUtils::removeEmptyLicenses(array_unique($licenses)));
586  $this->toLicensesWithFilesAdder($licensesWithFiles,
587  $licenses, $fileNode->getCopyrights(), $fileId, $fullPath);
588  } else {
589  if (! empty($fileNode->getScanners())) {
590  $implodedLicenses = [];
591  foreach ($fileNode->getScanners() as $license) {
592  $implodedLicenses[] = $this->licensesInDocument[$license]
593  ->getLicenseObj()->getSpdxId();
594  }
595  $implodedLicenses = SpdxUtils::implodeLicenses(
596  SpdxUtils::removeEmptyLicenses(array_unique($implodedLicenses)));
597  if ($fileNode->isCleared()) {
598  $msgLicense = "None (scanners found: " . $implodedLicenses . ")";
599  } else {
600  $msgLicense = "NoLicenseConcluded (scanners found: " . $implodedLicenses . ")";
601  }
602  } else {
603  if ($fileNode->isCleared()) {
604  $msgLicense = "None";
605  } else {
606  $msgLicense = "NoLicenseConcluded";
607  }
608  }
609  $this->toLicensesWithFilesAdder($licensesWithFiles, $msgLicense,
610  $fileNode->getCopyrights(), $fileId, $fullPath);
611  }
612  }
613  return $licensesWithFiles;
614  }
615 
620  protected function computeUri($uploadId)
621  {
622  $upload = $this->uploadDao->getUpload($uploadId);
623  $packageName = $upload->getFilename();
624 
625  $this->uri = $this->getUri($packageName);
626  $this->filename = $this->getFileName($packageName);
627  }
628 
635  protected function writeReport(&$packageNodes, $packageIds, $uploadId)
636  {
637  global $SysConf;
638  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
639  $fileBase = dirname($this->filename);
640 
641  if (!is_dir($fileBase)) {
642  mkdir($fileBase, 0777, true);
643  }
644  umask(0133);
645 
646  $organizationName = $SysConf['SYSCONFIG']["ReportHeaderText"] ?? 'FOSSology';
647  $version = $SysConf['BUILD']['VERSION'];
648 
649  $uploadTreeTableName = $this->uploadDao->getUploadtreeTableName($uploadId);
650  $itemTreeBounds = $this->uploadDao->getParentItemBounds($uploadId,$uploadTreeTableName);
651  $this->heartbeat(0);
652 
653  $filesWithLicenses = $this->reportutils
654  ->getFilesWithLicensesFromClearings($itemTreeBounds, $this->groupId,
655  $this, $this->licensesInDocument);
656  $this->heartbeat(0);
657  $upload = $this->uploadDao->getUpload($uploadId);
658  $this->generateFileNodes($filesWithLicenses, $upload->getTreeTableName(), $uploadId);
659  $this->declaredLicenseFileIds = array_unique(array_diff($this->declaredLicenseFileIds, $this->concludedLicenseFileIds));
660  $this->concludedLicenseFileIds = array_unique($this->concludedLicenseFileIds);
661  $this->validateLicenseIdentifiers();
662  $message = $this->renderString($this->getTemplateFile('document'),array(
663  'documentName' => $fileBase,
664  'uri' => $this->uri,
665  'userName' => $this->container->get('dao.user')->getUserName($this->userId) . " (" . $this->container->get('dao.user')->getUserEmail($this->userId) . ")",
666  'organisation' => $organizationName,
667  'concludedLicenseFileIds'=>$this->concludedLicenseFileIds,
668  'declaredLicenseFileIds'=>$this->declaredLicenseFileIds,
669  'toolVersion' => 'fossology-' . $version,
670  'packageNodes' => $packageNodes,
671  'packageIds' => $packageIds,
672  'dataLicense' => $this->getSPDXDataLicense(),
673  'licenseList' => $this->licensesInDocument,
674  'EnableOsselotExport' => $stateOsselot,
675  )
676  );
677 
678  // To ensure the file is valid, replace any non-printable characters with a question mark.
679  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F (delete).
680  $message = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/','?',$message);
681 
682  file_put_contents($this->filename, $message);
683  $this->updateReportTable($uploadId, $this->jobId, $this->filename);
684  }
685 
692  protected function updateReportTable($uploadId, $jobId, $fileName)
693  {
694  $this->reportutils->updateOrInsertReportgenEntry($uploadId, $jobId, $fileName);
695  }
696 
697  protected function validateLicenseIdentifiers()
698  {
699  $issues = [];
700  foreach ($this->licensesInDocument as $licenseInfo) {
701  $license = $licenseInfo->getLicenseObj();
702  $spdxId = $license->getSpdxId();
703  $shortName = $license->getShortName();
704 
705  if (!SpdxLicenseValidator::isLicenseRef($spdxId)) {
706  continue;
707  }
708  if (preg_match('/[^a-zA-Z0-9.\-\s]/', $shortName)) {
709  $idstring = SpdxLicenseValidator::extractIdstring($spdxId);
710  $fossologyPrefix = substr(LicenseRef::SPDXREF_PREFIX_FOSSOLOGY,
711  strlen(LicenseRef::SPDXREF_PREFIX));
712  if (strpos($idstring, $fossologyPrefix) === 0) {
713  $idstring = substr($idstring, strlen($fossologyPrefix));
714  }
715  $issues[] = sprintf(" \"%s\" -> sanitized to \"%s\"",
716  $shortName, $idstring);
717  }
718  }
719 
720  if (!empty($issues)) {
721  error_log("spdx: License shortnames with invalid SPDX characters were sanitized:\n"
722  . implode("\n", $issues));
723  }
724  }
725 
732  protected function renderString($templateName, $vars)
733  {
734  return $this->renderer->load($templateName)->render($vars);
735  }
736 
744  protected function generateFileNodes($filesWithLicenses, $treeTableName, $uploadId)
745  {
746  $this->deduplicateLicenseList();
747  if (strcmp($this->outputFormat, "dep5") !== 0) {
748  return $this->generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId);
749  } else {
750  return $this->generateFileNodesByLicenses($filesWithLicenses, $treeTableName);
751  }
752  }
753 
761  protected function generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId)
762  {
763  /* @var $treeDao TreeDao */
764  $treeDao = $this->container->get('dao.tree');
765  $filesProceeded = 0;
766  $lastValue = 0;
767  $content = '';
768  $textToBePrinted = [];
769  foreach ($filesWithLicenses as $fileId => $fileData) {
770  $filesProceeded += 1;
771  if (($filesProceeded & 2047) == 0) {
772  $this->heartbeat($filesProceeded - $lastValue);
773  $lastValue = $filesProceeded;
774  }
775  $hashes = $treeDao->getItemHashes($fileId);
776  $fileName = $treeDao->getFullPath($fileId, $treeTableName, 0);
777  $stateComment = $this->getSPDXReportConf($uploadId, 0);
778  $stateWoInfos = $this->getSPDXReportConf($uploadId, 1);
779  $stateOsselot = $this->getSPDXReportConf($uploadId, 2);
780  foreach ($fileData->getConcludedLicenses() as $license) {
781  $this->concludedLicenseFileIds[] = $fileId;
782  if (! $this->licensesInDocument[$license]->isTextPrinted()) {
783  $textToBePrinted[] = $license;
784  }
785  }
786  foreach ($fileData->getScanners() as $license) {
787  $this->declaredLicenseFileIds[] = $fileId;
788  if (! $this->licensesInDocument[$license]->isTextPrinted()) {
789  $textToBePrinted[] = $license;
790  }
791  }
792  $concludedLicensesString = [];
793  if ($this->outputFormat == "spdx2tv" ||
794  $this->outputFormat == "spdx2csv" ||
795  $this->outputFormat == "spdx3tv") {
796  foreach ($fileData->getConcludedLicenses() as $license) {
797  $shortName = $this->licensesInDocument[$license]
798  ->getLicenseObj()->getShortName();
799  if (StringOperation::stringStartsWith($shortName,
800  LicenseRef::SPDXREF_PREFIX)) {
801  $concludedLicensesString[] = $shortName;
802  } else {
803  $concludedLicensesString[] = $this->licensesInDocument[$license]
804  ->getLicenseObj()->getSpdxId();
805  }
806  }
807  if ($this->outputFormat == "spdx2tv") {
808  if ($stateOsselot) {
809  foreach ($concludedLicensesString as $j => $licId) {
810  if (StringOperation::stringStartsWith($licId, LicenseRef::SPDXREF_PREFIX)
811  || StringOperation::stringStartsWith($licId, "Dual-license")) {
812  continue;
813  }
814  $concludedLicensesString[$j] = "LicenseRef-$licId";
815  }
816  }
817  $concludedLicensesString = SpdxUtils::implodeLicenses(
818  SpdxUtils::removeEmptyLicenses($concludedLicensesString)
819  );
820  } else {
821  $concludedLicensesString = SpdxUtils::implodeLicenses(
822  SpdxUtils::removeEmptyLicenses($concludedLicensesString));
823  }
824  }
825  if (!$stateWoInfos ||
826  ($stateWoInfos && (!empty($fileData->getConcludedLicenses()) ||
827  !empty($fileData->getScanners()) || !empty($fileData->getCopyrights())))) {
828  $fileData->setAcknowledgements(
829  SpdxUtils::cleanTextArray($fileData->getAcknowledgements()));
830  $fileData->setComments(
831  SpdxUtils::cleanTextArray($fileData->getComments()));
832  $dataTemplate = array(
833  'fileId' => $fileId,
834  'sha1' => $hashes['sha1'],
835  'md5' => $hashes['md5'],
836  'sha256' => $hashes['sha256'],
837  'uri' => $this->uri,
838  'fileName' => $fileName,
839  'fileDirName' => dirname($fileName),
840  'fileBaseName' => basename($fileName),
841  'fileData' => $fileData,
842  'licenseList' => $this->licensesInDocument,
843  'concludedLicensesString' => $concludedLicensesString,
844  'licenseCommentState' => $stateComment,
845  'EnableOsselotExport' =>$stateOsselot,
846  );
847  $content .= $this->renderString($this->getTemplateFile('file'),
848  $dataTemplate);
849  }
850  foreach ($textToBePrinted as $license) {
851  $this->licensesInDocument[$license]->setTextPrinted(true);
852  }
853  }
854  $this->heartbeat($filesProceeded - $lastValue);
855  return $content;
856  }
857 
864  protected function generateFileNodesByLicenses($filesWithLicenses, $treeTableName)
865  {
866  $licensesWithFiles = $this->toLicensesWithFiles($filesWithLicenses, $treeTableName);
867 
868  $content = '';
869  $filesProceeded = 0;
870  $lastStep = 0;
871  $lastValue = 0;
872  foreach ($licensesWithFiles as $licenseId=>$entry) {
873  $filesProceeded += count($entry['files']);
874  if ($filesProceeded&(~2047) > $lastStep) {
875  $this->heartbeat($filesProceeded - $lastValue);
876  $lastStep = $filesProceeded&(~2047) + 2048;
877  $lastValue = $filesProceeded;
878  }
879 
880  $comment = "";
881  if (strrpos($licenseId, "NoLicenseConcluded (scanners found: ", -strlen($licenseId)) !== false) {
882  $comment = substr($licenseId,20,strlen($licenseId)-21);
883  $licenseId = "NoLicenseConcluded";
884  } elseif (strrpos($licenseId, "None (scanners found: ", -strlen($licenseId)) !== false) {
885  $comment = substr($licenseId,6,strlen($licenseId)-7);
886  $licenseId = "None";
887  }
888 
889  $content .= $this->renderString($this->getTemplateFile('file'),array(
890  'fileNames'=>$entry['files'],
891  'license'=>$licenseId,
892  'copyrights'=>$entry['copyrights'],
893  'comment'=>$comment));
894  }
895  $this->heartbeat($filesProceeded - $lastValue);
896  return $content;
897  }
898 
907  protected function getVerificationCode(Upload $upload)
908  {
909  $stmt = __METHOD__;
910  $param = array();
911  if ($upload->getTreeTableName()=='uploadtree_a') {
912  $sql = $upload->getTreeTableName().' WHERE upload_fk=$1 AND';
913  $param[] = $upload->getId();
914  } else {
915  $sql = $upload->getTreeTableName().' WHERE';
916  $stmt .= '.'.$upload->getTreeTableName();
917  }
918 
919  $sql = "SELECT STRING_AGG(lower_sha1,'') concat_sha1 FROM
920  (SELECT LOWER(pfile_sha1) lower_sha1 FROM pfile, $sql pfile_fk=pfile_pk AND parent IS NOT NULL ORDER BY pfile_sha1) templist";
921  $filelistPack = $this->dbManager->getSingleRow($sql,$param,$stmt);
922 
923  return sha1($filelistPack['concat_sha1']);
924  }
925 
933  protected function getSPDXReportConf($uploadId, $key)
934  {
935  $sql = "SELECT ri_spdx_selection FROM report_info WHERE upload_fk = $1";
936  $getCommentState = $this->dbManager->getSingleRow($sql, array($uploadId), __METHOD__.'.SPDX_license_comment');
937  if (!empty($getCommentState['ri_spdx_selection'])) {
938  $getCommentStateSingle = explode(',', $getCommentState['ri_spdx_selection']);
939  if ($getCommentStateSingle[$key] === "checked") {
940  return true;
941  }
942  }
943  return false;
944  }
951  private function getObligations(int $uploadId, int $groupId): array
952  {
953  $licenses = $this->licenseClearedGetter->getCleared($uploadId, $this,
954  $groupId, true, "license", false);
955  $this->heartbeat(0);
956  $licensesMain = $this->licenseMainGetter->getCleared($uploadId, $this,
957  $groupId, true, null, false);
958  $this->heartbeat(0);
959  list($obligations, $_) = $this->obligationsGetter->getObligations(
960  $licenses['statements'], $licensesMain['statements'], $uploadId,
961  $groupId);
962  if (empty($obligations)) {
963  return [];
964  } else {
965  return array_column($obligations, "text");
966  }
967  }
968 
973  protected function getSPDXDataLicense()
974  {
975  $dataLic = $this->licenseDao->getLicenseByShortName(self::DATA_LICENSE);
976  return $dataLic->getId() . "-" . md5($dataLic->getText());
977  }
978 
990  private function deduplicateLicenseList()
991  {
992  $localList = array_values($this->licensesInDocument);
993  usort($localList,
994  function(SpdxLicenseInfo $a, SpdxLicenseInfo $b) {
995  return strcmp(
996  $a->getLicenseObj()->getSpdxId() . $a->getLicenseObj()->getShortName(),
997  $b->getLicenseObj()->getSpdxId() . $b->getLicenseObj()->getShortName());
998  }
999  );
1000  for ($i = 0; $i < count($localList) - 1; $i++) {
1001  if ((! $localList[$i]->isCustomText() && ! $localList[$i + 1]->isCustomText()) &&
1002  $localList[$i]->getLicenseObj()->getSpdxId() ===
1003  $localList[$i + 1]->getLicenseObj()->getSpdxId()) {
1004  $newShortName = $localList[$i + 1]->getLicenseObj()->getShortName();
1006  $localList[$i + 1]->getLicenseObj()->getShortName(),
1007  LicenseRef::SPDXREF_PREFIX)) {
1008  $newShortName = LicenseRef::SPDXREF_PREFIX .
1009  $localList[$i + 1]->getLicenseObj()->getShortName();
1010  $newShortName = preg_replace('/\+$/', '-or-later', $newShortName);
1011  }
1012  $md5 = md5($localList[$i + 1]->getLicenseObj()->getText());
1013  $reportedLicenseShortname = "$newShortName-$md5";
1014  $licIndex = $localList[$i + 1]->getLicenseObj()->getId() . "-$md5";
1015  $oldLicObj = $this->licensesInDocument[$licIndex]->getLicenseObj();
1016  $this->licensesInDocument[$licIndex]->setLicenseObj(
1017  new License($oldLicObj->getId(), $reportedLicenseShortname,
1018  $oldLicObj->getFullName(), $oldLicObj->getRisk(),
1019  $oldLicObj->getText(), $oldLicObj->getUrl(),
1020  $oldLicObj->getDetectorType(), $oldLicObj->getSpdxId()));
1021  }
1022  }
1023  }
1024 }
1025 
1026 $agent = new SpdxAgent();
1027 $agent->scheduler_connect();
1028 $agent->run_scheduler_event_loop();
1029 $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:525
getFileName($packageName)
Get absolute path for report.
Definition: spdx.php:326
getVerificationCode(Upload $upload)
Get a unique identifier for a given upload.
Definition: spdx.php:907
getTemplateFile($partname)
Get TWIG template file based on output format.
Definition: spdx.php:252
const UPLOAD_ADDS
Argument for additional uploads.
Definition: spdx.php:83
getObligations(int $uploadId, int $groupId)
Definition: spdx.php:951
processUploadId($uploadId)
Given an upload ID, process the items in it.
Definition: spdx.php:217
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:692
preWorkOnArgs($args)
Parse arguments.
Definition: spdx.php:199
renderString($templateName, $vars)
Render a twig template.
Definition: spdx.php:732
const AVAILABLE_OUTPUT_FORMATS
Output formats available.
Definition: spdx.php:82
renderPackage($uploadId)
Given an upload id, render the report string.
Definition: spdx.php:354
getFileBasename($packageName)
Generate report basename based on upload name.
Definition: spdx.php:286
generateFileNodesByLicenses($filesWithLicenses, $treeTableName)
For each file, generate the nodes by licenses.
Definition: spdx.php:864
toLicensesWithFiles(&$filesWithLicenses, $treeTableName)
Map findings to the files.
Definition: spdx.php:567
generateFileNodes($filesWithLicenses, $treeTableName, $uploadId)
Generate report nodes for files.
Definition: spdx.php:744
getSPDXReportConf($uploadId, $key)
Get spdx license comment state for a given upload.
Definition: spdx.php:933
toLicensesWithFilesAdder(&$filesWithLicenses, $licenses, $copyrights, $file, $fullPath)
Map licenses, copyrights, files and full path to filesWithLicenses array.
Definition: spdx.php:544
generateFileNodesByFiles($filesWithLicenses, $treeTableName, $uploadId)
For each file, generate the nodes by files.
Definition: spdx.php:761
writeReport(&$packageNodes, $packageIds, $uploadId)
Write the report the file and update report table.
Definition: spdx.php:635
computeUri($uploadId)
For a given upload, compute the URI and filename for the report.
Definition: spdx.php:620
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:338
deduplicateLicenseList()
De-duplicate license list by comparing licenses with the same SPDX ID.
Definition: spdx.php:990
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.