FOSSology  4.7.1
Open Source License Compliance by Open Source Software
UploadDao.php
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2018 Siemens AG
4  Authors: Andreas Würl, Steffen Weber
5 
6  SPDX-License-Identifier: GPL-2.0-only
7 */
8 
9 namespace Fossology\Lib\Dao;
10 
22 use Monolog\Logger;
23 use DateTime;
24 
25 require_once(dirname(dirname(__FILE__)) . "/common-dir.php");
26 
27 class UploadDao
28 {
29 
30  const REUSE_NONE = 0;
31  const REUSE_ENHANCED = 2;
32  const REUSE_MAIN = 4;
33  const REUSE_CONF = 16;
34  const REUSE_COPYRIGHT = 128;
35  const REUSE_BULK = 8;
36  const UNIFIED_REPORT_HEADINGS = array(
37  "assessment" => array("Assessment Summary" => true),
38  "compliancetasks" => array("Required license compliance tasks" => true),
39  "acknowledgements" => array("Acknowledgements" => true),
40  "exportrestrictions" => array("Export Restrictions" => true),
41  "intellectualProperty" => array("Patent Relevant Statements" => true),
42  "notes" => array("Notes" => true),
43  "scanresults" => array("Results of License Scan" => true),
44  "mainlicenses" => array("Main Licenses" => true),
45  "redlicense" => array("Other OSS Licenses (red) - Do not Use Licenses" => true),
46  "yellowlicense" => array("Other OSS Licenses (yellow) - additional obligations to common rules (e.g. copyleft)" => true),
47  "whitelicense" => array("Other OSS Licenses (white) - only common rules" => true),
48  "overviewwithwithoutobligations" => array("Overview of All Licenses with or without Obligations" => true),
49  "copyrights" => array("Copyrights" => true),
50  "copyrightsuf" => array("Copyrights (User Findings)" => true),
51  "bulkfindings" => array("Bulk Findings" => true),
52  "licensenf" => array("Non-Functional Licenses" => true),
53  "irrelevantfiles" => array("Irrelevant Files" => true),
54  "dnufiles" => array("Do not use Files" => true),
55  "changelog" => array("Clearing Protocol Change Log" => true)
56  );
57  const CLIXML_REPORT_HEADINGS = array(
58  "mainlicensesclixml" => array("Main Licenses" => true),
59  "licensepath" => array("License File Path" => true),
60  "licensehash" => array("License File Hash" => true),
61  "copyrightsclixml" => array("Copyrights" => true),
62  "copyrightpath" => array("Copyright File Path" => true),
63  "copyrighthash" => array("Copyright File Hash" => true),
64  "exportrestrictionsclixml" => array("Export Restrictions(ECC)" => true),
65  "eccpath" => array("ECC File Path" => true),
66  "ecchash" => array("ECC File Hash" => true),
67  "intellectualPropertyclixml" => array("Identified Patent Relevant Analysis(IPRA)" => true),
68  "iprapath" => array("IPRA File Path" => true),
69  "iprahash" => array("IPRA File Hash" => true),
70  "allobligations" => array("All Obligations" => true),
71  "acknowledgementsclixml" => array("Acknowledgements" => true),
72  "irrelevantfilesclixml" => array("Irrelevant Files" => true),
73  "dnufilesclixml" => array("Do not use Files" => true),
74  "notesclixml" => array("Additional Notes" => true)
75  );
77  private $dbManager;
79  private $logger;
81  private $permissionDao;
82 
83  public function __construct(DbManager $dbManager, Logger $logger, UploadPermissionDao $uploadPermissionDao)
84  {
85  $this->dbManager = $dbManager;
86  $this->logger = $logger;
87  $this->permissionDao = $uploadPermissionDao;
88  }
89 
90 
96  public function getUploadEntry($uploadTreeId, $uploadTreeTableName = "uploadtree")
97  {
98  $stmt = __METHOD__ . ".$uploadTreeTableName";
99  $uploadEntry = $this->dbManager->getSingleRow("SELECT * FROM $uploadTreeTableName WHERE uploadtree_pk = $1",
100  array($uploadTreeId), $stmt);
101  if ($uploadEntry) {
102  $uploadEntry['tablename'] = $uploadTreeTableName;
103  }
104  return $uploadEntry;
105  }
106 
113  public function getUploadtreeIdFromPfile($uploadFk, $pfileFk)
114  {
115  $uploadTreeTableName = $this->getUploadtreeTableName($uploadFk);
116  $stmt = __METHOD__ . ".$uploadTreeTableName";
117  $uploadEntry = $this->dbManager->getSingleRow("SELECT uploadtree_pk " .
118  "FROM $uploadTreeTableName " .
119  "WHERE upload_fk = $1 AND pfile_fk = $2",
120  array($uploadFk, $pfileFk), $stmt);
121  return intval($uploadEntry['uploadtree_pk']);
122  }
123 
128  public function getUpload($uploadId)
129  {
130  $stmt = __METHOD__;
131  $row = $this->dbManager->getSingleRow("SELECT * FROM upload WHERE upload_pk = $1",
132  array($uploadId), $stmt);
133 
134  return $row ? Upload::createFromTable($row) : null;
135  }
136 
137  public function getActiveUploadsArray()
138  {
139  $stmt = __METHOD__;
140  $queryResult = $this->dbManager->getRows("SELECT * FROM upload where pfile_fk IS NOT NULL",
141  array(), $stmt);
142 
143  $results = array();
144  foreach ($queryResult as $row) {
145  $results[] = Upload::createFromTable($row);
146  }
147 
148  return $results;
149  }
150 
156  public function getAccessibleUploads($groupId)
157  {
158  $stmt = __METHOD__;
159  $permNone = Auth::PERM_NONE;
160  $sql = "SELECT u.* FROM upload u
161  LEFT JOIN perm_upload p ON p.upload_fk = u.upload_pk AND p.group_fk = $1
162  WHERE u.pfile_fk IS NOT NULL
163  AND (p.perm > $permNone OR u.public_perm > $permNone)";
164 
165  $queryResult = $this->dbManager->getRows($sql, array($groupId), $stmt);
166 
167  $results = array();
168  foreach ($queryResult as $row) {
169  $results[] = Upload::createFromTable($row);
170  }
171 
172  return $results;
173  }
174 
180  public function getItemTreeBounds($itemId, $uploadTreeTableName = "uploadtree")
181  {
182  $uploadEntryData = $this->getUploadEntry($itemId, $uploadTreeTableName);
183  return $this->createItemTreeBounds($uploadEntryData, $uploadTreeTableName);
184  }
185 
191  public function getItemTreeBoundsFromUploadId($uploadTreeId, $uploadId)
192  {
193  $uploadTreeTableName = $this->getUploadtreeTableName($uploadId);
194  return $this->getItemTreeBounds($uploadTreeId, $uploadTreeTableName);
195  }
196 
203  public function getParentItemBounds($uploadId, $uploadTreeTableName = null)
204  {
205  if ($uploadTreeTableName === null) {
206  $uploadTreeTableName = $this->getUploadtreeTableName($uploadId);
207  }
208 
209  $stmt = __METHOD__ . ".$uploadTreeTableName";
210  $parameters = array();
211  $uploadCondition = $this->handleUploadIdForTable($uploadTreeTableName, $uploadId, $parameters);
212 
213  $uploadEntryData = $this->dbManager->getSingleRow("SELECT * FROM $uploadTreeTableName
214  WHERE parent IS NULL
215  $uploadCondition
216  ",
217  $parameters, $stmt);
218 
219  return $uploadEntryData ? $this->createItemTreeBounds($uploadEntryData, $uploadTreeTableName) : false;
220  }
221 
226  public function countPlainFiles(ItemTreeBounds $itemTreeBounds)
227  {
228  $uploadTreeTableName = $itemTreeBounds->getUploadTreeTableName();
229 
230  $stmt = __METHOD__ . ".$uploadTreeTableName";
231  $parameters = array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight());
232  $uploadCondition = $this->handleUploadIdForTable($uploadTreeTableName, $itemTreeBounds->getUploadId(), $parameters);
233 
234  $row = $this->dbManager->getSingleRow("SELECT count(*) as count FROM $uploadTreeTableName
235  WHERE lft BETWEEN $1 AND $2
236  $uploadCondition
237  AND ((ufile_mode & (3<<28))=0)
238  AND pfile_fk != 0",
239  $parameters, $stmt);
240  return intval($row["count"]);
241  }
242 
243  private function handleUploadIdForTable($uploadTreeTableName, $uploadId, &$parameters)
244  {
245  if ($uploadTreeTableName === "uploadtree" || $uploadTreeTableName === "uploadtree_a") {
246  $parameters[] = $uploadId;
247  return " AND upload_fk = $" . count($parameters) . " ";
248  } else {
249  return "";
250  }
251  }
252 
256  public function getStatusTypeMap()
257  {
258  global $container;
260  $uploadStatus = $container->get('upload_status.types');
261  return $uploadStatus->getMap();
262  }
263 
271  public function getStatus($uploadId, $groupId)
272  {
273  if ($this->isAccessible($uploadId, $groupId)) {
274  $row = $this->dbManager->getSingleRow("SELECT status_fk " .
275  "FROM upload_clearing WHERE upload_fk=$1 AND group_fk=$2;",
276  array($uploadId, $groupId));
277  if (false === $row) {
278  return 1;
279  }
280  return $row['status_fk'];
281  } else {
282  throw new \Exception("permission denied");
283  }
284  }
285 
286 
294  public function getAssignee($uploadId, $groupId)
295  {
296  if ($this->isAccessible($uploadId, $groupId)) {
297  $row = $this->dbManager->getSingleRow("SELECT assignee FROM upload_clearing WHERE upload_fk=$1 AND group_fk=$2;",
298  array($uploadId, $groupId));
299  if (false === $row) {
300  return 1;
301  }
302  return $row['assignee'];
303  } else {
304  throw new \Exception("permission denied");
305  }
306  }
307 
315  public function getAssigneeDate(int $uploadId): ?string
316  {
317  $sql = "SELECT MIN(event_ts) as event_ts FROM upload_events WHERE upload_fk = $1 " .
318  "AND event_type = " . UploadEvents::ASSIGNEE_EVENT;
319  $row = $this->dbManager->getSingleRow($sql, [$uploadId], __METHOD__);
320  if (empty($row) || empty($row["event_ts"])) {
321  return null;
322  }
323  return $row["event_ts"];
324  }
325 
332  public function getClosedDate(int $uploadId): ?string
333  {
334  $sql = "SELECT MAX(event_ts) as event_ts FROM upload_events WHERE upload_fk = $1 " .
335  "AND event_type = " . UploadEvents::UPLOAD_CLOSED_EVENT;
336  $row = $this->dbManager->getSingleRow($sql, [$uploadId], __METHOD__);
337  if (empty($row) || empty($row["event_ts"])) {
338  return null;
339  }
340  return $row["event_ts"];
341  }
342 
349  public function getClearingDuration(int $uploadId): array
350  {
351  $batch = $this->getClearingDurationsBatch(array($uploadId));
352  return $batch[$uploadId] ?? ['NA', 0];
353  }
354 
360  public function getClearingDurationsBatch(array $uploadIds): array
361  {
362  if (empty($uploadIds)) {
363  return array();
364  }
365 
366  $uploadIds = array_unique(array_filter($uploadIds));
367  if (empty($uploadIds)) {
368  return array();
369  }
370 
371  $results = array();
372  foreach ($uploadIds as $id) {
373  $results[(int)$id] = array("NA", 0);
374  }
375 
376  $uploadIdsStr = '{' . implode(',', array_map('intval', $uploadIds)) . '}';
377 
378  $sql = "SELECT upload_fk, " .
379  "MIN(CASE WHEN event_type = $2 THEN event_ts END) as assignee_ts, " .
380  "MAX(CASE WHEN event_type = $3 THEN event_ts END) as closed_ts " .
381  "FROM upload_events " .
382  "WHERE upload_fk = ANY($1::int[]) AND event_type IN ($2, $3) " .
383  "GROUP BY upload_fk";
384 
385  $rows = $this->dbManager->getRows($sql, array($uploadIdsStr, UploadEvents::ASSIGNEE_EVENT, UploadEvents::UPLOAD_CLOSED_EVENT), __METHOD__);
386 
387  foreach ($rows as $row) {
388  $id = (int)$row['upload_fk'];
389  $duration = "NA";
390  $durationSort = 0;
391 
392  if (!empty($row['assignee_ts']) && !empty($row['closed_ts'])) {
393  try {
394  $assignDate = new DateTime($row['assignee_ts']);
395  $closingDate = new DateTime($row['closed_ts']);
396  if ($assignDate < $closingDate) {
397  $duration = HumanDuration($closingDate->diff($assignDate));
398  $durationSort = $closingDate->getTimestamp() - $assignDate->getTimestamp();
399  }
400  } catch (\Exception $_) {
401  }
402  }
403  $results[$id] = array($duration, $durationSort);
404  }
405 
406  return $results;
407  }
416  public function getUploadtreeTableName($uploadId)
417  {
418  if (!empty($uploadId)) {
419  $statementName = __METHOD__;
420  $row = $this->dbManager->getSingleRow(
421  "SELECT uploadtree_tablename FROM upload WHERE upload_pk=$1",
422  array($uploadId),
423  $statementName
424  );
425  if (!empty($row['uploadtree_tablename'])) {
426  return $row['uploadtree_tablename'];
427  }
428  }
429  return "uploadtree";
430  }
431 
437  public function getNextItem($uploadId, $itemId, $options = null)
438  {
439  return $this->getItemByDirection($uploadId, $itemId, self::DIR_FWD, $options);
440  }
441 
448  public function getPreviousItem($uploadId, $itemId, $options = null)
449  {
450  return $this->getItemByDirection($uploadId, $itemId, self::DIR_BCK, $options);
451  }
452 
453  const DIR_FWD = 1;
454  const DIR_BCK = -1;
455  const NOT_FOUND = null;
456 
457 
465  public function getItemByDirection($uploadId, $itemId, $direction, $options)
466  {
467  $uploadTreeTableName = $this->getUploadtreeTableName($uploadId);
468  $originItem = $this->getUploadEntry($itemId, $uploadTreeTableName);
469  $originLft = $originItem['lft'];
470 
471  $options[UploadTreeProxy::OPT_ITEM_FILTER] = " AND ut.ufile_mode & (3<<28) = 0";
472  $uploadTreeViewName = 'items2care';
473 
474  if ($direction == self::DIR_FWD) {
475  $uploadTreeViewName .= 'fwd';
476  $options[UploadTreeProxy::OPT_ITEM_FILTER] .= " AND lft>$1";
477  $order = 'ASC';
478  } else {
479  $uploadTreeViewName .= 'bwd';
480  $options[UploadTreeProxy::OPT_ITEM_FILTER] .= " AND lft<$1";
481  $order = 'DESC';
482  }
483 
484  $uploadTreeView = new UploadTreeProxy($uploadId, $options, $uploadTreeTableName, $uploadTreeViewName);
485  $statementName = __METHOD__ . ".$uploadTreeViewName.";
486  $query = $uploadTreeView->getDbViewQuery()." ORDER BY lft $order";
487 
488  $newItemRow = $this->dbManager->getSingleRow("$query LIMIT 1", array($originLft), $statementName);
489  if ($newItemRow) {
490  return $this->createItem($newItemRow, $uploadTreeTableName);
491  } else {
492  return self::NOT_FOUND;
493  }
494  }
495 
496 
501  public function getUploadParent($uploadId)
502  {
503  $uploadTreeTableName = $this->getUploadtreeTableName($uploadId);
504  $statementname = __METHOD__ . $uploadTreeTableName;
505 
506  $parent = $this->dbManager->getSingleRow(
507  "SELECT uploadtree_pk
508  FROM $uploadTreeTableName
509  WHERE upload_fk=$1 AND parent IS NULL", array($uploadId), $statementname);
510  if (false === $parent) {
511  throw new \Exception("Missing upload tree parent for upload");
512  }
513  return $parent['uploadtree_pk'];
514  }
515 
516  public function getLeftAndRight($uploadtreeID, $uploadTreeTableName = "uploadtree")
517  {
518  $statementName = __METHOD__ . $uploadTreeTableName;
519  $leftRight = $this->dbManager->getSingleRow(
520  "SELECT lft,rgt FROM $uploadTreeTableName WHERE uploadtree_pk = $1",
521  array($uploadtreeID), $statementName
522  );
523 
524  return array($leftRight['lft'], $leftRight['rgt']);
525  }
526 
532  public function getContainingFileCount(ItemTreeBounds $itemTreeBounds, UploadTreeProxy $uploadTreeView)
533  {
534  $sql = "SELECT count(*) FROM " . $uploadTreeView->getDbViewName() . " WHERE lft BETWEEN $1 AND $2";
535  $result = $this->dbManager->getSingleRow($sql
536  , array($itemTreeBounds->getLeft(), $itemTreeBounds->getRight()), __METHOD__ . $uploadTreeView->asCTE());
537  return $result['count'];
538  }
539 
546  public function getContainedItems(ItemTreeBounds $itemTreeBounds, $addCondition = "", $addParameters = array())
547  {
548  $uploadTreeTableName = $itemTreeBounds->getUploadTreeTableName();
549 
550  $statementName = __METHOD__ . ".$uploadTreeTableName";
551 
552  $view = new UploadTreeViewProxy($itemTreeBounds, array(UploadTreeViewProxy::CONDITION_PLAIN_FILES));
553 
554  $this->dbManager->prepare($statementName,
555  $view->asCTE() . " SELECT * FROM " . $view->getDbViewName() ." ut ".
556  ($addCondition ? " WHERE " . $addCondition : ''));
557  $res = $this->dbManager->execute($statementName, $addParameters);
558  $items = array();
559 
560  while ($row = $this->dbManager->fetchArray($res)) {
561  $items[] = $this->createItem($row, $uploadTreeTableName);
562  }
563  $this->dbManager->freeResult($res);
564  return $items;
565  }
566 
574  public function addReusedUpload($uploadId, $reusedUploadId, $groupId, $reusedGroupId, $reuseMode=0)
575  {
576  $this->dbManager->insertTableRow('upload_reuse',
577  array('upload_fk'=>$uploadId, 'group_fk'=> $groupId, 'reused_upload_fk'=>$reusedUploadId, 'reused_group_fk'=>$reusedGroupId,'reuse_mode'=>$reuseMode));
578  }
579 
586  public function getReusedUpload($uploadId, $groupId)
587  {
588  $statementName = __METHOD__;
589 
590  $this->dbManager->prepare($statementName,
591  "SELECT reused_upload_fk, reused_group_fk, reuse_mode FROM upload_reuse WHERE upload_fk = $1 AND group_fk=$2 ORDER BY date_added DESC");
592  $res = $this->dbManager->execute($statementName, array($uploadId, $groupId));
593  $reusedPairs = $this->dbManager->fetchAll($res);
594  $this->dbManager->freeResult($res);
595  return $reusedPairs;
596  }
597 
603  protected function createItem($uploadEntry, $uploadTreeTableName)
604  {
605  $itemTreeBounds = new ItemTreeBounds(
606  intval($uploadEntry['uploadtree_pk']),
607  $uploadTreeTableName,
608  intval($uploadEntry['upload_fk']),
609  intval($uploadEntry['lft']), intval($uploadEntry['rgt']));
610 
611  $parent = $uploadEntry['parent'];
612  return new Item(
613  $itemTreeBounds, $parent !== null ? intval($parent) : null, intval($uploadEntry['pfile_fk']), intval($uploadEntry['ufile_mode']), $uploadEntry['ufile_name']
614  );
615  }
616 
623  protected function createItemTreeBounds($uploadEntryData, $uploadTreeTableName)
624  {
625  if ($uploadEntryData === false) {
626  throw new Exception("did not find uploadTreeId in $uploadTreeTableName");
627  }
628  return new ItemTreeBounds(intval($uploadEntryData['uploadtree_pk']), $uploadTreeTableName, intval($uploadEntryData['upload_fk']), intval($uploadEntryData['lft']), intval($uploadEntryData['rgt']));
629  }
630 
636  public function countNonArtifactDescendants(ItemTreeBounds $itemTreeBounds, $isFlat=true)
637  {
638  $stmt=__METHOD__;
639  $sql = "SELECT count(*) FROM ".$itemTreeBounds->getUploadTreeTableName()." ut "
640  . "WHERE ut.upload_fk=$1";
641  $params = array($itemTreeBounds->getUploadId());
642  if (!$isFlat) {
643  $stmt = __METHOD__.'.parent';
644  $params[] = $itemTreeBounds->getItemId();
645  $sql .= " AND ut.ufile_mode & (1<<28) = 0 AND ut.realparent = $2";
646  } else {
647  $params[] = $itemTreeBounds->getLeft();
648  $params[] = $itemTreeBounds->getRight();
649  $sql .= " AND ut.ufile_mode & (3<<28) = 0 AND (ut.lft BETWEEN $2 AND $3)";
650  }
651 
652  $descendants = $this->dbManager->getSingleRow($sql,$params);
653  return $descendants['count'];
654  }
655 
656 
657  public function isAccessible($uploadId, $groupId)
658  {
659  return $this->permissionDao->isAccessible($uploadId, $groupId);
660  }
661 
662  public function isEditable($uploadId, $groupId)
663  {
664  return $this->permissionDao->isEditable($uploadId, $groupId);
665  }
666 
667  public function makeAccessibleToGroup($uploadId, $groupId, $perm=null)
668  {
669  $this->permissionDao->makeAccessibleToGroup($uploadId, $groupId, $perm);
670  }
671 
672  public function makeAccessibleToAllGroupsOf($uploadId, $userId, $perm=null)
673  {
674  $this->permissionDao->makeAccessibleToAllGroupsOf($uploadId, $userId, $perm);
675  }
676 
677  public function filterAccessibleUploads(array $uploadIds, $groupId)
678  {
679  return $this->permissionDao->filterAccessibleUploads($uploadIds, $groupId);
680  }
681 
686  public function getUploadHashes($uploadId)
687  {
688  $pfile = $this->dbManager->getSingleRow('SELECT pfile.* FROM upload, pfile WHERE upload_pk=$1 AND pfile_fk=pfile_pk',
689  array($uploadId), __METHOD__);
690  return array('sha1'=>$pfile['pfile_sha1'],'md5'=>$pfile['pfile_md5'],'sha256'=>$pfile['pfile_sha256']);
691  }
692 
699  public function getFatItemArray($itemId,$uploadId,$uploadtreeTablename)
700  {
701  $sqlChildrenOf = "SELECT COUNT(*) FROM $uploadtreeTablename s
702  WHERE ufile_mode&(1<<28)=0 and s.upload_fk=$2 AND s.realparent=";
703  $sql="WITH RECURSIVE item_path (item_id,num_children,depth,ufile_mode,ufile_name) AS (
704  SELECT uploadtree_pk item_id, ($sqlChildrenOf $1) num_children, 0 depth, ufile_mode, ufile_name
705  FROM $uploadtreeTablename WHERE upload_fk=$2 AND uploadtree_pk=$1
706  UNION
707  SELECT uploadtree_pk item_id, ($sqlChildrenOf ut.uploadtree_pk) num_children,
708  item_path.depth+1 depth, ut.ufile_mode, item_path.ufile_name||'/'||ut.ufile_name ufile_name
709  FROM $uploadtreeTablename ut INNER JOIN item_path ON item_id=ut.realparent
710  WHERE upload_fk=$2 AND ut.ufile_mode&(1<<28)=0 AND num_children<2
711  )
712  SELECT * FROM item_path WHERE num_children!=1 OR ufile_mode&(1<<29)=0 ORDER BY depth DESC LIMIT 1";
713  return $this->dbManager->getSingleRow($sql,array($itemId, $uploadId),__METHOD__.$uploadtreeTablename);
714  }
715 
722  public function getFatItemId($itemId,$uploadId,$uploadtreeTablename)
723  {
724  $itemRow = $this->getFatItemArray($itemId,$uploadId,$uploadtreeTablename);
725  return $itemRow['item_id'];
726  }
727 
732  public function getPFileDataPerFileName(ItemTreeBounds $itemTreeBounds)
733  {
734  $uploadTreeTableName = $itemTreeBounds->getUploadTreeTableName();
735  $statementName = __METHOD__ . '.' . $uploadTreeTableName;
736  $param = array();
737 
738  $param[] = $itemTreeBounds->getLeft();
739  $param[] = $itemTreeBounds->getRight();
740  $condition = " lft BETWEEN $1 AND $2";
741  $condition .= " AND (ufile_mode & (1<<28)) = 0";
742 
743  if ('uploadtree_a' == $uploadTreeTableName) {
744  $param[] = $itemTreeBounds->getUploadId();
745  $condition .= " AND upload_fk=$".count($param);
746  }
747 
748  $sql = "
749 SELECT ufile_name, uploadtree_pk, lft, rgt, ufile_mode,
750  pfile_pk, pfile_md5, pfile_sha1, pfile_sha256
751 FROM $uploadTreeTableName
752  LEFT JOIN pfile
753  ON pfile_fk = pfile_pk
754 WHERE $condition
755 ORDER BY lft asc
756 ";
757 
758  $this->dbManager->prepare($statementName, $sql);
759  $result = $this->dbManager->execute($statementName, $param);
760  $pfilePerFileName = array();
761 
762  $row = $this->dbManager->fetchArray($result);
763  $pathStack = array($row['ufile_name']);
764  $rgtStack = array($row['rgt']);
765  $lastLft = $row['lft'];
766  $this->addToPFilePerFileName($pfilePerFileName, $pathStack, $row);
767  while ($row = $this->dbManager->fetchArray($result)) {
768  if ($row['lft'] < $lastLft) {
769  continue;
770  }
771 
772  $this->updateStackState($pathStack, $rgtStack, $lastLft, $row);
773  $this->addToPFilePerFileName($pfilePerFileName, $pathStack, $row);
774  }
775  $this->dbManager->freeResult($result);
776  return $pfilePerFileName;
777  }
778 
779  private function updateStackState(&$pathStack, &$rgtStack, &$lastLft, $row)
780  {
781  if ($row['lft'] >= $lastLft) {
782  while (count($rgtStack) > 0 && $row['lft'] > $rgtStack[count($rgtStack)-1]) {
783  array_pop($pathStack);
784  array_pop($rgtStack);
785  }
786  if ($row['lft'] > $lastLft) {
787  $pathStack[] = $row['ufile_name'];
788  $rgtStack[] = $row['rgt'];
789  $lastLft = $row['lft'];
790  }
791  }
792  }
793 
794  private function addToPFilePerFileName(&$pfilePerFileName, $pathStack, $row)
795  {
796  if (($row['ufile_mode'] & (1 << 29)) == 0) {
797  $path = implode('/', $pathStack);
798  $pfilePerFileName[$path]['pfile_pk'] = $row['pfile_pk'];
799  $pfilePerFileName[$path]['uploadtree_pk'] = $row['uploadtree_pk'];
800  $pfilePerFileName[$path]['md5'] = $row['pfile_md5'];
801  $pfilePerFileName[$path]['sha1'] = $row['pfile_sha1'];
802  $pfilePerFileName[$path]['sha256'] = $row['pfile_sha256'];
803  }
804  }
805 
811  public function getPFilesDataPerHashAlgo(ItemTreeBounds $itemTreeBounds, $hashAlgo="sha1")
812  {
813  $uploadTreeTableName = $itemTreeBounds->getUploadTreeTableName();
814  $statementName = __METHOD__ . '.' . $uploadTreeTableName;
815  $param = array();
816 
817  $param[] = $itemTreeBounds->getLeft();
818  $param[] = $itemTreeBounds->getRight();
819  $condition = " lft BETWEEN $1 AND $2";
820  $condition .= " AND (ufile_mode & (1<<28)) = 0";
821 
822  if ('uploadtree_a' == $uploadTreeTableName) {
823  $param[] = $itemTreeBounds->getUploadId();
824  $condition .= " AND upload_fk=$".count($param);
825  }
826  $condition .= " AND pfile_$hashAlgo IS NOT NULL";
827 
828  $sql = "
829 SELECT pfile_fk, uploadtree_pk, ufile_mode, pfile_$hashAlgo as hash
830 FROM $uploadTreeTableName
831  LEFT JOIN pfile
832  ON pfile_fk = pfile_pk
833 WHERE $condition
834 ORDER BY lft asc
835 ";
836 
837  $this->dbManager->prepare($statementName, $sql);
838  $result = $this->dbManager->execute($statementName, $param);
839 
840  $pfilePerHashAlgo = array();
841  while ($row = $this->dbManager->fetchArray($result)) {
842  if (($row['ufile_mode']&(1<<29)) == 0) {
843  $pfilePerHashAlgo[strtolower($row['hash'])][] = array('pfile_pk' => $row['pfile_fk'],
844  'uploadtree_pk' => $row['uploadtree_pk']);
845  }
846  }
847  $this->dbManager->freeResult($result);
848  return $pfilePerHashAlgo;
849  }
850 
851 
852  /* @param int $uploadId
853  * @return array
854  */
855  public function getReportInfo($uploadId)
856  {
857  $stmt = __METHOD__;
858  $sql = "SELECT * FROM report_info WHERE upload_fk = $1";
859  $row = $this->dbManager->getSingleRow($sql, array($uploadId), $stmt);
860 
861  if (empty($row)) {
862  $this->dbManager->begin();
863  $stmt = __METHOD__.'ifempty';
864  $userSPDXDefaults = $this->getUserSPDXDefaultsForUpload($uploadId);
865  $userCycloneDxDefaults = $this->getUserCycloneDxDefaultsForUpload($uploadId)->toReportInfo();
866  $sql = "INSERT INTO report_info (upload_fk, ri_spdx_selection, ri_cyclonedx_selection) VALUES ($1, $2, $3) RETURNING *";
867  $row = $this->dbManager->getSingleRow($sql, array($uploadId, $userSPDXDefaults, $userCycloneDxDefaults), $stmt);
868  $this->dbManager->commit();
869  }
870  return $row;
871  }
872 
878  private function getUserSPDXDefaultsForUpload($uploadId)
879  {
880  $stmt = __METHOD__ . '.getOwner';
881  $sql = "SELECT user_fk FROM upload WHERE upload_pk = $1";
882  $uploadOwner = $this->dbManager->getSingleRow($sql, array($uploadId), $stmt);
883 
884  if (empty($uploadOwner)) {
885  return "unchecked,unchecked,unchecked";
886  }
887 
888  $userId = $uploadOwner['user_fk'];
889 
890  $stmt = __METHOD__ . '.getUserDefaults';
891  $sql = "SELECT spdx_settings FROM users WHERE user_pk = $1";
892  $userDefaults = $this->dbManager->getSingleRow($sql, array($userId), $stmt);
893 
894  if (empty($userDefaults) || empty($userDefaults['spdx_settings'])) {
895  return "unchecked,unchecked,unchecked";
896  }
897 
898  $settings = explode(',', $userDefaults['spdx_settings']);
899  if (count($settings) < 3) {
900  $settings = array_pad($settings, 3, 'unchecked');
901  }
902 
903  $osselotExport = $settings[0];
904  $spdxLicenseComment = $settings[1];
905  $ignoreFilesWOInfo = $settings[2];
906 
907  $result = "$spdxLicenseComment,$ignoreFilesWOInfo,$osselotExport";
908 
909  return $result;
910  }
911 
917  private function getUserCycloneDxDefaultsForUpload($uploadId)
918  {
919  $sql = "SELECT user_fk FROM upload WHERE upload_pk = $1";
920  $owner = $this->dbManager->getSingleRow($sql, array($uploadId),
921  __METHOD__ . '.owner');
922  if (empty($owner['user_fk'])) {
923  return new CycloneDxSettings();
924  }
925  $sql = "SELECT cyclonedx_settings FROM users WHERE user_pk = $1";
926  $row = $this->dbManager->getSingleRow($sql, array($owner['user_fk']),
927  __METHOD__ . '.settings');
929  empty($row) ? null : $row['cyclonedx_settings']);
930  }
931 
940  public function getCyclonedxSettings($uploadId)
941  {
942  $sql = "SELECT ri_cyclonedx_selection FROM report_info WHERE upload_fk = $1";
943  $row = $this->dbManager->getSingleRow($sql, array($uploadId), __METHOD__);
944  $settings = empty($row) ? null :
945  CycloneDxSettings::fromReportInfo($row['ri_cyclonedx_selection']);
946  return $settings ?? $this->getUserCycloneDxDefaultsForUpload($uploadId);
947  }
948 
956  public function updateReportInfo($uploadId, $column, $value)
957  {
958  if ($column === "ri_unifiedcolumns") {
959  $value = json_decode($value, true);
960  $oldValues = $this->getReportInfo($uploadId)["ri_unifiedcolumns"];
961  if (!empty($oldValues)) {
962  $oldValues = json_decode($oldValues, true);
963  } else {
964  $oldValues = self::UNIFIED_REPORT_HEADINGS;
965  }
966  foreach ($value as $key => $val) {
967  $newValText = array_keys($val)[0];
968  $newValValue = array_values($val)[0];
969  $newValValue = ($newValValue === true || $newValValue == "true") ? "on" : null;
970  $oldValues[$key] = [$newValText => $newValValue];
971  }
972  $value = json_encode($oldValues);
973  } elseif ($column === "ri_excluded_obligations") {
974  $value = json_decode($value, true);
975  $oldValues = $this->getReportInfo($uploadId)["ri_excluded_obligations"];
976  if (!empty($oldValues)) {
977  $oldValues = json_decode($oldValues, true);
978  } else {
979  $oldValues = [];
980  }
981  foreach ($value as $key => $newValue) {
982  $oldValues[$key] = $newValue;
983  }
984  $value = json_encode($oldValues);
985  } elseif ($column === "ri_globaldecision") {
986  $value = filter_var($value, FILTER_VALIDATE_BOOL);
987  } elseif ($column === "ri_cyclonedx_selection") {
988  $value = $this->mergeCyclonedxSettings($uploadId, $value);
989  }
990 
991  $sql = "UPDATE report_info SET $column = $2 WHERE upload_fk = $1;";
992  $stmt = __METHOD__ . "updateReportInfo" . $column;
993  $this->dbManager->getSingleRow($sql, [$uploadId, $value], $stmt);
994  return true;
995  }
996 
1005  private function mergeCyclonedxSettings($uploadId, $value)
1006  {
1007  $changes = is_array($value) ? $value : json_decode((string) $value, true);
1008  if (!is_array($changes)) {
1009  throw new \UnexpectedValueException("CycloneDX settings must be a JSON object");
1010  }
1011  $merged = json_decode($this->getCyclonedxSettings($uploadId)->toReportInfo(), true);
1012  foreach ($changes as $key => $newValue) {
1013  if (!array_key_exists($key, $merged)) {
1014  throw new \UnexpectedValueException("Unknown CycloneDX setting '$key'");
1015  }
1016  if (is_bool($merged[$key])) {
1017  $newValue = filter_var($newValue, FILTER_VALIDATE_BOOL, FILTER_NULL_ON_FAILURE);
1018  if ($newValue === null) {
1019  throw new \UnexpectedValueException("CycloneDX setting '$key' must be a boolean");
1020  }
1021  }
1022  $merged[$key] = $newValue;
1023  }
1024  if (CycloneDxSettings::normaliseNamespace((string) $merged['tagNamespace']) === null) {
1025  throw new \UnexpectedValueException("Invalid CycloneDX tag namespace");
1026  }
1027  if (CycloneDxSettings::normaliseSpecVersion((string) $merged['specVersion']) === null) {
1028  throw new \UnexpectedValueException("Unsupported CycloneDX spec version");
1029  }
1030  return CycloneDxSettings::fromReportInfo(json_encode($merged))->toReportInfo();
1031  }
1032 
1033  /* @param int $uploadId
1034  * @return int
1035  */
1036  public function getGlobalDecisionSettingsFromInfo($uploadId, $setGlobal=null)
1037  {
1038  $stmt = __METHOD__ . 'get';
1039  $sql = "SELECT ri_globaldecision, ri_spdx_selection FROM report_info WHERE upload_fk = $1";
1040  $row = $this->dbManager->getSingleRow($sql, array($uploadId), $stmt);
1041  if (empty($row)) {
1042  if ($setGlobal === null) {
1043  // Old upload, set default value to enable
1044  $setGlobal = 1;
1045  }
1046  $stmt = __METHOD__ . 'ifempty';
1047  $userSPDXDefaults = $this->getUserSPDXDefaultsForUpload($uploadId);
1048  $userCycloneDxDefaults = $this->getUserCycloneDxDefaultsForUpload($uploadId)->toReportInfo();
1049  $sql = "INSERT INTO report_info (upload_fk, ri_globaldecision, ri_spdx_selection, ri_cyclonedx_selection) VALUES ($1, $2, $3, $4) RETURNING ri_globaldecision";
1050  $row = $this->dbManager->getSingleRow($sql, array($uploadId, $setGlobal, $userSPDXDefaults, $userCycloneDxDefaults), $stmt);
1051 
1052  }
1053 
1054  if (!empty($setGlobal)) {
1055  $stmt = __METHOD__ . 'update';
1056  $sql = "UPDATE report_info SET ri_globaldecision = $2 WHERE upload_fk = $1 RETURNING ri_globaldecision";
1057  $row = $this->dbManager->getSingleRow($sql, array($uploadId, $setGlobal), $stmt);
1058  }
1059 
1060  return $row['ri_globaldecision'];
1061  }
1062 
1068  public function getUploadHashesFromPfileId($pfilePk)
1069  {
1070  $stmt = __METHOD__."getUploadHashesFromPfileId";
1071  $sql = "SELECT * FROM pfile WHERE pfile_pk = $1";
1072  $row = $this->dbManager->getSingleRow($sql, array($pfilePk), $stmt);
1073 
1074  return ["sha1" => $row["pfile_sha1"], "md5" => $row["pfile_md5"], "sha256" => $row["pfile_sha256"]];
1075  }
1076 
1082  public function insertReportConfReuse($uploadId, $reusedUploadId)
1083  {
1084  $stmt = __METHOD__ . ".checkReused";
1085  $sql = "SELECT 1 AS exists FROM report_info WHERE upload_fk = $1 LIMIT 1;";
1086  $row = $this->dbManager->getSingleRow($sql, array($reusedUploadId), $stmt);
1087 
1088  if (empty($row['exists'])) {
1089  return false;
1090  }
1091 
1092  $this->dbManager->begin();
1093 
1094  $stmt = __METHOD__ . ".removeExists";
1095  $sql = "DELETE FROM report_info WHERE upload_fk = $1;";
1096  $this->dbManager->getSingleRow($sql, [$uploadId], $stmt);
1097 
1098  $stmt = __METHOD__ . ".getAllColumns";
1099  $sql = "SELECT string_agg(column_name, ',') AS columns
1100  FROM information_schema.columns
1101  WHERE table_name = 'report_info'
1102  AND column_name != 'ri_pk'
1103  AND column_name != 'upload_fk';";
1104  $row = $this->dbManager->getSingleRow($sql, [], $stmt);
1105  $columns = $row['columns'];
1106 
1107  $stmt = __METHOD__."CopyinsertReportConfReuse";
1108  $this->dbManager->prepare($stmt,
1109  "INSERT INTO report_info(upload_fk, $columns)
1110  SELECT $1, $columns
1111  FROM report_info WHERE upload_fk = $2"
1112  );
1113  $this->dbManager->freeResult($this->dbManager->execute(
1114  $stmt, array($uploadId, $reusedUploadId)
1115  ));
1116 
1117  $this->dbManager->commit();
1118  return true;
1119  }
1120 }
Contains the constants and helpers for authentication of user.
Definition: Auth.php:24
getUploadtreeIdFromPfile($uploadFk, $pfileFk)
Definition: UploadDao.php:113
getParentItemBounds($uploadId, $uploadTreeTableName=null)
Definition: UploadDao.php:203
getReusedUpload($uploadId, $groupId)
Definition: UploadDao.php:586
createItem($uploadEntry, $uploadTreeTableName)
Definition: UploadDao.php:603
addReusedUpload($uploadId, $reusedUploadId, $groupId, $reusedGroupId, $reuseMode=0)
Definition: UploadDao.php:574
countNonArtifactDescendants(ItemTreeBounds $itemTreeBounds, $isFlat=true)
Definition: UploadDao.php:636
getClearingDuration(int $uploadId)
Definition: UploadDao.php:349
createItemTreeBounds($uploadEntryData, $uploadTreeTableName)
Definition: UploadDao.php:623
getUploadHashesFromPfileId($pfilePk)
Get Pfile hashes from the pfile id.
Definition: UploadDao.php:1068
getCyclonedxSettings($uploadId)
CycloneDX settings of an upload.
Definition: UploadDao.php:940
getPFileDataPerFileName(ItemTreeBounds $itemTreeBounds)
Definition: UploadDao.php:732
mergeCyclonedxSettings($uploadId, $value)
Apply a partial CycloneDX settings update to an upload's settings.
Definition: UploadDao.php:1005
getContainingFileCount(ItemTreeBounds $itemTreeBounds, UploadTreeProxy $uploadTreeView)
Definition: UploadDao.php:532
getFatItemId($itemId, $uploadId, $uploadtreeTablename)
Definition: UploadDao.php:722
getItemTreeBoundsFromUploadId($uploadTreeId, $uploadId)
Definition: UploadDao.php:191
getAssigneeDate(int $uploadId)
Definition: UploadDao.php:315
getPFilesDataPerHashAlgo(ItemTreeBounds $itemTreeBounds, $hashAlgo="sha1")
Definition: UploadDao.php:811
getItemByDirection($uploadId, $itemId, $direction, $options)
Definition: UploadDao.php:465
getClearingDurationsBatch(array $uploadIds)
Definition: UploadDao.php:360
getFatItemArray($itemId, $uploadId, $uploadtreeTablename)
Definition: UploadDao.php:699
countPlainFiles(ItemTreeBounds $itemTreeBounds)
Definition: UploadDao.php:226
getNextItem($uploadId, $itemId, $options=null)
Definition: UploadDao.php:437
getClosedDate(int $uploadId)
Definition: UploadDao.php:332
getUserCycloneDxDefaultsForUpload($uploadId)
CycloneDX defaults of the upload owner.
Definition: UploadDao.php:917
updateReportInfo($uploadId, $column, $value)
Update report info for upload.
Definition: UploadDao.php:956
getPreviousItem($uploadId, $itemId, $options=null)
Definition: UploadDao.php:448
getItemTreeBounds($itemId, $uploadTreeTableName="uploadtree")
Definition: UploadDao.php:180
getUploadtreeTableName($uploadId)
Get the uploadtree table name for this upload_pk If upload_pk does not exist, return "uploadtree".
Definition: UploadDao.php:416
getAssignee($uploadId, $groupId)
Get the upload assignee id.
Definition: UploadDao.php:294
getUploadEntry($uploadTreeId, $uploadTreeTableName="uploadtree")
Definition: UploadDao.php:96
insertReportConfReuse($uploadId, $reusedUploadId)
Definition: UploadDao.php:1082
getUserSPDXDefaultsForUpload($uploadId)
Definition: UploadDao.php:878
getStatus($uploadId, $groupId)
Get the upload status.
Definition: UploadDao.php:271
CycloneDX export settings of a user (defaults) or of an upload.
This class contains the events for the upload_events table.
Fossology exception.
Definition: Exception.php:15
asCTE()
Common Table Expressions.
Definition: DbViewProxy.php:69
HumanDuration(DateInterval $duration)
Convert DateInterval to Human readable format.
Definition: common-ui.php:120
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16