XML import rewritten (work in progress)

This commit is contained in:
nuxsmin
2017-01-19 18:47:26 +01:00
parent 476ae8a572
commit 680467404e
13 changed files with 293 additions and 158 deletions

View File

@@ -134,6 +134,10 @@ class LogMessage extends MessageBase
*/
public function addDetails($key, $value)
{
if ($value === '' || $key === '') {
return $this;
}
$this->details[] = [$this->formatString($key), $this->formatString($value)];
return $this;
@@ -218,7 +222,7 @@ class LogMessage extends MessageBase
* Devolver un detalle formateado
*
* @param array $detail
* @param bool $translate
* @param bool $translate
* @return string
*/
protected function formatDetail(array $detail, $translate = false)

View File

@@ -43,10 +43,12 @@ class CsvImport extends CsvImportBase
*/
public function doImport()
{
try{
try {
$this->LogMessage->addDescription(sprintf(__('Formato detectado: %s'), 'CSV'));
$this->file->readFileToArray();
$this->processAccounts();
} catch (SPException $e){
} catch (SPException $e) {
throw $e;
}
}

View File

@@ -28,9 +28,6 @@ use SP\Core\Exceptions\SPException;
use SP\DataModel\AccountExtData;
use SP\DataModel\CategoryData;
use SP\DataModel\CustomerData;
use SP\Log\Log;
use SP\Mgmt\Categories\Category;
use SP\Mgmt\Customers\Customer;
defined('APP_ROOT') || die();
@@ -76,10 +73,6 @@ abstract class CsvImportBase extends ImportBase
{
$line = 0;
$Log = new Log();
$LogMessage = $Log->getLogMessage();
$LogMessage->setAction(__('Importar Cuentas', false));
foreach ($this->file->getFileContent() as $data) {
$line++;
$fields = str_getcsv($data, $this->ImportParams->getCsvDelimiter(), '"');
@@ -115,19 +108,11 @@ abstract class CsvImportBase extends ImportBase
try {
$this->addAccount($AccountData);
$LogMessage->addDetails(__('Cuenta importada', false), $accountName);
} catch (SPException $e) {
// Escribir los mensajes pendientes
$Log->writeLog(true);
$LogMessage->addDescription(__('Error importando cuenta', false));
$LogMessage->addDetails(__('Error procesando línea', false), $line);
$LogMessage->addDetails(__('Error', false), $e->getMessage());
// Flush y reset
$Log->writeLog(true);
$this->LogMessage->addDetails(__('Error importando cuenta', false), $accountName);
$this->LogMessage->addDetails(__('Error procesando línea', false), $line);
$this->LogMessage->addDetails(__('Error', false), $e->getMessage());
}
}
$Log->writeLog();
}
}

View File

@@ -61,9 +61,9 @@ class Import
*/
public function doImport(&$fileData)
{
$Log = new Log();
$LogMessage = $Log->getLogMessage();
$LogMessage = new LogMessage();
$LogMessage->setAction(__('Importar Cuentas', false));
$Log = new Log($LogMessage);
try {
$file = new FileImport($fileData);
@@ -71,20 +71,22 @@ class Import
switch ($file->getFileType()) {
case 'text/csv':
case 'application/vnd.ms-excel':
$Import = new CsvImport($file, $this->ImportParams);
$Import = new CsvImport($file, $this->ImportParams, $LogMessage);
break;
case 'text/xml':
$Import = new XmlImport($file, $this->ImportParams);
$Import = new XmlImport($file, $this->ImportParams, $LogMessage);
break;
default:
throw new SPException(
SPException::SP_WARNING,
sprintf(__('Tipo mime no soportado ("%s")', false), $file->getFileType()),
sprintf(__('Tipo mime no soportado ("%s")'), $file->getFileType()),
__('Compruebe el formato del archivo', false)
);
}
$Import->doImport();
$LogMessage->addDetails(__('Cuentas importadas'), $Import->getCounter());
} catch (SPException $e) {
$LogMessage->addDescription($e->getMessage());
$LogMessage->addDetails(__('Ayuda', false), $e->getHint());
@@ -94,11 +96,11 @@ class Import
throw $e;
}
$LogMessage->addDescription(__('Importación finalizada', false));
$Log->writeLog();
$Log->writeLog(true);
Email::sendEmail($LogMessage);
$LogMessage->addDescription(__('Importación finalizada', false));
$LogMessage->addDescription(__('Revise el registro de eventos para más detalles', false));
return $LogMessage;

View File

@@ -24,6 +24,7 @@
namespace SP\Import;
use Import\ImportInterface;
use SP\Account\Account;
use SP\Core\Crypt;
use SP\Core\Exceptions\SPException;
@@ -44,7 +45,7 @@ defined('APP_ROOT') || die();
*
* @package SP
*/
abstract class ImportBase
abstract class ImportBase implements ImportInterface
{
/**
* @var ImportParams
@@ -58,55 +59,55 @@ abstract class ImportBase
* @var LogMessage
*/
protected $LogMessage;
/**
* @var int
*/
protected $counter = 0;
/**
* ImportBase constructor.
*
* @param FileImport $File
* @param FileImport $File
* @param ImportParams $ImportParams
* @param LogMessage $LogMessage
*/
public function __construct(FileImport $File, ImportParams $ImportParams)
public function __construct(FileImport $File = null, ImportParams $ImportParams = null, LogMessage $LogMessage = null)
{
$this->file = $File;
$this->ImportParams = $ImportParams;
$this->LogMessage = new LogMessage(__('Importar Cuentas', false));
$this->LogMessage = null !== $LogMessage ? $LogMessage : new LogMessage(__('Importar Cuentas', false));
}
/**
* Iniciar la importación desde XML.
*
* @throws \SP\Core\Exceptions\SPException
* @return bool
* @return LogMessage
*/
public abstract function doImport();
public function getLogMessage()
{
return $this->LogMessage;
}
/**
* Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas.
*
* @return bool
* @param LogMessage $LogMessage
*/
protected function parseFileHeader()
public function setLogMessage($LogMessage)
{
$handle = @fopen($this->file->getTmpFile(), 'r');
$headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i';
$this->LogMessage = $LogMessage;
}
if ($handle) {
// No. de líneas a leer como máximo
$maxLines = 5;
$count = 0;
/**
* @return int
*/
public function getCounter()
{
return $this->counter;
}
while (($buffer = fgets($handle, 4096)) !== false && $count <= $maxLines) {
if (preg_match($headersRegex, $buffer, $app)) {
fclose($handle);
return strtolower($app[0]);
}
$count++;
}
fclose($handle);
}
return false;
/**
* @param ImportParams $ImportParams
*/
public function setImportParams($ImportParams)
{
$this->ImportParams = $ImportParams;
}
/**
@@ -139,8 +140,10 @@ abstract class ImportBase
$Account->createAccount();
$this->LogMessage->addDetails(__('Cuenta creada', false), $AccountData->getAccountName());
$this->counter++;
} catch (SPException $e) {
Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR);
$this->LogMessage->addDetails($e->getMessage(), $AccountData->getAccountName());
$this->LogMessage->addDetails(__('ERROR', false), $e->getHint());
}
return true;
@@ -163,7 +166,8 @@ abstract class ImportBase
return $Category;
} catch (SPException $e) {
Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR);
$this->LogMessage->addDetails($e->getMessage(), $CategoryData->category_name);
$this->LogMessage->addDetails(__('ERROR', false), $e->getHint());
}
return null;
@@ -185,7 +189,8 @@ abstract class ImportBase
return $Customer;
} catch (SPException $e) {
Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR);
$this->LogMessage->addDetails($e->getMessage(), $CustomerData->getCustomerName());
$this->LogMessage->addDetails(__('ERROR', false), $e->getHint());
}
return null;
@@ -208,7 +213,8 @@ abstract class ImportBase
return $Tag;
} catch (SPException $e) {
Log::writeNewLog(__FUNCTION__, $e->getMessage(), Log::ERROR);
$this->LogMessage->addDetails($e->getMessage(), $TagData->getTagName());
$this->LogMessage->addDetails(__('Error', false), $e->getHint());
}
return null;

View File

@@ -0,0 +1,37 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Import;
/**
* Interface ImportInterface
* @package Import
*/
interface ImportInterface
{
/**
* Iniciar la importación
*/
public function doImport();
}

View File

@@ -34,8 +34,10 @@ defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde KeePass
*/
class KeepassImport extends XmlImportBase
class KeepassImport extends ImportBase
{
use XmlImportTrait;
/**
* @var int
*/
@@ -66,6 +68,12 @@ class KeepassImport extends XmlImportBase
*/
protected function processCategories(SimpleXMLElement $xml)
{
$Tags = $this->xmlDOM->getElementsByTagName('Root');
/** @var \DOMNode[] $Tags */
// foreach ($Tags as $Tag) {
// }
foreach ($xml as $node) {
if ($node->Group) {
foreach ($node->Group as $group) {
@@ -100,8 +108,8 @@ class KeepassImport extends XmlImportBase
/**
* Obtener los datos de las entradas de KeePass.
*
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
* @throws \SP\Core\Exceptions\SPException
*/
protected function processAccounts(SimpleXMLElement $entries, $categoryId)

View File

@@ -34,8 +34,10 @@ defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde KeePassX
*/
class KeepassXImport extends XmlImportBase
class KeepassXImport extends ImportBase
{
use XmlImportTrait;
/**
* @var int
*/
@@ -100,8 +102,8 @@ class KeepassXImport extends XmlImportBase
/**
* Obtener los datos de las entradas de KeePass.
*
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
* @param SimpleXMLElement $entries El objeto XML con las entradas
* @param int $categoryId Id de la categoría
* @throws \SP\Core\Exceptions\SPException
*/
protected function processAccounts(SimpleXMLElement $entries, $categoryId)

View File

@@ -36,8 +36,10 @@ defined('APP_ROOT') || die();
/**
* Esta clase es la encargada de importar cuentas desde sysPass
*/
class SyspassImport extends XmlImportBase
class SyspassImport extends ImportBase
{
use XmlImportTrait;
/**
* Mapeo de etiquetas
*

View File

@@ -22,67 +22,33 @@
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
namespace Import;
use SP\Core\Exceptions\SPException;
defined('APP_ROOT') || die();
use SP\Import\FileImport;
/**
* Class XmlImportBase abstracta para manejar archivos de importación en formato XML
*
* @package SP
* Class XmlFileImport
* @package Import
*/
abstract class XmlImportBase extends ImportBase
class XmlFileImport
{
/**
* @var \SimpleXMLElement
* @var FileImport
*/
protected $xml;
protected $FileImport;
/**
* @var \DOMDocument
*/
protected $xmlDOM;
/**
* ImportBase constructor.
*
* @param FileImport $File
* @param ImportParams $ImportParams
* @throws SPException
* XmlFileImport constructor.
* @param FileImport $FileImport
*/
public function __construct(FileImport $File, ImportParams $ImportParams)
public function __construct(FileImport $FileImport)
{
parent::__construct($File, $ImportParams);
try {
$this->readXMLFile();
} catch (SPException $e) {
throw $e;
}
}
/**
* Leer el archivo a un objeto XML.
*
* @throws SPException
*/
protected function readXMLFile()
{
$this->xml = simplexml_load_file($this->file->getTmpFile());
// Cargar el XML con DOM
$this->xmlDOM = new \DOMDocument();
$this->xmlDOM->load($this->file->getTmpFile());
if ($this->xml === false) {
throw new SPException(
SPException::SP_CRITICAL,
__('Error interno', false),
__('No es posible procesar el archivo XML', false)
);
}
$this->FileImport = $FileImport;
}
/**
@@ -92,11 +58,18 @@ abstract class XmlImportBase extends ImportBase
*/
public function detectXMLFormat()
{
if ((string)$this->xml->Meta->Generator === 'KeePass') {
return 'keepass';
} else if ((string)$this->xml->Meta->Generator === 'sysPass') {
return 'syspass';
} else if ($xmlApp = $this->parseFileHeader()) {
$this->readXMLFile();
$tags = $this->xmlDOM->getElementsByTagName('Generator');
/** @var \DOMElement[] $tags */
foreach ($tags as $tag) {
if ($tag->nodeValue === 'KeePass' || $tag->nodeValue === 'sysPass') {
return strtolower($tag->nodeValue);
}
}
if ($xmlApp = $this->parseFileHeader()) {
switch ($xmlApp) {
case 'keepassx_database':
return 'keepassx';
@@ -117,31 +90,58 @@ abstract class XmlImportBase extends ImportBase
}
/**
* Obtener los datos de los nodos
* Leer el archivo a un objeto XML.
*
* @param string $nodeName Nombre del nodo principal
* @param string $childNodeName Nombre de los nodos hijos
* @throws SPException
*/
protected function getNodesData($nodeName, $childNodeName, $callback)
protected function readXMLFile()
{
$ParentNode = $this->xmlDOM->getElementsByTagName($nodeName);
// Cargar el XML con DOM
$this->xmlDOM = new \DOMDocument();
if ($ParentNode->length === 0) {
if ($this->xmlDOM->load($this->FileImport->getTmpFile()) === false) {
throw new SPException(
SPException::SP_WARNING,
__('Formato de XML inválido', false),
sprintf(__('El nodo "%s" no existe'), $nodeName));
} elseif (!is_callable([$this, $callback])) {
throw new SPException(SPException::SP_WARNING, __('Método inválido', false));
}
/** @var \DOMElement $nodes */
foreach ($ParentNode as $nodes) {
/** @var \DOMElement $Account */
foreach ($nodes->getElementsByTagName($childNodeName) as $Node) {
$this->$callback($Node);
}
SPException::SP_CRITICAL,
__('Error interno', false),
__('No es posible procesar el archivo XML', false)
);
}
}
/**
* Leer la cabecera del archivo XML y obtener patrones de aplicaciones conocidas.
*
* @return bool
*/
protected function parseFileHeader()
{
$handle = @fopen($this->FileImport->getTmpFile(), 'r');
$headersRegex = '/(KEEPASSX_DATABASE|revelationdata)/i';
if ($handle) {
// No. de líneas a leer como máximo
$maxLines = 5;
$count = 0;
while (($buffer = fgets($handle, 4096)) !== false && $count <= $maxLines) {
if (preg_match($headersRegex, $buffer, $app)) {
fclose($handle);
return strtolower($app[0]);
}
$count++;
}
fclose($handle);
}
return false;
}
/**
* @return \DOMDocument
*/
public function getXmlDOM()
{
return $this->xmlDOM;
}
}

View File

@@ -24,8 +24,8 @@
namespace SP\Import;
use SP\Core\Exceptions\SPException;
use SP\Log\Log;
use Import\XmlFileImport;
use SP\Core\Messages\LogMessage;
defined('APP_ROOT') || die();
@@ -35,36 +35,42 @@ defined('APP_ROOT') || die();
*
* @package SP
*/
class XmlImport extends XmlImportBase
class XmlImport
{
/**
* Iniciar la importación desde XML.
*
* @throws SPException
* @return bool
* @param FileImport $File
* @param ImportParams $ImportParams
* @param LogMessage $LogMessage
* @return ImportBase|false
*/
public function doImport()
public function doImport(FileImport $File, ImportParams $ImportParams, LogMessage $LogMessage)
{
$Import = null;
$format = $this->detectXMLFormat();
$XmlFileImport = new XmlFileImport($File);
$format = $XmlFileImport->detectXMLFormat();
switch ($format) {
case 'syspass':
$Import = new SyspassImport($this->file, $this->ImportParams);
$Import = new SyspassImport();
break;
case 'keepass':
$Import = new KeepassImport($this->file, $this->ImportParams);
$Import = new KeepassImport();
break;
case 'keepassx':
$Import = new KeepassXImport($this->file, $this->ImportParams);
$Import = new KeepassXImport();
break;
default:
return false;
}
if (is_object($Import)){
Log::writeNewLog(__('Importar Cuentas', false), __('Inicio', false));
Log::writeNewLog(__('Importar Cuentas', false), sprintf(__('Formato detectado: %s', false), strtoupper($format)));
$Import->setImportParams($ImportParams);
$Import->setXmlDOM($XmlFileImport->getXmlDOM());
$Import->setLogMessage($LogMessage);
$Import->doImport();
}
$LogMessage->addDescription(sprintf(__('Formato detectado: %s'), strtoupper($format)));
return $Import;
}
}

View File

@@ -0,0 +1,81 @@
<?php
/**
* sysPass
*
* @author nuxsmin
* @link http://syspass.org
* @copyright 2012-2017, Rubén Domínguez nuxsmin@$syspass.org
*
* This file is part of sysPass.
*
* sysPass is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* sysPass is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with sysPass. If not, see <http://www.gnu.org/licenses/>.
*/
namespace SP\Import;
use Import\XmlFileImport;
use SP\Core\Exceptions\SPException;
defined('APP_ROOT') || die();
/**
* Trait XmlImportTrait para manejar archivos de importación en formato XML
*
* @package SP
*/
trait XmlImportTrait
{
/**
* @var \DOMDocument
*/
protected $xmlDOM;
/**
* @param \DOMDocument $xmlDOM
*/
public function setXmlDOM($xmlDOM)
{
$this->xmlDOM =& $xmlDOM;
}
/**
* Obtener los datos de los nodos
*
* @param string $nodeName Nombre del nodo principal
* @param string $childNodeName Nombre de los nodos hijos
* @param string $callback Método a ejecutar
* @throws SPException
*/
protected function getNodesData($nodeName, $childNodeName, $callback)
{
$ParentNode = $this->xmlDOM->getElementsByTagName($nodeName);
if ($ParentNode->length === 0) {
throw new SPException(
SPException::SP_WARNING,
__('Formato de XML inválido', false),
sprintf(__('El nodo "%s" no existe'), $nodeName));
} elseif (!is_callable([$this, $callback])) {
throw new SPException(SPException::SP_WARNING, __('Método inválido', false));
}
/** @var \DOMElement $nodes */
foreach ($ParentNode as $nodes) {
/** @var \DOMElement $Account */
foreach ($nodes->getElementsByTagName($childNodeName) as $Node) {
$this->$callback($Node);
}
}
}
}

View File

@@ -27,7 +27,7 @@ use SP\Core\Session; ?>
<option value=""><?php echo __('Seleccionar Usuario'); ?></option>
<?php foreach ($users as $user): ?>
<option
value="<?php echo $user->id; ?>" <?php echo ($user->id === Session::getUserData()->getUserId()) ? 'selected' : ''; ?>>
value="<?php echo $user->id; ?>" <?php echo ($user->id === Session::getUserData()->getUserId()) ? 'selected' : ''; ?>>
<?php echo $user->name; ?>
</option>
<?php endforeach; ?>
@@ -51,7 +51,7 @@ use SP\Core\Session; ?>
<option value=""><?php echo __('Seleccionar Grupo'); ?></option>
<?php foreach ($groups as $group): ?>
<option
value="<?php echo $group->id; ?>" <?php echo ($group->id === $SessionUserData->getUserGroupId()) ? 'selected' : ''; ?>>
value="<?php echo $group->id; ?>" <?php echo ($group->id === $SessionUserData->getUserGroupId()) ? 'selected' : ''; ?>>
<?php echo $group->name; ?>
</option>
<?php endforeach; ?>
@@ -94,7 +94,7 @@ use SP\Core\Session; ?>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<input id="importMasterPwd" name="importMasterPwd" type="password"
class="mdl-textfield__input mdl-color-text--indigo-400 passwordfield__input-show"
maxlength="255" />
maxlength="255"/>
<label class="mdl-textfield__label"
for="importMasterPwd"><?php echo __('Clave Maestra'); ?></label>
</div>