From e921c4df25a36ff72bea13056ca90e48a87d8f30 Mon Sep 17 00:00:00 2001 From: Paul Rock Date: Sun, 28 Jun 2020 20:39:01 +0300 Subject: [PATCH] Deprecated write method was removed from Client class, export method now support additional arguments, detector of /export command which exec ssh was added --- src/Client.php | 126 ++++++++++++++++++++++--------------- src/Interfaces/ClientInterface.php | 25 +++----- 2 files changed, 82 insertions(+), 69 deletions(-) diff --git a/src/Client.php b/src/Client.php index 723b3a7..478f8af 100644 --- a/src/Client.php +++ b/src/Client.php @@ -5,7 +5,6 @@ namespace RouterOS; use DivineOmega\SSHConnection\SSHConnection; use RouterOS\Exceptions\ClientException; use RouterOS\Exceptions\ConfigException; -use RouterOS\Exceptions\QueryException; use RouterOS\Interfaces\ClientInterface; use RouterOS\Interfaces\QueryInterface; use RouterOS\Helpers\ArrayHelper; @@ -14,7 +13,6 @@ use function array_shift; use function chr; use function count; use function is_array; -use function is_string; use function md5; use function pack; use function preg_match_all; @@ -36,15 +34,21 @@ class Client implements Interfaces\ClientInterface * * @var \RouterOS\Config */ - private $_config; + private $config; /** * API communication object * * @var \RouterOS\APIConnector */ + private $connector; - private $_connector; + /** + * Some strings with custom output + * + * @var string + */ + private $customOutput; /** * Client constructor. @@ -69,7 +73,7 @@ class Client implements Interfaces\ClientInterface } // Save config if everything is okay - $this->_config = $config; + $this->config = $config; // Skip next step if not need to instantiate connection if (false === $autoConnect) { @@ -92,46 +96,21 @@ class Client implements Interfaces\ClientInterface */ private function config(string $parameter) { - return $this->_config->get($parameter); - } - - /** - * Send write query to RouterOS - * - * @param string|array|\RouterOS\Interfaces\QueryInterface $query - * - * @return \RouterOS\Interfaces\ClientInterface - * @throws \RouterOS\Exceptions\QueryException - * @deprecated - */ - public function write($query): ClientInterface - { - if (is_string($query)) { - $query = new Query($query); - } elseif (is_array($query)) { - $endpoint = array_shift($query); - $query = new Query($endpoint, $query); - } - - if (!$query instanceof Query) { - throw new QueryException('Parameters cannot be processed'); - } - - // Submit query to RouterOS - return $this->writeRAW($query); + return $this->config->get($parameter); } /** * Send write query to RouterOS (modern version of write) * - * @param string|\RouterOS\Query $endpoint Path of API query or Query object - * @param array|null $where List of where filters - * @param string|null $operations Some operations which need make on response - * @param string|null $tag Mark query with tag + * @param array|string|\RouterOS\Interfaces\QueryInterface $endpoint Path of API query or Query object + * @param array|null $where List of where filters + * @param string|null $operations Some operations which need make on response + * @param string|null $tag Mark query with tag * - * @return \RouterOS\Client + * @return \RouterOS\Interfaces\ClientInterface * @throws \RouterOS\Exceptions\QueryException * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\ConfigException * @since 1.0.0 */ public function query($endpoint, array $where = null, string $operations = null, string $tag = null): ClientInterface @@ -211,37 +190,62 @@ class Client implements Interfaces\ClientInterface * * @return \RouterOS\Interfaces\ClientInterface * @throws \RouterOS\Exceptions\QueryException + * @throws \RouterOS\Exceptions\ConfigException * @since 1.0.0 */ private function writeRAW(QueryInterface $query): ClientInterface { + $commands = $query->getQuery(); + + // Check if first command is export + if (strpos($commands[0], '/export') === 0) { + + // Convert export command with all arguments to valid SSH command + $arguments = explode('/', $commands[0]); + unset($arguments[1]); + $arguments = implode(' ', $arguments); + + // Call the router via ssh and store output of export + $this->customOutput = $this->export($arguments); + + // Return current object + return $this; + } + // Send commands via loop to router - foreach ($query->getQuery() as $command) { - $this->_connector->writeWord(trim($command)); + foreach ($commands as $command) { + $this->connector->writeWord(trim($command)); } // Write zero-terminator (empty string) - $this->_connector->writeWord(''); + $this->connector->writeWord(''); + // Return current object return $this; } /** - * Read RAW response from RouterOS + * Read RAW response from RouterOS, it can be /export command results also, not only array from API * - * @return array + * @return array|string * @since 1.0.0 */ - private function readRAW(): array + private function readRAW() { // By default response is empty $response = []; // We have to wait a !done or !fatal $lastReply = false; + // Convert strings to array and return results + if ($this->isCustomOutput()) { + // Return RAW configuration + return $this->customOutput; + } + // Read answer from socket in loop while (true) { - $word = $this->_connector->readWord(); + $word = $this->connector->readWord(); if ('' === $word) { if ($lastReply) { @@ -278,7 +282,7 @@ class Client implements Interfaces\ClientInterface * Reply ends with a complete !done or !fatal block (ended with 'empty line') * A !fatal block precedes TCP connexion close * - * @param bool $parse + * @param bool $parse If need parse output to array * * @return mixed */ @@ -287,6 +291,12 @@ class Client implements Interfaces\ClientInterface // Read RAW response $response = $this->readRAW(); + // Return RAW configuration if custom output is set + if ($this->isCustomOutput()) { + $this->customOutput = null; + return $response; + } + // Parse results and return return $parse ? $this->rosario($response) : $response; } @@ -455,7 +465,7 @@ class Client implements Interfaces\ClientInterface // => problem with legacy version, swap it and retry // Only tested with ROS pre 6.43, will test with post 6.43 => this could make legacy parameter obsolete? if ($legacyRetry && $this->isLegacy($response)) { - $this->_config->set('legacy', true); + $this->config->set('legacy', true); return $this->login(); } @@ -502,7 +512,7 @@ class Client implements Interfaces\ClientInterface // If socket is active if (null !== $this->getSocket()) { - $this->_connector = new APIConnector(new Streams\ResourceStream($this->getSocket())); + $this->connector = new APIConnector(new Streams\ResourceStream($this->getSocket())); // If we logged in then exit from loop if (true === $this->login()) { $connected = true; @@ -522,19 +532,31 @@ class Client implements Interfaces\ClientInterface } /** - * Execute export command on remote host + * Check if custom output is not empty + * + * @return bool + */ + private function isCustomOutput(): bool + { + return $this->customOutput !== null; + } + + /** + * Execute export command on remote host, it also will be used + * if "/export" command passed to query. + * + * @param string|null $arguments String with arguments which should be passed to export command * * @return string * @throws \RouterOS\Exceptions\ConfigException - * @throws \RuntimeException - * * @since 1.3.0 */ - public function export(): string + public function export(string $arguments = null): string { // Connect to remote host $connection = (new SSHConnection()) + ->timeout($this->config('timeout')) ->to($this->config('host')) ->onPort($this->config('ssh_port')) ->as($this->config('user') . '+etc') @@ -542,7 +564,7 @@ class Client implements Interfaces\ClientInterface ->connect(); // Run export command - $command = $connection->run('/export'); + $command = $connection->run('/export' . ' ' . $arguments); // Return the output return $command->getOutput(); diff --git a/src/Interfaces/ClientInterface.php b/src/Interfaces/ClientInterface.php index fede7cd..2b81e1e 100644 --- a/src/Interfaces/ClientInterface.php +++ b/src/Interfaces/ClientInterface.php @@ -26,33 +26,24 @@ interface ClientInterface * Reply ends with a complete !done or !fatal block (ended with 'empty line') * A !fatal block precedes TCP connexion close * - * @param bool $parse + * @param bool $parse If need parse output to array * * @return mixed */ - public function read(bool $parse); - - /** - * Send write query to RouterOS - * - * @param string|array|\RouterOS\Interfaces\QueryInterface $query - * - * @return \RouterOS\Interfaces\ClientInterface - * @throws \RouterOS\Exceptions\QueryException - * @deprecated - */ - public function write($query): ClientInterface; + public function read(bool $parse = true); /** * Send write query to RouterOS (modern version of write) * - * @param string|\RouterOS\Interfaces\QueryInterface $endpoint Path of API query or Query object - * @param array|null $where List of where filters - * @param string|null $operations Some operations which need make on response - * @param string|null $tag Mark query with tag + * @param array|string|\RouterOS\Interfaces\QueryInterface $endpoint Path of API query or Query object + * @param array|null $where List of where filters + * @param string|null $operations Some operations which need make on response + * @param string|null $tag Mark query with tag * * @return \RouterOS\Interfaces\ClientInterface * @throws \RouterOS\Exceptions\QueryException + * @throws \RouterOS\Exceptions\ClientException + * @throws \RouterOS\Exceptions\ConfigException * @since 1.0.0 */ public function query($endpoint, array $where, string $operations, string $tag): ClientInterface;