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