FOSSology  4.4.0
Open Source License Compliance by Open Source Software
clixml.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2021 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 namespace Fossology\CliXml;
8 
22 use Twig\Environment;
23 
24 include_once(__DIR__ . "/version.php");
25 include_once(__DIR__ . "/services.php");
26 
27 class CliXml extends Agent
28 {
29 
30  const OUTPUT_FORMAT_KEY = "outputFormat";
31  const DEFAULT_OUTPUT_FORMAT = "clixml";
32  const AVAILABLE_OUTPUT_FORMATS = "xml";
33  const UPLOAD_ADDS = "uploadsAdd";
34 
36  private $uploadDao;
37 
39  protected $dbManager;
40 
42  protected $licenseDao;
43 
45  protected $renderer;
46 
48  protected $uri;
49 
51  protected $packageName;
52 
57 
62 
71 
75  private $reportutils;
76 
78  protected $outputFormat = self::DEFAULT_OUTPUT_FORMAT;
79 
80  function __construct()
81  {
82  parent::__construct('clixml', AGENT_VERSION, AGENT_REV);
83 
84  $this->uploadDao = $this->container->get('dao.upload');
85  $this->dbManager = $this->container->get('db.manager');
86  $this->licenseDao = $this->container->get('dao.license');
87  $this->renderer = $this->container->get('twig.environment');
88  $this->renderer->setCache(false);
89 
90  $this->cpClearedGetter = new XpClearedGetter("copyright", "statement");
91  $this->eccClearedGetter = new XpClearedGetter("ecc", "ecc");
92  $this->ipraClearedGetter = new XpClearedGetter("ipra", "ipra");
93  $this->licenseIrrelevantGetter = new LicenseIrrelevantGetter();
94  $this->licenseIrrelevantGetterComments = new LicenseIrrelevantGetter(false);
95  $this->licenseDNUGetter = new LicenseDNUGetter();
96  $this->licenseDNUCommentGetter = new LicenseDNUGetter(false);
97  $this->licenseClearedGetter = new LicenseClearedGetter();
98  $this->licenseMainGetter = new LicenseMainGetter();
99  $this->obligationsGetter = new ObligationsGetter();
100  $this->otherGetter = new OtherGetter();
101  $this->reportutils = new ReportUtils();
102  $this->agentSpecifLongOptions[] = self::UPLOAD_ADDS.':';
103  $this->agentSpecifLongOptions[] = self::OUTPUT_FORMAT_KEY.':';
104  }
105 
113  protected function preWorkOnArgsFlp($args,$key1,$key2)
114  {
115  $needle = ' --'.$key2.'=';
116  if (array_key_exists($key1,$args) && strpos($args[$key1],$needle) !== false) {
117  $exploded = explode($needle,$args[$key1]);
118  $args[$key1] = trim($exploded[0]);
119  $args[$key2] = trim($exploded[1]);
120  }
121  return $args;
122  }
123 
129  protected function preWorkOnArgs($args)
130  {
131  if ((!array_key_exists(self::OUTPUT_FORMAT_KEY,$args)
132  || $args[self::OUTPUT_FORMAT_KEY] === "")
133  && array_key_exists(self::UPLOAD_ADDS,$args)) {
134 
135  $args = $this->preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
136  } else {
137  if (!array_key_exists(self::UPLOAD_ADDS,$args) || $args[self::UPLOAD_ADDS] === "") {
138  $args = $this->preWorkOnArgsFlp($args,self::UPLOAD_ADDS,self::OUTPUT_FORMAT_KEY);
139  }
140  }
141  return $args;
142  }
143 
144  function processUploadId($uploadId)
145  {
147 
148  $args = $this->preWorkOnArgs($this->args);
149 
150  if (array_key_exists(self::OUTPUT_FORMAT_KEY,$args)) {
151  $possibleOutputFormat = trim($args[self::OUTPUT_FORMAT_KEY]);
152  if (in_array($possibleOutputFormat, explode(',',self::AVAILABLE_OUTPUT_FORMATS))) {
153  $this->outputFormat = $possibleOutputFormat;
154  }
155  }
156  $this->computeUri($uploadId);
157 
158  $contents = $this->renderPackage($uploadId, $groupId);
159 
160  $additionalUploadIds = array_key_exists(self::UPLOAD_ADDS,$args) ? explode(',',$args[self::UPLOAD_ADDS]) : array();
161  $packageIds = array($uploadId);
162  foreach ($additionalUploadIds as $additionalId) {
163  $contents .= $this->renderPackage($additionalId, $groupId);
164  $packageIds[] = $additionalId;
165  }
166 
167  $this->writeReport($contents, $packageIds, $uploadId);
168  return true;
169  }
170 
171  protected function getTemplateFile($partname)
172  {
173  $prefix = $this->outputFormat . "-";
174  $postfix = ".twig";
175  $postfix = ".xml" . $postfix;
176  return $prefix . $partname . $postfix;
177  }
178 
179  protected function getUri($fileBase)
180  {
181  $fileName = $fileBase. strtoupper($this->outputFormat)."_".$this->packageName;
182  return $fileName .".xml";
183  }
184 
185  protected function renderPackage($uploadId, $groupId)
186  {
187  $this->heartbeat(0);
188 
189  $otherStatement = $this->otherGetter->getReportData($uploadId);
190  $this->heartbeat(empty($otherStatement) ? 0 : count($otherStatement));
191 
192  if (!empty($otherStatement['ri_clixmlcolumns'])) {
193  $clixmlColumns = json_decode($otherStatement['ri_clixmlcolumns'], true);
194  } else {
195  $clixmlColumns = UploadDao::CLIXML_REPORT_HEADINGS;
196  }
197 
198  $licenses = $this->licenseClearedGetter->getCleared($uploadId, $this, $groupId, true, "license", false);
199  $this->heartbeat(empty($licenses) ? 0 : count($licenses["statements"]));
200 
201  $licensesMain = $this->licenseMainGetter->getCleared($uploadId, $this, $groupId, true, null, false);
202  $this->heartbeat(empty($licensesMain) ? 0 : count($licensesMain["statements"]));
203 
204  if (array_values($clixmlColumns['irrelevantfilesclixml'])[0]) {
205  $licensesIrre = $this->licenseIrrelevantGetter->getCleared($uploadId, $this, $groupId, true, null, false);
206  $irreComments = $this->licenseIrrelevantGetterComments->getCleared($uploadId, $this, $groupId, true, null, false);
207  } else {
208  $licensesIrre = array("statements" => array());
209  $irreComments = array("statements" => array());
210  }
211  $this->heartbeat(empty($licensesIrre) ? 0 : count($licensesIrre["statements"]));
212  $this->heartbeat(empty($irreComments) ? 0 : count($irreComments["statements"]));
213 
214  if (array_values($clixmlColumns['dnufilesclixml'])[0]) {
215  $licensesDNU = $this->licenseDNUGetter->getCleared($uploadId, $this, $groupId, true, null, false);
216  $licensesDNUComment = $this->licenseDNUCommentGetter->getCleared($uploadId, $this, $groupId, true, null, false);
217  } else {
218  $licensesDNU = array("statements" => array());
219  $licensesDNUComment = array("statements" => array());
220  }
221  $this->heartbeat(empty($licensesDNU) ? 0 : count($licensesDNU["statements"]));
222  $this->heartbeat(empty($licensesDNUComment) ? 0 : count($licensesDNUComment["statements"]));
223 
224  if (array_values($clixmlColumns['copyrightsclixml'])[0]) {
225  $copyrights = $this->cpClearedGetter->getCleared($uploadId, $this, $groupId, true, "copyright", false);
226  } else {
227  $copyrights = array("statements" => array());
228  }
229  $this->heartbeat(empty($copyrights["statements"]) ? 0 : count($copyrights["statements"]));
230 
231  if (array_values($clixmlColumns['exportrestrictionsclixml'])[0]) {
232  $ecc = $this->eccClearedGetter->getCleared($uploadId, $this, $groupId, true, "ecc", false);
233  } else {
234  $ecc = array("statements" => array());
235  }
236  $this->heartbeat(empty($ecc) ? 0 : count($ecc["statements"]));
237 
238  if (array_values($clixmlColumns['intellectualPropertyclixml'])[0]) {
239  $ipra = $this->ipraClearedGetter->getCleared($uploadId, $this, $groupId, true, "ipra", false);
240  } else {
241  $ipra = array("statements" => array());
242  }
243  $this->heartbeat(empty($ipra) ? 0 : count($ipra["statements"]));
244 
245  if (array_values($clixmlColumns['notesclixml'])[0]) {
246  $notes = htmlspecialchars($otherStatement['ri_ga_additional'], ENT_DISALLOWED);
247  } else {
248  $notes = "";
249  }
250 
251  $countAcknowledgement = 0;
252  $includeAcknowledgements = array_values($clixmlColumns['acknowledgementsclixml'])[0];
253  $licenses["statements"] = $this->addLicenseNames($licenses["statements"]);
254  $licensesWithAcknowledgement = $this->removeDuplicateAcknowledgements(
255  $licenses["statements"], $countAcknowledgement, $includeAcknowledgements);
256 
257  if (array_values($clixmlColumns['allobligations'])[0]) {
258  $obligations = $this->obligationsGetter->getObligations(
259  $licenses['statements'], $licensesMain['statements'], $uploadId, $groupId)[0];
260  $obligations = array_values($obligations);
261  } else {
262  $obligations = array();
263  }
264 
265  if (array_values($clixmlColumns['mainlicensesclixml'])[0]) {
266  $mainLicenses = $licensesMain["statements"];
267  } else {
268  $mainLicenses = array();
269  }
270  $componentHash = $this->uploadDao->getUploadHashes($uploadId);
271  $contents = array(
272  "licensesMain" => $mainLicenses,
273  "licenses" => $licensesWithAcknowledgement,
274  "obligations" => $obligations,
275  "copyrights" => $copyrights["statements"],
276  "ecc" => $ecc["statements"],
277  "ipra" => $ipra["statements"],
278  "licensesIrre" => $licensesIrre["statements"],
279  "irreComments" => $irreComments["statements"],
280  "licensesDNU" => $licensesDNU["statements"],
281  "licensesDNUComment" => $licensesDNUComment["statements"],
282  "countAcknowledgement" => $countAcknowledgement
283  );
284  $contents = $this->reArrangeMainLic($contents, $includeAcknowledgements);
285  $contents = $this->reArrangeContent($contents);
286  $fileOperations = array(
287  "licensepath" => array_values($clixmlColumns['licensepath'])[0],
288  "licensehash" => array_values($clixmlColumns['licensehash'])[0],
289  "copyrightpath" => array_values($clixmlColumns['copyrightpath'])[0],
290  "copyrighthash" => array_values($clixmlColumns['copyrighthash'])[0],
291  "eccpath" => array_values($clixmlColumns['eccpath'])[0],
292  "ecchash" => array_values($clixmlColumns['ecchash'])[0],
293  "iprapath" => array_values($clixmlColumns['iprapath'])[0],
294  "iprahash" => array_values($clixmlColumns['iprahash'])[0]
295  );
296  list($generalInformation, $assessmentSummary) = $this->getReportSummary($uploadId);
297  $generalInformation['componentHash'] = $componentHash['sha1'];
298  return $this->renderString($this->getTemplateFile('file'),array(
299  'documentName' => $this->packageName,
300  'version' => "1.6",
301  'uri' => $this->uri,
302  'userName' => $this->container->get('dao.user')->getUserName($this->userId),
303  'organisation' => '',
304  'componentHash' => strtolower($componentHash['sha1']),
305  'contents' => $contents,
306  'commentAdditionalNotes' => $notes,
307  'externalIdLink' => htmlspecialchars($otherStatement['ri_sw360_link']),
308  'generalInformation' => $generalInformation,
309  'assessmentSummary' => $assessmentSummary,
310  'fileOperations' => $fileOperations
311  ));
312  }
313 
314  protected function removeDuplicateAcknowledgements($licenses, &$countAcknowledgement, $includeAcknowledgements)
315  {
316  if (empty($licenses)) {
317  return $licenses;
318  }
319 
320  foreach ($licenses as $ackKey => $ackValue) {
321  if (!$includeAcknowledgements) {
322  $licenses[$ackKey]['acknowledgement'] = null;
323  } else if (isset($ackValue['acknowledgement'])) {
324  $licenses[$ackKey]['acknowledgement'] = array_unique(array_filter($ackValue['acknowledgement']));
325  $countAcknowledgement += count($licenses[$ackKey]['acknowledgement']);
326  }
327  }
328  return $licenses;
329  }
330 
331  protected function riskMapping($licenseContent)
332  {
333  foreach ($licenseContent as $riskKey => $riskValue) {
334  if (!array_key_exists('risk', $riskValue)) {
335  $riskValue['risk'] = 0;
336  }
337  if ($riskValue['risk'] == '2' || $riskValue['risk'] == '3') {
338  $licenseContent[$riskKey]['risk'] = 'otheryellow';
339  } else if ($riskValue['risk'] == '4' || $riskValue['risk'] == '5') {
340  $licenseContent[$riskKey]['risk'] = 'otherred';
341  } else {
342  $licenseContent[$riskKey]['risk'] = 'otherwhite';
343  }
344  }
345  return $licenseContent;
346  }
347 
348  protected function reArrangeMainLic($contents, $includeAcknowledgements)
349  {
350  $mainlic = array();
351  $lenTotalLics = count($contents["licenses"]);
352  // both of this variables have same value but used for different operations
353  $lenMainLics = count($contents["licensesMain"]);
354  for ($i=0; $i<$lenMainLics; $i++) {
355  $count = 0 ;
356  for ($j=0; $j<$lenTotalLics; $j++) {
357  if (!strcmp($contents["licenses"][$j]["content"], $contents["licensesMain"][$i]["content"])) {
358  $count = 1;
359  $mainlic[] = $contents["licenses"][$j];
360  unset($contents["licenses"][$j]);
361  }
362  }
363  if ($count != 1) {
364  $mainlic[] = $contents["licensesMain"][$i];
365  }
366  unset($contents["licensesMain"][$i]);
367  }
368  $contents["licensesMain"] = $mainlic;
369 
370  $lenMainLicenses=count($contents["licensesMain"]);
371  for ($i=0; $i<$lenMainLicenses; $i++) {
372  $contents["licensesMain"][$i]["contentMain"] = $contents["licensesMain"][$i]["content"];
373  $contents["licensesMain"][$i]["nameMain"] = $contents["licensesMain"][$i]["name"];
374  $contents["licensesMain"][$i]["textMain"] = $contents["licensesMain"][$i]["text"];
375  $contents["licensesMain"][$i]["riskMain"] = $contents["licensesMain"][$i]["risk"];
376  if (array_key_exists('acknowledgement', $contents["licensesMain"][$i])) {
377  if ($includeAcknowledgements) {
378  $contents["licensesMain"][$i]["acknowledgementMain"] = $contents["licensesMain"][$i]["acknowledgement"];
379  }
380  unset($contents["licensesMain"][$i]["acknowledgement"]);
381  }
382  unset($contents["licensesMain"][$i]["content"]);
383  unset($contents["licensesMain"][$i]["text"]);
384  unset($contents["licensesMain"][$i]["risk"]);
385  }
386  return $contents;
387  }
388 
389  protected function reArrangeContent($contents)
390  {
391  $contents['licensesMain'] = $this->riskMapping($contents['licensesMain']);
392  $contents['licenses'] = $this->riskMapping($contents['licenses']);
393 
394  $contents["obligations"] = array_map(function($changeKey) {
395  return array(
396  'obliText' => $changeKey['text'],
397  'topic' => $changeKey['topic'],
398  'license' => $changeKey['license']
399  );
400  }, $contents["obligations"]);
401 
402  $contents["copyrights"] = array_map(function($changeKey) {
403  $content = htmlspecialchars_decode($changeKey['content']);
404  $content = str_replace("]]>", "]]&gt;", $content);
405  $comments = htmlspecialchars_decode($changeKey['comments']);
406  $comments = str_replace("]]>", "]]&gt;", $comments);
407  return array(
408  'contentCopy' => $content,
409  'comments' => $comments,
410  'files' => $changeKey['files'],
411  'hash' => $changeKey['hash']
412  );
413  }, $contents["copyrights"]);
414 
415  $contents["ecc"] = array_map(function($changeKey) {
416  $content = htmlspecialchars_decode($changeKey['content']);
417  $content = str_replace("]]>", "]]&gt;", $content);
418  $comments = htmlspecialchars_decode($changeKey['comments']);
419  $comments = str_replace("]]>", "]]&gt;", $comments);
420  return array(
421  'contentEcc' => $content,
422  'commentsEcc' => $comments,
423  'files' => $changeKey['files'],
424  'hash' => $changeKey['hash']
425  );
426  }, $contents["ecc"]);
427 
428  $contents["ipra"] = array_map(function($changeKey) {
429  $content = htmlspecialchars_decode($changeKey['content']);
430  $content = str_replace("]]>", "]]&gt;", $content);
431  $comments = htmlspecialchars_decode($changeKey['comments']);
432  $comments = str_replace("]]>", "]]&gt;", $comments);
433  return array(
434  'contentIpra' => $content,
435  'commentsIpra' => $comments,
436  'files' => $changeKey['files'],
437  'hash' => $changeKey['hash']
438  );
439  }, $contents["ipra"]);
440 
441  $contents["irreComments"] = array_map(function($changeKey) {
442  return array(
443  'contentIrre' => $changeKey['content'],
444  'textIrre' => $changeKey['text']
445  );
446  }, $contents["irreComments"]);
447 
448  $contents["licensesIrre"] = array_map(function($changeKey) {
449  return array(
450  'filesIrre' => $changeKey['fullPath']
451  );
452  }, $contents["licensesIrre"]);
453 
454  $contents["licensesDNUComment"] = array_map(function($changeKey) {
455  return array(
456  'contentDNU' => $changeKey['content'],
457  'textDNU' => $changeKey['text']
458  );
459  }, $contents["licensesDNUComment"]);
460 
461  $contents["licensesDNU"] = array_map(function($changeKey) {
462  return array(
463  'filesDNU' => $changeKey['fullPath']
464  );
465  }, $contents["licensesDNU"]);
466 
467  return $contents;
468  }
469 
470  protected function computeUri($uploadId)
471  {
472  global $SysConf;
473  $upload = $this->uploadDao->getUpload($uploadId);
474  $this->packageName = $upload->getFilename();
475 
476  $fileBase = $SysConf['FOSSOLOGY']['path']."/report/";
477 
478  $this->uri = $this->getUri($fileBase);
479  }
480 
481  protected function writeReport($contents, $packageIds, $uploadId)
482  {
483  $fileBase = dirname($this->uri);
484 
485  if (!is_dir($fileBase)) {
486  mkdir($fileBase, 0777, true);
487  }
488  umask(0133);
489 
490  $message = $this->renderString($this->getTemplateFile('document'),
491  array('content' => $contents));
492 
493  // To ensure the file is valid, replace any non-printable characters with a question mark.
494  // 'Non-printable' is ASCII < 0x20 (excluding \r, \n and tab) and 0x7F - 0x9F.
495  $message = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u','?',$message);
496  file_put_contents($this->uri, $message);
497  $this->updateReportTable($uploadId, $this->jobId, $this->uri);
498  }
499 
500  protected function updateReportTable($uploadId, $jobId, $fileName)
501  {
502  $this->reportutils->updateOrInsertReportgenEntry($uploadId, $jobId, $fileName);
503  }
504 
510  protected function renderString($templateName, $vars)
511  {
512  return $this->renderer->load($templateName)->render($vars);
513  }
514 
522  private function getReportSummary($uploadId)
523  {
524  global $SysConf;
525  $row = $this->uploadDao->getReportInfo($uploadId);
526 
527  $review = htmlspecialchars($row['ri_reviewed']);
528  if ($review == 'NA') {
529  $review = '';
530  }
531  $critical = 'None';
532  $dependency = 'None';
533  $ecc = 'None';
534  $usage = 'None';
535  if (!empty($row['ri_ga_checkbox_selection'])) {
536  $listURCheckbox = explode(',', $row['ri_ga_checkbox_selection']);
537  if ($listURCheckbox[0] == 'checked') {
538  $critical = 'None';
539  }
540  if ($listURCheckbox[1] == 'checked') {
541  $critical = 'Found';
542  }
543  if ($listURCheckbox[2] == 'checked') {
544  $dependency = 'None';
545  }
546  if ($listURCheckbox[3] == 'checked') {
547  $dependency = 'SourceDependenciesFound';
548  }
549  if ($listURCheckbox[4] == 'checked') {
550  $dependency = 'BinaryDependenciesFound';
551  }
552  if ($listURCheckbox[5] == 'checked') {
553  $ecc = 'None';
554  }
555  if ($listURCheckbox[6] == 'checked') {
556  $ecc = 'Found';
557  }
558  if ($listURCheckbox[7] == 'checked') {
559  $usage = 'None';
560  }
561  if ($listURCheckbox[8] == 'checked') {
562  $usage = 'Found';
563  }
564  }
565  $componentType = $row['ri_component_type'];
566  if (!empty($componentType)) {
567  $componentType = ComponentType::TYPE_MAP[$componentType];
568  } else {
570  }
571  $componentId = $row['ri_component_id'];
572  if (empty($componentId) || $componentId == "NA") {
573  $componentId = "";
574  }
575 
576  $parentItem = $this->uploadDao->getUploadParent($uploadId);
577 
578  $uploadLink = $SysConf['SYSCONFIG']['FOSSologyURL'];
579  if (substr($uploadLink, 0, 4) !== "http") {
580  $uploadLink = "http://" . $uploadLink;
581  }
582  $uploadLink .= "?mod=browse&upload=$uploadId&item=$parentItem";
583 
584  return [[
585  'reportId' => uuid_create(UUID_TYPE_TIME),
586  'reviewedBy' => $review,
587  'componentName' => htmlspecialchars($row['ri_component']),
588  'community' => htmlspecialchars($row['ri_community']),
589  'version' => htmlspecialchars($row['ri_version']),
590  'componentHash' => '',
591  'componentReleaseDate' => htmlspecialchars($row['ri_release_date']),
592  'linkComponentManagement' => htmlspecialchars($row['ri_sw360_link']),
593  'linkScanTool' => $uploadLink,
594  'componentType' => htmlspecialchars($componentType),
595  'componentId' => htmlspecialchars($componentId)
596  ], [
597  'generalAssessment' => $row['ri_general_assesment'],
598  'criticalFilesFound' => $critical,
599  'dependencyNotes' => $dependency,
600  'exportRestrictionsFound' => $ecc,
601  'usageRestrictionsFound' => $usage,
602  'additionalNotes' => $row['ri_ga_additional']
603  ]];
604  }
605 
611  private function addLicenseNames($licenses)
612  {
613  $statementsWithNames = [];
614  foreach ($licenses as $license) {
615  $allLicenseCols = $this->licenseDao->getLicenseById($license["licenseId"],
616  $this->groupId);
617  $license["name"] = $allLicenseCols->getShortName();
618  $statementsWithNames[] = $license;
619  }
620  return $statementsWithNames;
621  }
622 }
623 
624 $agent = new CliXml();
625 $agent->scheduler_connect();
626 $agent->run_scheduler_event_loop();
627 $agent->scheduler_disconnect(0);
addLicenseNames($licenses)
Definition: clixml.php:611
preWorkOnArgsFlp($args, $key1, $key2)
Definition: clixml.php:113
getReportSummary($uploadId)
Definition: clixml.php:522
renderString($templateName, $vars)
Definition: clixml.php:510
processUploadId($uploadId)
Given an upload ID, process the items in it.
Definition: clixml.php:144
Structure of an Agent with all required parameters.
Definition: Agent.php:41
heartbeat($newProcessed)
Send hear beat to the scheduler.
Definition: Agent.php:203
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