FOSSology  4.4.0
Open Source License Compliance by Open Source Software
LicenseCsvImport.php
Go to the documentation of this file.
1 <?php
2 /*
3  SPDX-FileCopyrightText: © 2014-2015 Siemens AG
4 
5  SPDX-License-Identifier: GPL-2.0-only
6 */
7 
9 
14 
25 {
28  protected $dbManager;
31  protected $userDao;
34  protected $delimiter = ',';
37  protected $enclosure = '"';
40  protected $headrow = null;
43  protected $nkMap = array();
46  protected $mdkMap = array();
49  protected $alias = array(
50  'shortname'=>array('shortname','Short Name'),
51  'fullname'=>array('fullname','Long Name'),
52  'spdx_id'=>array('spdx_id', 'SPDX ID'),
53  'text'=>array('text','Full Text'),
54  'parent_shortname'=>array('parent_shortname','Decider Short Name'),
55  'report_shortname'=>array('report_shortname','Regular License Text Short Name'),
56  'url'=>array('url','URL'),
57  'notes'=>array('notes'),
58  'source'=>array('source','Foreign ID'),
59  'risk'=>array('risk','risk_level'),
60  'group'=>array('group','License group'),
61  'obligations'=>array('obligations','License obligations')
62  );
63 
70  {
71  $this->dbManager = $dbManager;
72  $this->userDao = $userDao;
73  }
74 
79  public function setDelimiter($delimiter=',')
80  {
81  $this->delimiter = substr($delimiter,0,1);
82  }
83 
88  public function setEnclosure($enclosure='"')
89  {
90  $this->enclosure = substr($enclosure,0,1);
91  }
92 
99  public function handleFile($filename)
100  {
101  if (!is_file($filename) || ($handle = fopen($filename, 'r')) === false) {
102  return _('Internal error');
103  }
104  $cnt = -1;
105  $msg = '';
106  try {
107  while (($row = fgetcsv($handle,0,$this->delimiter,$this->enclosure)) !== false) {
108  $log = $this->handleCsv($row);
109  if (!empty($log)) {
110  $msg .= "$log\n";
111  }
112  $cnt++;
113  }
114  $msg .= _('Read csv').(": $cnt ")._('licenses');
115  } catch(\Exception $e) {
116  fclose($handle);
117  return $msg .= _('Error while parsing file').': '.$e->getMessage();
118  }
119  fclose($handle);
120  return $msg;
121  }
122 
129  private function handleCsv($row)
130  {
131  if ($this->headrow === null) {
132  $this->headrow = $this->handleHeadCsv($row);
133  return 'head okay';
134  }
135 
136  $mRow = array();
137  foreach (array('shortname','fullname','text') as $needle) {
138  $mRow[$needle] = $row[$this->headrow[$needle]];
139  }
140  foreach (array('parent_shortname' => null, 'report_shortname' => null,
141  'url' => '', 'notes' => '', 'source' => '', 'risk' => 0,
142  'group' => null, 'spdx_id' => null) as $optNeedle=>$defaultValue) {
143  $mRow[$optNeedle] = $defaultValue;
144  if ($this->headrow[$optNeedle]!==false && array_key_exists($this->headrow[$optNeedle], $row)) {
145  $mRow[$optNeedle] = $row[$this->headrow[$optNeedle]];
146  }
147  }
148 
149  return $this->handleCsvLicense($mRow);
150  }
151 
158  private function handleHeadCsv($row)
159  {
160  $headrow = array();
161  $row[0] = trim($row[0], "\xEF\xBB\xBF"); // Remove BOM
162  foreach (array('shortname','fullname','text') as $needle) {
163  $col = ArrayOperation::multiSearch($this->alias[$needle], $row);
164  if (false === $col) {
165  throw new \Exception("Undetermined position of $needle");
166  }
167  $headrow[$needle] = $col;
168  }
169  foreach (array('parent_shortname', 'report_shortname', 'url', 'notes',
170  'source', 'risk', 'group', 'spdx_id') as $optNeedle) {
171  $headrow[$optNeedle] = ArrayOperation::multiSearch($this->alias[$optNeedle], $row);
172  }
173  return $headrow;
174  }
175 
182  private function updateLicense($row, $rfPk)
183  {
184  $stmt = __METHOD__ . '.getOldLicense';
185  $oldLicense = $this->dbManager->getSingleRow('SELECT ' .
186  'rf_shortname, rf_fullname, rf_spdx_id, rf_text, rf_url, rf_notes, rf_source, rf_risk ' .
187  'FROM license_ref WHERE rf_pk = $1', array($rfPk), $stmt);
188 
189  $stmt = __METHOD__ . '.getOldMapping';
190  $sql = 'SELECT rf_parent FROM license_map WHERE rf_fk = $1 AND usage = $2;';
191  $oldParent = null;
192  $oldParentRow = $this->dbManager->getSingleRow($sql, array($rfPk,
193  LicenseMap::CONCLUSION), $stmt);
194  if (!empty($oldParentRow)) {
195  $oldParent = $oldParentRow['rf_parent'];
196  }
197  $oldReport = null;
198  $oldReportRow = $this->dbManager->getSingleRow($sql, array($rfPk,
199  LicenseMap::REPORT), $stmt);
200  if (!empty($oldReportRow)) {
201  $oldReport = $oldReportRow['rf_parent'];
202  }
203 
204  $newParent = null;
205  $newParent = ($row['parent_shortname'] == null) ? null :
206  $this->getKeyFromShortname($row['parent_shortname']);
207 
208  $newReport = null;
209  $newReport = ($row['report_shortname'] == null) ? null :
210  $this->getKeyFromShortname($row['report_shortname']);
211 
212  $log = "License '$row[shortname]' already exists in DB (id = $rfPk)";
213  $stmt = __METHOD__ . '.updateLicense';
214  $sql = "UPDATE license_ref SET ";
215  if (! empty($row['group'])) {
216  $sql = "UPDATE license_candidate SET ";
217  }
218  $extraParams = array();
219  $param = array($rfPk);
220  if (!empty($row['fullname']) && $row['fullname'] != $oldLicense['rf_fullname']) {
221  $param[] = $row['fullname'];
222  $stmt .= '.fullN';
223  $extraParams[] = "rf_fullname=$" . count($param);
224  $log .= ", updated fullname";
225  }
226  if (!empty($row['spdx_id']) && $row['spdx_id'] != $oldLicense['rf_spdx_id']) {
227  $param[] = $row['spdx_id'];
228  $stmt .= '.spId';
229  $extraParams[] = "rf_spdx_id=$" . count($param);
230  $log .= ", updated SPDX ID";
231  }
232  if (!empty($row['text']) && $row['text'] != $oldLicense['rf_text'] && $row['text'] != LicenseMap::TEXT_MAX_CHAR_LIMIT) {
233  $param[] = $row['text'];
234  $stmt .= '.text';
235  $extraParams[] = "rf_text=$" . count($param) . ",rf_md5=md5($" .
236  count($param) . ")";
237  $log .= ", updated text";
238  }
239  if (!empty($row['url']) && $row['url'] != $oldLicense['rf_url']) {
240  $param[] = $row['url'];
241  $stmt .= '.url';
242  $extraParams[] = "rf_url=$" . count($param);
243  $log .= ", updated URL";
244  }
245  if (!empty($row['notes']) && $row['notes'] != $oldLicense['rf_notes']) {
246  $param[] = $row['notes'];
247  $stmt .= '.notes';
248  $extraParams[] = "rf_notes=$" . count($param);
249  $log .= ", updated notes";
250  }
251  if (!empty($row['source']) && $row['source'] != $oldLicense['rf_source']) {
252  $param[] = $row['source'];
253  $stmt .= '.updSource';
254  $extraParams[] = "rf_source=$".count($param);
255  $log .= ', updated the source';
256  }
257  if (!empty($row['risk']) && $row['risk'] != $oldLicense['rf_risk']) {
258  $param[] = $row['risk'];
259  $stmt .= '.updRisk';
260  $extraParams[] = "rf_risk=$".count($param);
261  $log .= ', updated the risk level';
262  }
263  if (count($param) > 1) {
264  $sql .= join(",", $extraParams);
265  $sql .= " WHERE rf_pk=$1;";
266  $this->dbManager->getSingleRow($sql, $param, $stmt);
267  $this->mdkMap[md5($row['text'])] = $rfPk;
268  }
269 
270  if (($oldParent != $newParent) && $this->setMap($newParent, $rfPk, LicenseMap::CONCLUSION)) {
271  $log .= " with conclusion '$row[parent_shortname]'";
272  }
273  if (($oldReport != $newReport) && $this->setMap($newReport, $rfPk, LicenseMap::REPORT)) {
274  $log .= " reporting '$row[report_shortname]'";
275  }
276  return $log;
277  }
278 
287  private function handleCsvLicense($row)
288  {
289  if (empty($row['risk'])) {
290  $row['risk'] = 0;
291  }
292  $rfPk = $this->getKeyFromShortname($row['shortname'], $row['group']);
293  $md5Match = $this->getKeyFromMd5($row['text']);
294 
295  // If shortname exists, does not collide with other texts and is not
296  // candidate
297  if ($rfPk !== false) {
298  if (! empty($row['group']) || ($md5Match == $rfPk || $md5Match === false)) {
299  return $this->updateLicense($row, $rfPk);
300  } else {
301  return "Error: MD5 checksum of '" . $row['shortname'] .
302  "' collides with license id=$md5Match";
303  }
304  }
305  if ($md5Match !== false && empty($row['group'])) {
306  return "Error: MD5 checksum of '" . $row['shortname'] .
307  "' collides with license id=$md5Match";
308  }
309 
310  $return = "";
311  if (!empty($row['group'])) {
312  $return = $this->insertNewLicense($row, "license_candidate");
313  } else {
314  $return = $this->insertNewLicense($row, "license_ref");
315  }
316  return $return;
317  }
318 
330  private function insertMapIfNontrivial($fromName,$toName,$usage)
331  {
332  $isNontrivial = ($fromName!==null && $fromName!=$toName && $this->getKeyFromShortname($fromName)!==false);
333  if ($isNontrivial) {
334  $this->dbManager->insertTableRow('license_map',
335  array('rf_fk'=>$this->getKeyFromShortname($toName),
336  'rf_parent'=>$this->getKeyFromShortname($fromName),
337  'usage'=> $usage));
338  }
339  return $isNontrivial;
340  }
341 
347  private function getKeyFromShortname($shortname, $groupFk = null)
348  {
349  $keyName = $shortname;
350  $tableName = "license_ref";
351  $addCondition = "";
352  $statement = __METHOD__ . ".getId";
353  $params = array($shortname);
354 
355  if ($groupFk != null) {
356  $keyName .= $groupFk;
357  $tableName = "license_candidate";
358  $addCondition = "AND group_fk = $2";
359  $statement .= ".candidate";
360  $params[] = $this->userDao->getGroupIdByName($groupFk);
361  }
362  $sql = "SELECT rf_pk FROM ONLY $tableName WHERE rf_shortname = $1 $addCondition;";
363  if (array_key_exists($keyName, $this->nkMap)) {
364  return $this->nkMap[$keyName];
365  }
366  $row = $this->dbManager->getSingleRow($sql, $params, $statement);
367  $this->nkMap[$keyName] = ($row===false) ? false : $row['rf_pk'];
368  return $this->nkMap[$keyName];
369  }
370 
376  private function getKeyFromMd5($licenseText)
377  {
378  $md5 = md5($licenseText);
379  if (array_key_exists($md5, $this->mdkMap)) {
380  return $this->mdkMap[$md5];
381  }
382  $row = $this->dbManager->getSingleRow("SELECT rf_pk " .
383  "FROM ONLY license_ref WHERE rf_md5=md5($1)",
384  array($licenseText));
385  $this->mdkMap[$md5] = (empty($row)) ? false : $row['rf_pk'];
386  return $this->mdkMap[$md5];
387  }
388 
399  private function setMap($from, $to, $usage)
400  {
401  $return = false;
402  if (!empty($from)) {
403  $sql = "SELECT license_map_pk, rf_parent FROM license_map WHERE rf_fk = $1 AND usage = $2;";
404  $statement = __METHOD__ . ".getCurrentMapping";
405  $row = $this->dbManager->getSingleRow($sql, array($to, $usage), $statement);
406  if (!empty($row) && $row['rf_parent'] != $from) {
407  $this->dbManager->updateTableRow("license_map", array(
408  'rf_fk' => $to,
409  'rf_parent' => $from,
410  'usage' => $usage
411  ), 'license_map_pk', $row['license_map_pk']);
412  $return = true;
413  } elseif (empty($row)) {
414  $this->dbManager->insertTableRow('license_map', array(
415  'rf_fk' => $to,
416  'rf_parent' => $from,
417  'usage' => $usage
418  ));
419  $return = true;
420  }
421  }
422  return $return;
423  }
424 
434  private function insertNewLicense($row, $tableName = "license_ref")
435  {
436  $stmtInsert = __METHOD__ . '.insert.' . $tableName;
437  $columns = array(
438  "rf_shortname" => $row['shortname'],
439  "rf_fullname" => $row['fullname'],
440  "rf_spdx_id" => $row['spdx_id'],
441  "rf_text" => $row['text'],
442  "rf_md5" => md5($row['text']),
443  "rf_detector_type" => 1,
444  "rf_url" => $row['url'],
445  "rf_notes" => $row['notes'],
446  "rf_source" => $row['source'],
447  "rf_risk" => $row['risk']
448  );
449 
450  $as = "";
451  if ($tableName == "license_candidate") {
452  $groupId = $this->userDao->getGroupIdByName($row['group']);
453  if (empty($groupId)) {
454  return "Error: Unable to insert candidate license " . $row['shortname'] .
455  " as group " . $row['group'] . " does not exist";
456  }
457  $columns["group_fk"] = $groupId;
458  $columns["marydone"] = $this->dbManager->booleanToDb(true);
459  $as = " as candidate license under group " . $row["group"];
460  }
461 
462  $newPk = $this->dbManager->insertTableRow($tableName, $columns, $stmtInsert, 'rf_pk');
463 
464  if ($tableName == "license_candidate") {
465  $this->nkMap[$row['shortname'].$row['group']] = $newPk;
466  } else {
467  $this->nkMap[$row['shortname']] = $newPk;
468  }
469  $this->mdkMap[md5($row['text'])] = $newPk;
470  $return = "Inserted '$row[shortname]' in DB" . $as;
471 
472  if ($this->insertMapIfNontrivial($row['parent_shortname'], $row['shortname'], LicenseMap::CONCLUSION)) {
473  $return .= " with conclusion '$row[parent_shortname]'";
474  }
475  if ($this->insertMapIfNontrivial($row['report_shortname'], $row['shortname'], LicenseMap::REPORT)) {
476  $return .= " reporting '$row[report_shortname]'";
477  }
478  return $return;
479  }
480 }
insertMapIfNontrivial($fromName, $toName, $usage)
Insert in license_map table if the license conclusion is non-trivial.
handleCsvLicense($row)
Handle a single row from CSV.
updateLicense($row, $rfPk)
Update the license info in the DB.
__construct(DbManager $dbManager, UserDao $userDao)
setEnclosure($enclosure='"')
Update the enclosure.
getKeyFromShortname($shortname, $groupFk=null)
Get the license id using license shortname from DB or nkMap.
handleHeadCsv($row)
Handle a row as head row.
handleFile($filename)
Read the CSV line by line and import it.
insertNewLicense($row, $tableName="license_ref")
Insert a new license in DB.
setDelimiter($delimiter=',')
Update the delimiter.
setMap($from, $to, $usage)
Update license mappings.
Wrapper class for license map.
Definition: LicenseMap.php:19
Fossology exception.
Definition: Exception.php:15
char * trim(char *ptext)
Trimming whitespace.
Definition: fossconfig.c:690
fo_dbManager * dbManager
fo_dbManager object
Definition: process.c:16
Utility functions for specific applications.