forked from kevinowino869/mitrobill
PHPMixBill v5.0 - First Upload
This commit is contained in:
819
system/autoload/PEAR2/Net/RouterOS/Client.php
Normal file
819
system/autoload/PEAR2/Net/RouterOS/Client.php
Normal file
@ -0,0 +1,819 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Refers to transmitter direction constants.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter\Stream as S;
|
||||
|
||||
/**
|
||||
* Refers to the cryptography constants.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter\NetworkStream as N;
|
||||
|
||||
/**
|
||||
* Catches arbitrary exceptions at some points.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* A RouterOS client.
|
||||
*
|
||||
* Provides functionality for easily communicating with a RouterOS host.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class Client
|
||||
{
|
||||
/**
|
||||
* Used in {@link static::isRequestActive()} to limit search only to
|
||||
* requests that have a callback.
|
||||
*/
|
||||
const FILTER_CALLBACK = 1;
|
||||
/**
|
||||
* Used in {@link static::isRequestActive()} to limit search only to
|
||||
* requests that use the buffer.
|
||||
*/
|
||||
const FILTER_BUFFER = 2;
|
||||
/**
|
||||
* Used in {@link static::isRequestActive()} to indicate no limit in search.
|
||||
*/
|
||||
const FILTER_ALL = 3;
|
||||
|
||||
/**
|
||||
* @var Communicator The communicator for this client.
|
||||
*/
|
||||
protected $com;
|
||||
|
||||
/**
|
||||
* @var int The number of currently pending requests.
|
||||
*/
|
||||
protected $pendingRequestsCount = 0;
|
||||
|
||||
/**
|
||||
* @var array An array of responses that have not yet been extracted or
|
||||
* passed to a callback. Key is the tag of the request, and the value
|
||||
* is an array of associated responses.
|
||||
*/
|
||||
protected $responseBuffer = array();
|
||||
|
||||
/**
|
||||
* @var array An array of callbacks to be executed as responses come.
|
||||
* Key is the tag of the request, and the value is the callback for it.
|
||||
*/
|
||||
protected $callbacks = array();
|
||||
|
||||
/**
|
||||
* @var Registry A registry for the operations. Particularly helpful at
|
||||
* persistent connections.
|
||||
*/
|
||||
protected $registry = null;
|
||||
|
||||
/**
|
||||
* @var bool Whether to stream future responses.
|
||||
*/
|
||||
private $_streamingResponses = false;
|
||||
|
||||
/**
|
||||
* Creates a new instance of a RouterOS API client.
|
||||
*
|
||||
* Creates a new instance of a RouterOS API client with the specified
|
||||
* settings.
|
||||
*
|
||||
* @param string $host Hostname (IP or domain) of the RouterOS server.
|
||||
* @param string $username The RouterOS username.
|
||||
* @param string $password The RouterOS password.
|
||||
* @param int|null $port The port on which the RouterOS server provides
|
||||
* the API service. You can also specify NULL, in which case the port
|
||||
* will automatically be chosen between 8728 and 8729, depending on the
|
||||
* value of $crypto.
|
||||
* @param bool $persist Whether or not the connection should be a
|
||||
* persistent one.
|
||||
* @param float $timeout The timeout for the connection.
|
||||
* @param string $crypto The encryption for this connection. Must be one
|
||||
* of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off
|
||||
* by default. RouterOS currently supports only TLS, but the setting is
|
||||
* provided in this fashion for forward compatibility's sake. And for
|
||||
* the sake of simplicity, if you specify an encryption, don't specify a
|
||||
* context and your default context uses the value "DEFAULT" for
|
||||
* ciphers, "ADH" will be automatically added to the list of ciphers.
|
||||
* @param resource $context A context for the socket.
|
||||
*
|
||||
* @see sendSync()
|
||||
* @see sendAsync()
|
||||
*/
|
||||
public function __construct(
|
||||
$host,
|
||||
$username,
|
||||
$password = '',
|
||||
$port = 8728,
|
||||
$persist = false,
|
||||
$timeout = null,
|
||||
$crypto = N::CRYPTO_OFF,
|
||||
$context = null
|
||||
) {
|
||||
$this->com = new Communicator(
|
||||
$host,
|
||||
$port,
|
||||
$persist,
|
||||
$timeout,
|
||||
$username . '/' . $password,
|
||||
$crypto,
|
||||
$context
|
||||
);
|
||||
$timeout = null == $timeout
|
||||
? ini_get('default_socket_timeout')
|
||||
: (int) $timeout;
|
||||
//Login the user if necessary
|
||||
if ((!$persist
|
||||
|| !($old = $this->com->getTransmitter()->lock(S::DIRECTION_ALL)))
|
||||
&& $this->com->getTransmitter()->isFresh()
|
||||
) {
|
||||
if (!static::login($this->com, $username, $password, $timeout)) {
|
||||
$this->com->close();
|
||||
throw new DataFlowException(
|
||||
'Invalid username or password supplied.',
|
||||
DataFlowException::CODE_INVALID_CREDENTIALS
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($old)) {
|
||||
$this->com->getTransmitter()->lock($old, true);
|
||||
}
|
||||
|
||||
if ($persist) {
|
||||
$this->registry = new Registry("{$host}:{$port}/{$username}");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A shorthand gateway.
|
||||
*
|
||||
* This is a magic PHP method that allows you to call the object as a
|
||||
* function. Depending on the argument given, one of the other functions in
|
||||
* the class is invoked and its returned value is returned by this function.
|
||||
*
|
||||
* @param mixed $arg Value can be either a {@link Request} to send, which
|
||||
* would be sent asynchoniously if it has a tag, and synchroniously if
|
||||
* not, a number to loop with or NULL to complete all pending requests.
|
||||
* Any other value is converted to string and treated as the tag of a
|
||||
* request to complete.
|
||||
*
|
||||
* @return mixed Whatever the long form function would have returned.
|
||||
*/
|
||||
public function __invoke($arg = null)
|
||||
{
|
||||
if (is_int($arg) || is_double($arg)) {
|
||||
return $this->loop($arg);
|
||||
} elseif ($arg instanceof Request) {
|
||||
return '' == $arg->getTag() ? $this->sendSync($arg)
|
||||
: $this->sendAsync($arg);
|
||||
} elseif (null === $arg) {
|
||||
return $this->completeRequest();
|
||||
}
|
||||
return $this->completeRequest((string) $arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to a RouterOS connection.
|
||||
*
|
||||
* @param Communicator $com The communicator to attempt to login to.
|
||||
* @param string $username The RouterOS username.
|
||||
* @param string $password The RouterOS password.
|
||||
* @param int|null $timeout The time to wait for each response. NULL
|
||||
* waits indefinetly.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public static function login(
|
||||
Communicator $com,
|
||||
$username,
|
||||
$password = '',
|
||||
$timeout = null
|
||||
) {
|
||||
if (null !== ($remoteCharset = $com->getCharset($com::CHARSET_REMOTE))
|
||||
&& null !== ($localCharset = $com->getCharset($com::CHARSET_LOCAL))
|
||||
) {
|
||||
$password = iconv(
|
||||
$localCharset,
|
||||
$remoteCharset . '//IGNORE//TRANSLIT',
|
||||
$password
|
||||
);
|
||||
}
|
||||
$old = null;
|
||||
try {
|
||||
if ($com->getTransmitter()->isPersistent()) {
|
||||
$old = $com->getTransmitter()->lock(S::DIRECTION_ALL);
|
||||
$result = self::_login($com, $username, $password, $timeout);
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
return $result;
|
||||
}
|
||||
return self::_login($com, $username, $password, $timeout);
|
||||
} catch (E $e) {
|
||||
if ($com->getTransmitter()->isPersistent() && null !== $old) {
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
}
|
||||
throw ($e instanceof NotSupportedException
|
||||
|| $e instanceof UnexpectedValueException
|
||||
|| !$com->getTransmitter()->isDataAwaiting()) ? new SocketException(
|
||||
'This is not a compatible RouterOS service',
|
||||
SocketException::CODE_SERVICE_INCOMPATIBLE,
|
||||
$e
|
||||
) : $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Login to a RouterOS connection.
|
||||
*
|
||||
* This is the actual login procedure, applied regardless of persistence and
|
||||
* charset settings.
|
||||
*
|
||||
* @param Communicator $com The communicator to attempt to login to.
|
||||
* @param string $username The RouterOS username.
|
||||
* @param string $password The RouterOS password. Potentially parsed
|
||||
* already by iconv.
|
||||
* @param int|null $timeout The time to wait for each response. NULL
|
||||
* waits indefinetly.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
private static function _login(
|
||||
Communicator $com,
|
||||
$username,
|
||||
$password = '',
|
||||
$timeout = null
|
||||
) {
|
||||
$request = new Request('/login');
|
||||
$request->send($com);
|
||||
$response = new Response($com, false, $timeout);
|
||||
$request->setArgument('name', $username);
|
||||
$request->setArgument(
|
||||
'response',
|
||||
'00' . md5(
|
||||
chr(0) . $password
|
||||
. pack('H*', $response->getProperty('ret'))
|
||||
)
|
||||
);
|
||||
$request->send($com);
|
||||
$response = new Response($com, false, $timeout);
|
||||
return $response->getType() === Response::TYPE_FINAL
|
||||
&& null === $response->getProperty('ret');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the charset(s) for this connection.
|
||||
*
|
||||
* Sets the charset(s) for this connection. The specified charset(s) will be
|
||||
* used for all future requests and responses. When sending,
|
||||
* {@link Communicator::CHARSET_LOCAL} is converted to
|
||||
* {@link Communicator::CHARSET_REMOTE}, and when receiving,
|
||||
* {@link Communicator::CHARSET_REMOTE} is converted to
|
||||
* {@link Communicator::CHARSET_LOCAL}. Setting NULL to either charset will
|
||||
* disable charset convertion, and data will be both sent and received "as
|
||||
* is".
|
||||
*
|
||||
* @param mixed $charset The charset to set. If $charsetType is
|
||||
* {@link Communicator::CHARSET_ALL}, you can supply either a string to
|
||||
* use for all charsets, or an array with the charset types as keys, and
|
||||
* the charsets as values.
|
||||
* @param int $charsetType Which charset to set. Valid values are the
|
||||
* Communicator::CHARSET_* constants. Any other value is treated as
|
||||
* {@link Communicator::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The old charset. If $charsetType is
|
||||
* {@link Communicator::CHARSET_ALL}, the old values will be returned as
|
||||
* an array with the types as keys, and charsets as values.
|
||||
* @see Communicator::setDefaultCharset()
|
||||
*/
|
||||
public function setCharset(
|
||||
$charset,
|
||||
$charsetType = Communicator::CHARSET_ALL
|
||||
) {
|
||||
return $this->com->setCharset($charset, $charsetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the charset(s) for this connection.
|
||||
*
|
||||
* @param int $charsetType Which charset to get. Valid values are the
|
||||
* Communicator::CHARSET_* constants. Any other value is treated as
|
||||
* {@link Communicator::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The current charset. If $charsetType is
|
||||
* {@link Communicator::CHARSET_ALL}, the current values will be
|
||||
* returned as an array with the types as keys, and charsets as values.
|
||||
* @see setCharset()
|
||||
*/
|
||||
public function getCharset($charsetType)
|
||||
{
|
||||
return $this->com->getCharset($charsetType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request and waits for responses.
|
||||
*
|
||||
* @param Request $request The request to send.
|
||||
* @param callback $callback Optional. A function that is to be executed
|
||||
* when new responses for this request are available. The callback takes
|
||||
* two parameters. The {@link Response} object as the first, and the
|
||||
* {@link Client} object as the second one. If the function returns
|
||||
* TRUE, the request is canceled. Note that the callback may be executed
|
||||
* one last time after that with a response that notifies about the
|
||||
* canceling.
|
||||
*
|
||||
* @return $this The client object.
|
||||
* @see completeRequest()
|
||||
* @see loop()
|
||||
* @see cancelRequest()
|
||||
*/
|
||||
public function sendAsync(Request $request, $callback = null)
|
||||
{
|
||||
//Error checking
|
||||
$tag = $request->getTag();
|
||||
if ('' == $tag) {
|
||||
throw new DataFlowException(
|
||||
'Asynchonous commands must have a tag.',
|
||||
DataFlowException::CODE_TAG_REQUIRED
|
||||
);
|
||||
}
|
||||
if ($this->isRequestActive($tag)) {
|
||||
throw new DataFlowException(
|
||||
'There must not be multiple active requests sharing a tag.',
|
||||
DataFlowException::CODE_TAG_UNIQUE
|
||||
);
|
||||
}
|
||||
if (null !== $callback && !is_callable($callback, true)) {
|
||||
throw new UnexpectedValueException(
|
||||
'Invalid callback provided.',
|
||||
UnexpectedValueException::CODE_CALLBACK_INVALID
|
||||
);
|
||||
}
|
||||
|
||||
$this->send($request);
|
||||
|
||||
if (null === $callback) {
|
||||
//Register the request at the buffer
|
||||
$this->responseBuffer[$tag] = array();
|
||||
} else {
|
||||
//Prepare the callback
|
||||
$this->callbacks[$tag] = $callback;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a request is active.
|
||||
*
|
||||
* Checks if a request is active. A request is considered active if it's a
|
||||
* pending request and/or has responses that are not yet extracted.
|
||||
*
|
||||
* @param string $tag The tag of the request to look for.
|
||||
* @param int $filter One of the FILTER_* consntants. Limits the search
|
||||
* to the specified places.
|
||||
*
|
||||
* @return bool TRUE if the request is active, FALSE otherwise.
|
||||
* @see getPendingRequestsCount()
|
||||
* @see completeRequest()
|
||||
*/
|
||||
public function isRequestActive($tag, $filter = self::FILTER_ALL)
|
||||
{
|
||||
$result = 0;
|
||||
if ($filter & self::FILTER_CALLBACK) {
|
||||
$result |= (int) array_key_exists($tag, $this->callbacks);
|
||||
}
|
||||
if ($filter & self::FILTER_BUFFER) {
|
||||
$result |= (int) array_key_exists($tag, $this->responseBuffer);
|
||||
}
|
||||
return 0 !== $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request and gets the full response.
|
||||
*
|
||||
* @param Request $request The request to send.
|
||||
*
|
||||
* @return ResponseCollection The received responses as a collection.
|
||||
* @see sendAsync()
|
||||
* @see close()
|
||||
*/
|
||||
public function sendSync(Request $request)
|
||||
{
|
||||
$tag = $request->getTag();
|
||||
if ('' == $tag) {
|
||||
$this->send($request);
|
||||
} else {
|
||||
$this->sendAsync($request);
|
||||
}
|
||||
return $this->completeRequest($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Completes a specified request.
|
||||
*
|
||||
* Starts an event loop for the RouterOS callbacks and finishes when a
|
||||
* specified request is completed.
|
||||
*
|
||||
* @param string $tag The tag of the request to complete. Setting NULL
|
||||
* completes all requests.
|
||||
*
|
||||
* @return ResponseCollection A collection of {@link Response} objects that
|
||||
* haven't been passed to a callback function or previously extracted
|
||||
* with {@link static::extractNewResponses()}. Returns an empty
|
||||
* collection when $tag is set to NULL (responses can still be
|
||||
* extracted).
|
||||
*/
|
||||
public function completeRequest($tag = null)
|
||||
{
|
||||
$hasNoTag = '' == $tag;
|
||||
$result = $hasNoTag ? array()
|
||||
: $this->extractNewResponses($tag)->toArray();
|
||||
while ((!$hasNoTag && $this->isRequestActive($tag))
|
||||
|| ($hasNoTag && 0 !== $this->getPendingRequestsCount())
|
||||
) {
|
||||
$newReply = $this->dispatchNextResponse(null);
|
||||
if ($newReply->getTag() === $tag) {
|
||||
if ($hasNoTag) {
|
||||
$result[] = $newReply;
|
||||
}
|
||||
if ($newReply->getType() === Response::TYPE_FINAL) {
|
||||
if (!$hasNoTag) {
|
||||
$result = array_merge(
|
||||
$result,
|
||||
$this->isRequestActive($tag)
|
||||
? $this->extractNewResponses($tag)->toArray()
|
||||
: array()
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ResponseCollection($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts responses for a request.
|
||||
*
|
||||
* Gets all new responses for a request that haven't been passed to a
|
||||
* callback and clears the buffer from them.
|
||||
*
|
||||
* @param string $tag The tag of the request to extract new responses for.
|
||||
* Specifying NULL with extract new responses for all requests.
|
||||
*
|
||||
* @return ResponseCollection A collection of {@link Response} objects for
|
||||
* the specified request.
|
||||
* @see loop()
|
||||
*/
|
||||
public function extractNewResponses($tag = null)
|
||||
{
|
||||
if (null === $tag) {
|
||||
$result = array();
|
||||
foreach (array_keys($this->responseBuffer) as $tag) {
|
||||
$result = array_merge(
|
||||
$result,
|
||||
$this->extractNewResponses($tag)->toArray()
|
||||
);
|
||||
}
|
||||
return new ResponseCollection($result);
|
||||
} elseif ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
|
||||
return new ResponseCollection(array());
|
||||
} elseif ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
|
||||
$result = $this->responseBuffer[$tag];
|
||||
if (!empty($result)) {
|
||||
if (end($result)->getType() === Response::TYPE_FINAL) {
|
||||
unset($this->responseBuffer[$tag]);
|
||||
} else {
|
||||
$this->responseBuffer[$tag] = array();
|
||||
}
|
||||
}
|
||||
return new ResponseCollection($result);
|
||||
} else {
|
||||
throw new DataFlowException(
|
||||
'No such request, or the request has already finished.',
|
||||
DataFlowException::CODE_UNKNOWN_REQUEST
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an event loop for the RouterOS callbacks.
|
||||
*
|
||||
* Starts an event loop for the RouterOS callbacks and finishes when there
|
||||
* are no more pending requests or when a specified timeout has passed
|
||||
* (whichever comes first).
|
||||
*
|
||||
* @param int $sTimeout Timeout for the loop. If NULL, there is no time
|
||||
* limit.
|
||||
* @param int $usTimeout Microseconds to add to the time limit.
|
||||
*
|
||||
* @return bool TRUE when there are any more pending requests, FALSE
|
||||
* otherwise.
|
||||
* @see extractNewResponses()
|
||||
* @see getPendingRequestsCount()
|
||||
*/
|
||||
public function loop($sTimeout = null, $usTimeout = 0)
|
||||
{
|
||||
try {
|
||||
if (null === $sTimeout) {
|
||||
while ($this->getPendingRequestsCount() !== 0) {
|
||||
$this->dispatchNextResponse(null);
|
||||
}
|
||||
} else {
|
||||
list($usStart, $sStart) = explode(' ', microtime());
|
||||
while ($this->getPendingRequestsCount() !== 0
|
||||
&& ($sTimeout >= 0 || $usTimeout >= 0)
|
||||
) {
|
||||
$this->dispatchNextResponse($sTimeout, $usTimeout);
|
||||
list($usEnd, $sEnd) = explode(' ', microtime());
|
||||
|
||||
$sTimeout -= $sEnd - $sStart;
|
||||
$usTimeout -= $usEnd - $usStart;
|
||||
if ($usTimeout <= 0) {
|
||||
if ($sTimeout > 0) {
|
||||
$usTimeout = 1000000 + $usTimeout;
|
||||
$sTimeout--;
|
||||
}
|
||||
}
|
||||
|
||||
$sStart = $sEnd;
|
||||
$usStart = $usEnd;
|
||||
}
|
||||
}
|
||||
} catch (SocketException $e) {
|
||||
if ($e->getCode() !== SocketException::CODE_NO_DATA) {
|
||||
// @codeCoverageIgnoreStart
|
||||
// It's impossible to reliably cause any other SocketException.
|
||||
// This line is only here in case the unthinkable happens:
|
||||
// The connection terminates just after it was supposedly
|
||||
// about to send back some data.
|
||||
throw $e;
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
||||
}
|
||||
return $this->getPendingRequestsCount() !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of pending requests.
|
||||
*
|
||||
* @return int The number of pending requests.
|
||||
* @see isRequestActive()
|
||||
*/
|
||||
public function getPendingRequestsCount()
|
||||
{
|
||||
return $this->pendingRequestsCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a request.
|
||||
*
|
||||
* Cancels an active request. Using this function in favor of a plain call
|
||||
* to the "/cancel" command is highly reccomended, as it also updates the
|
||||
* counter of pending requests properly. Note that canceling a request also
|
||||
* removes any responses for it that were not previously extracted with
|
||||
* {@link static::extractNewResponses()}.
|
||||
*
|
||||
* @param string $tag Tag of the request to cancel. Setting NULL will cancel
|
||||
* all requests.
|
||||
*
|
||||
* @return $this The client object.
|
||||
* @see sendAsync()
|
||||
* @see close()
|
||||
*/
|
||||
public function cancelRequest($tag = null)
|
||||
{
|
||||
$cancelRequest = new Request('/cancel');
|
||||
$hasTag = !('' == $tag);
|
||||
$hasReg = null !== $this->registry;
|
||||
if ($hasReg && !$hasTag) {
|
||||
$tags = array_merge(
|
||||
array_keys($this->responseBuffer),
|
||||
array_keys($this->callbacks)
|
||||
);
|
||||
$this->registry->setTaglessMode(true);
|
||||
foreach ($tags as $t) {
|
||||
$cancelRequest->setArgument(
|
||||
'tag',
|
||||
$this->registry->getOwnershipTag() . $t
|
||||
);
|
||||
$this->sendSync($cancelRequest);
|
||||
}
|
||||
$this->registry->setTaglessMode(false);
|
||||
} else {
|
||||
if ($hasTag) {
|
||||
if ($this->isRequestActive($tag)) {
|
||||
if ($hasReg) {
|
||||
$this->registry->setTaglessMode(true);
|
||||
$cancelRequest->setArgument(
|
||||
'tag',
|
||||
$this->registry->getOwnershipTag() . $tag
|
||||
);
|
||||
} else {
|
||||
$cancelRequest->setArgument('tag', $tag);
|
||||
}
|
||||
} else {
|
||||
throw new DataFlowException(
|
||||
'No such request. Canceling aborted.',
|
||||
DataFlowException::CODE_CANCEL_FAIL
|
||||
);
|
||||
}
|
||||
}
|
||||
$this->sendSync($cancelRequest);
|
||||
if ($hasReg) {
|
||||
$this->registry->setTaglessMode(false);
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasTag) {
|
||||
if ($this->isRequestActive($tag, self::FILTER_BUFFER)) {
|
||||
$this->responseBuffer[$tag] = $this->completeRequest($tag);
|
||||
} else {
|
||||
$this->completeRequest($tag);
|
||||
}
|
||||
} else {
|
||||
$this->loop();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets response streaming setting.
|
||||
*
|
||||
* Sets whether future responses are streamed. If responses are streamed,
|
||||
* the argument values are returned as streams instead of strings. This is
|
||||
* particularly useful if you expect a response that may contain one or more
|
||||
* very large words.
|
||||
*
|
||||
* @param bool $streamingResponses Whether to stream future responses.
|
||||
*
|
||||
* @return bool The previous value of the setting.
|
||||
* @see isStreamingResponses()
|
||||
*/
|
||||
public function setStreamingResponses($streamingResponses)
|
||||
{
|
||||
$oldValue = $this->_streamingResponses;
|
||||
$this->_streamingResponses = (bool) $streamingResponses;
|
||||
return $oldValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets response streaming setting.
|
||||
*
|
||||
* Gets whether future responses are streamed.
|
||||
*
|
||||
* @return bool The value of the setting.
|
||||
* @see setStreamingResponses()
|
||||
*/
|
||||
public function isStreamingResponses()
|
||||
{
|
||||
return $this->_streamingResponses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the opened connection, even if it is a persistent one.
|
||||
*
|
||||
* Closes the opened connection, even if it is a persistent one. Note that
|
||||
* {@link static::extractNewResponses()} can still be used to extract
|
||||
* responses collected prior to the closing.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$result = true;
|
||||
/*
|
||||
* The check below is done because for some unknown reason
|
||||
* (either a PHP or a RouterOS bug) calling "/quit" on an encrypted
|
||||
* connection makes one end hang.
|
||||
*
|
||||
* Since encrypted connections only appeared in RouterOS 6.1, and
|
||||
* the "/quit" call is needed for all <6.0 versions, problems due
|
||||
* to its absence should be limited to some earlier 6.* versions
|
||||
* on some RouterBOARD devices.
|
||||
*/
|
||||
if ($this->com->getTransmitter()->getCrypto() === N::CRYPTO_OFF) {
|
||||
if (null !== $this->registry) {
|
||||
$this->registry->setTaglessMode(true);
|
||||
}
|
||||
try {
|
||||
$response = $this->sendSync(new Request('/quit'));
|
||||
$result = $response[0]->getType() === Response::TYPE_FATAL;
|
||||
} catch (SocketException $e) {
|
||||
$result
|
||||
= $e->getCode() === SocketException::CODE_REQUEST_SEND_FAIL;
|
||||
} catch (E $e) {
|
||||
//Ignore unknown errors.
|
||||
}
|
||||
if (null !== $this->registry) {
|
||||
$this->registry->setTaglessMode(false);
|
||||
}
|
||||
}
|
||||
$result = $result && $this->com->close();
|
||||
$this->callbacks = array();
|
||||
$this->pendingRequestsCount = 0;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection, unless it's a persistent one.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
if ($this->com->getTransmitter()->isPersistent()) {
|
||||
if (0 !== $this->pendingRequestsCount) {
|
||||
$this->cancelRequest();
|
||||
}
|
||||
} else {
|
||||
$this->close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request to RouterOS.
|
||||
*
|
||||
* @param Request $request The request to send.
|
||||
*
|
||||
* @return $this The client object.
|
||||
* @see sendSync()
|
||||
* @see sendAsync()
|
||||
*/
|
||||
protected function send(Request $request)
|
||||
{
|
||||
$request->send($this->com, $this->registry);
|
||||
$this->pendingRequestsCount++;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches the next response in queue.
|
||||
*
|
||||
* Dispatches the next response in queue, i.e. it executes the associated
|
||||
* callback if there is one, or places the response in the response buffer.
|
||||
*
|
||||
* @param int $sTimeout If a response is not immediatly available, wait
|
||||
* this many seconds. If NULL, wait indefinetly.
|
||||
* @param int $usTimeout Microseconds to add to the waiting time.
|
||||
*
|
||||
* @throws SocketException When there's no response within the time limit.
|
||||
* @return Response The dispatched response.
|
||||
*/
|
||||
protected function dispatchNextResponse($sTimeout = 0, $usTimeout = 0)
|
||||
{
|
||||
$response = new Response(
|
||||
$this->com,
|
||||
$this->_streamingResponses,
|
||||
$sTimeout,
|
||||
$usTimeout,
|
||||
$this->registry
|
||||
);
|
||||
if ($response->getType() === Response::TYPE_FATAL) {
|
||||
$this->pendingRequestsCount = 0;
|
||||
$this->com->close();
|
||||
return $response;
|
||||
}
|
||||
|
||||
$tag = $response->getTag();
|
||||
$isLastForRequest = $response->getType() === Response::TYPE_FINAL;
|
||||
if ($isLastForRequest) {
|
||||
$this->pendingRequestsCount--;
|
||||
}
|
||||
|
||||
if ('' != $tag) {
|
||||
if ($this->isRequestActive($tag, self::FILTER_CALLBACK)) {
|
||||
if ($this->callbacks[$tag]($response, $this)) {
|
||||
$this->cancelRequest($tag);
|
||||
} elseif ($isLastForRequest) {
|
||||
unset($this->callbacks[$tag]);
|
||||
}
|
||||
} else {
|
||||
$this->responseBuffer[$tag][] = $response;
|
||||
}
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
671
system/autoload/PEAR2/Net/RouterOS/Communicator.php
Normal file
671
system/autoload/PEAR2/Net/RouterOS/Communicator.php
Normal file
@ -0,0 +1,671 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Using transmitters.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter as T;
|
||||
|
||||
/**
|
||||
* A RouterOS communicator.
|
||||
*
|
||||
* Implementation of the RouterOS API protocol. Unlike the other classes in this
|
||||
* package, this class doesn't provide any conviniences beyond the low level
|
||||
* implementation details (automatic word length encoding/decoding, charset
|
||||
* translation and data integrity), and because of that, its direct usage is
|
||||
* strongly discouraged.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
* @see Client
|
||||
*/
|
||||
class Communicator
|
||||
{
|
||||
/**
|
||||
* Used when getting/setting all (default) charsets.
|
||||
*/
|
||||
const CHARSET_ALL = -1;
|
||||
|
||||
/**
|
||||
* Used when getting/setting the (default) remote charset.
|
||||
*
|
||||
* The remote charset is the charset in which RouterOS stores its data.
|
||||
* If you want to keep compatibility with your Winbox, this charset should
|
||||
* match the default charset from your Windows' regional settings.
|
||||
*/
|
||||
const CHARSET_REMOTE = 0;
|
||||
|
||||
/**
|
||||
* Used when getting/setting the (default) local charset.
|
||||
*
|
||||
* The local charset is the charset in which the data from RouterOS will be
|
||||
* returned as. This charset should match the charset of the place the data
|
||||
* will eventually be written to.
|
||||
*/
|
||||
const CHARSET_LOCAL = 1;
|
||||
|
||||
/**
|
||||
* @var array An array with the default charset types as keys, and the
|
||||
* default charsets as values.
|
||||
*/
|
||||
protected static $defaultCharsets = array(
|
||||
self::CHARSET_REMOTE => null,
|
||||
self::CHARSET_LOCAL => null
|
||||
);
|
||||
|
||||
/**
|
||||
* @var array An array with the current charset types as keys, and the
|
||||
* current charsets as values.
|
||||
*/
|
||||
protected $charsets = array();
|
||||
|
||||
/**
|
||||
* @var T\TcpClient The transmitter for the connection.
|
||||
*/
|
||||
protected $trans;
|
||||
|
||||
/**
|
||||
* Creates a new connection with the specified options.
|
||||
*
|
||||
* @param string $host Hostname (IP or domain) of the RouterOS server.
|
||||
* @param int|null $port The port on which the RouterOS server provides
|
||||
* the API service. You can also specify NULL, in which case the port
|
||||
* will automatically be chosen between 8728 and 8729, depending on the
|
||||
* value of $crypto.
|
||||
* @param bool $persist Whether or not the connection should be a
|
||||
* persistent one.
|
||||
* @param float $timeout The timeout for the connection.
|
||||
* @param string $key A string that uniquely identifies the
|
||||
* connection.
|
||||
* @param string $crypto The encryption for this connection. Must be one
|
||||
* of the PEAR2\Net\Transmitter\NetworkStream::CRYPTO_* constants. Off
|
||||
* by default. RouterOS currently supports only TLS, but the setting is
|
||||
* provided in this fashion for forward compatibility's sake. And for
|
||||
* the sake of simplicity, if you specify an encryption, don't specify a
|
||||
* context and your default context uses the value "DEFAULT" for
|
||||
* ciphers, "ADH" will be automatically added to the list of ciphers.
|
||||
* @param resource $context A context for the socket.
|
||||
*
|
||||
* @see sendWord()
|
||||
*/
|
||||
public function __construct(
|
||||
$host,
|
||||
$port = 8728,
|
||||
$persist = false,
|
||||
$timeout = null,
|
||||
$key = '',
|
||||
$crypto = T\NetworkStream::CRYPTO_OFF,
|
||||
$context = null
|
||||
) {
|
||||
$isUnencrypted = T\NetworkStream::CRYPTO_OFF === $crypto;
|
||||
if (($context === null) && !$isUnencrypted) {
|
||||
$context = stream_context_get_default();
|
||||
$opts = stream_context_get_options($context);
|
||||
if (!isset($opts['ssl']['ciphers'])
|
||||
|| 'DEFAULT' === $opts['ssl']['ciphers']
|
||||
) {
|
||||
stream_context_set_option($context, 'ssl', 'ciphers', 'ADH');
|
||||
}
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
// The $port is customizable in testing.
|
||||
if (null === $port) {
|
||||
$port = $isUnencrypted ? 8728 : 8729;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
try {
|
||||
$this->trans = new T\TcpClient(
|
||||
$host,
|
||||
$port,
|
||||
$persist,
|
||||
$timeout,
|
||||
$key,
|
||||
$crypto,
|
||||
$context
|
||||
);
|
||||
} catch (T\Exception $e) {
|
||||
throw new SocketException(
|
||||
'Error connecting to RouterOS',
|
||||
SocketException::CODE_CONNECTION_FAIL,
|
||||
$e
|
||||
);
|
||||
}
|
||||
$this->setCharset(
|
||||
self::getDefaultCharset(self::CHARSET_ALL),
|
||||
self::CHARSET_ALL
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* A shorthand gateway.
|
||||
*
|
||||
* This is a magic PHP method that allows you to call the object as a
|
||||
* function. Depending on the argument given, one of the other functions in
|
||||
* the class is invoked and its returned value is returned by this function.
|
||||
*
|
||||
* @param string $string A string of the word to send, or NULL to get the
|
||||
* next word as a string.
|
||||
*
|
||||
* @return int|string If a string is provided, returns the number of bytes
|
||||
* sent, otherwise retuns the next word as a string.
|
||||
*/
|
||||
public function __invoke($string = null)
|
||||
{
|
||||
return null === $string ? $this->getNextWord()
|
||||
: $this->sendWord($string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a variable is a seekable stream resource.
|
||||
*
|
||||
* @param mixed $var The value to check.
|
||||
*
|
||||
* @return bool TRUE if $var is a seekable stream, FALSE otherwise.
|
||||
*/
|
||||
public static function isSeekableStream($var)
|
||||
{
|
||||
if (T\Stream::isStream($var)) {
|
||||
$meta = stream_get_meta_data($var);
|
||||
return $meta['seekable'];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses iconv to convert a stream from one charset to another.
|
||||
*
|
||||
* @param string $inCharset The charset of the stream.
|
||||
* @param string $outCharset The desired resulting charset.
|
||||
* @param resource $stream The stream to convert. The stream is assumed
|
||||
* to be seekable, and is read from its current position to its end,
|
||||
* after which, it is seeked back to its starting position.
|
||||
*
|
||||
* @return resource A new stream that uses the $out_charset. The stream is a
|
||||
* subset from the original stream, from its current position to its
|
||||
* end, seeked at its start.
|
||||
*/
|
||||
public static function iconvStream($inCharset, $outCharset, $stream)
|
||||
{
|
||||
$bytes = 0;
|
||||
$result = fopen('php://temp', 'r+b');
|
||||
$iconvFilter = stream_filter_append(
|
||||
$result,
|
||||
'convert.iconv.' . $inCharset . '.' . $outCharset,
|
||||
STREAM_FILTER_WRITE
|
||||
);
|
||||
|
||||
flock($stream, LOCK_SH);
|
||||
while (!feof($stream)) {
|
||||
$bytes += stream_copy_to_stream($stream, $result, 0xFFFFF);
|
||||
}
|
||||
fseek($stream, -$bytes, SEEK_CUR);
|
||||
flock($stream, LOCK_UN);
|
||||
|
||||
stream_filter_remove($iconvFilter);
|
||||
rewind($result);
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default charset(s) for new connections.
|
||||
*
|
||||
* @param mixed $charset The charset to set. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, you can supply either a string to use for
|
||||
* all charsets, or an array with the charset types as keys, and the
|
||||
* charsets as values.
|
||||
* @param int $charsetType Which charset to set. Valid values are the
|
||||
* CHARSET_* constants. Any other value is treated as
|
||||
* {@link self::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The old charset. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, the old values will be returned as an
|
||||
* array with the types as keys, and charsets as values.
|
||||
* @see setCharset()
|
||||
*/
|
||||
public static function setDefaultCharset(
|
||||
$charset,
|
||||
$charsetType = self::CHARSET_ALL
|
||||
) {
|
||||
if (array_key_exists($charsetType, self::$defaultCharsets)) {
|
||||
$oldCharset = self::$defaultCharsets[$charsetType];
|
||||
self::$defaultCharsets[$charsetType] = $charset;
|
||||
return $oldCharset;
|
||||
} else {
|
||||
$oldCharsets = self::$defaultCharsets;
|
||||
self::$defaultCharsets = is_array($charset) ? $charset : array_fill(
|
||||
0,
|
||||
count(self::$defaultCharsets),
|
||||
$charset
|
||||
);
|
||||
return $oldCharsets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default charset(s).
|
||||
*
|
||||
* @param int $charsetType Which charset to get. Valid values are the
|
||||
* CHARSET_* constants. Any other value is treated as
|
||||
* {@link self::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The current charset. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, the current values will be returned as an
|
||||
* array with the types as keys, and charsets as values.
|
||||
* @see setDefaultCharset()
|
||||
*/
|
||||
public static function getDefaultCharset($charsetType)
|
||||
{
|
||||
return array_key_exists($charsetType, self::$defaultCharsets)
|
||||
? self::$defaultCharsets[$charsetType] : self::$defaultCharsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length of a seekable stream.
|
||||
*
|
||||
* Gets the length of a seekable stream.
|
||||
*
|
||||
* @param resource $stream The stream to check. The stream is assumed to be
|
||||
* seekable.
|
||||
*
|
||||
* @return double The number of bytes in the stream between its current
|
||||
* position and its end.
|
||||
*/
|
||||
public static function seekableStreamLength($stream)
|
||||
{
|
||||
$streamPosition = (double) sprintf('%u', ftell($stream));
|
||||
fseek($stream, 0, SEEK_END);
|
||||
$streamLength = ((double) sprintf('%u', ftell($stream)))
|
||||
- $streamPosition;
|
||||
fseek($stream, $streamPosition, SEEK_SET);
|
||||
return $streamLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the charset(s) for this connection.
|
||||
*
|
||||
* Sets the charset(s) for this connection. The specified charset(s) will be
|
||||
* used for all future words. When sending, {@link self::CHARSET_LOCAL} is
|
||||
* converted to {@link self::CHARSET_REMOTE}, and when receiving,
|
||||
* {@link self::CHARSET_REMOTE} is converted to {@link self::CHARSET_LOCAL}.
|
||||
* Setting NULL to either charset will disable charset convertion, and data
|
||||
* will be both sent and received "as is".
|
||||
*
|
||||
* @param mixed $charset The charset to set. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, you can supply either a string to use for
|
||||
* all charsets, or an array with the charset types as keys, and the
|
||||
* charsets as values.
|
||||
* @param int $charsetType Which charset to set. Valid values are the
|
||||
* CHARSET_* constants. Any other value is treated as
|
||||
* {@link self::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The old charset. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, the old values will be returned as an
|
||||
* array with the types as keys, and charsets as values.
|
||||
* @see setDefaultCharset()
|
||||
*/
|
||||
public function setCharset($charset, $charsetType = self::CHARSET_ALL)
|
||||
{
|
||||
if (array_key_exists($charsetType, $this->charsets)) {
|
||||
$oldCharset = $this->charsets[$charsetType];
|
||||
$this->charsets[$charsetType] = $charset;
|
||||
return $oldCharset;
|
||||
} else {
|
||||
$oldCharsets = $this->charsets;
|
||||
$this->charsets = is_array($charset) ? $charset : array_fill(
|
||||
0,
|
||||
count($this->charsets),
|
||||
$charset
|
||||
);
|
||||
return $oldCharsets;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the charset(s) for this connection.
|
||||
*
|
||||
* @param int $charsetType Which charset to get. Valid values are the
|
||||
* CHARSET_* constants. Any other value is treated as
|
||||
* {@link self::CHARSET_ALL}.
|
||||
*
|
||||
* @return string|array The current charset. If $charsetType is
|
||||
* {@link self::CHARSET_ALL}, the current values will be returned as an
|
||||
* array with the types as keys, and charsets as values.
|
||||
* @see getDefaultCharset()
|
||||
* @see setCharset()
|
||||
*/
|
||||
public function getCharset($charsetType)
|
||||
{
|
||||
return array_key_exists($charsetType, $this->charsets)
|
||||
? $this->charsets[$charsetType] : $this->charsets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the transmitter for this connection.
|
||||
*
|
||||
* @return T\TcpClient The transmitter for this connection.
|
||||
*/
|
||||
public function getTransmitter()
|
||||
{
|
||||
return $this->trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a word.
|
||||
*
|
||||
* Sends a word and automatically encodes its length when doing so.
|
||||
*
|
||||
* @param string $word The word to send.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
* @see sendWordFromStream()
|
||||
* @see getNextWord()
|
||||
*/
|
||||
public function sendWord($word)
|
||||
{
|
||||
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
|
||||
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
|
||||
) {
|
||||
$word = iconv(
|
||||
$localCharset,
|
||||
$remoteCharset . '//IGNORE//TRANSLIT',
|
||||
$word
|
||||
);
|
||||
}
|
||||
$length = strlen($word);
|
||||
static::verifyLengthSupport($length);
|
||||
if ($this->trans->isPersistent()) {
|
||||
$old = $this->trans->lock(T\Stream::DIRECTION_SEND);
|
||||
$bytes = $this->trans->send(self::encodeLength($length) . $word);
|
||||
$this->trans->lock($old, true);
|
||||
return $bytes;
|
||||
}
|
||||
return $this->trans->send(self::encodeLength($length) . $word);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a word based on a stream.
|
||||
*
|
||||
* Sends a word based on a stream and automatically encodes its length when
|
||||
* doing so. The stream is read from its current position to its end, and
|
||||
* then returned to its current position. Because of those operations, the
|
||||
* supplied stream must be seekable.
|
||||
*
|
||||
* @param string $prefix A string to prepend before the stream contents.
|
||||
* @param resource $stream The seekable stream to send.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
* @see sendWord()
|
||||
*/
|
||||
public function sendWordFromStream($prefix, $stream)
|
||||
{
|
||||
if (!self::isSeekableStream($stream)) {
|
||||
throw new InvalidArgumentException(
|
||||
'The stream must be seekable.',
|
||||
InvalidArgumentException::CODE_SEEKABLE_REQUIRED
|
||||
);
|
||||
}
|
||||
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
|
||||
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
|
||||
) {
|
||||
$prefix = iconv(
|
||||
$localCharset,
|
||||
$remoteCharset . '//IGNORE//TRANSLIT',
|
||||
$prefix
|
||||
);
|
||||
$stream = self::iconvStream(
|
||||
$localCharset,
|
||||
$remoteCharset . '//IGNORE//TRANSLIT',
|
||||
$stream
|
||||
);
|
||||
}
|
||||
|
||||
flock($stream, LOCK_SH);
|
||||
$totalLength = strlen($prefix) + self::seekableStreamLength($stream);
|
||||
static::verifyLengthSupport($totalLength);
|
||||
|
||||
$bytes = $this->trans->send(self::encodeLength($totalLength) . $prefix);
|
||||
$bytes += $this->trans->send($stream);
|
||||
|
||||
flock($stream, LOCK_UN);
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the length is supported.
|
||||
*
|
||||
* Verifies if the specified length is supported by the API. Throws a
|
||||
* {@link LengthException} if that's not the case. Currently, RouterOS
|
||||
* supports words up to 0xFFFFFFFF in length, so that's the only check
|
||||
* performed.
|
||||
*
|
||||
* @param int $length The length to verify.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected static function verifyLengthSupport($length)
|
||||
{
|
||||
if ($length > 0xFFFFFFFF) {
|
||||
throw new LengthException(
|
||||
'Words with length above 0xFFFFFFFF are not supported.',
|
||||
LengthException::CODE_UNSUPPORTED,
|
||||
null,
|
||||
$length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the length as requred by the RouterOS API.
|
||||
*
|
||||
* @param int $length The length to encode.
|
||||
*
|
||||
* @return string The encoded length.
|
||||
*/
|
||||
public static function encodeLength($length)
|
||||
{
|
||||
if ($length < 0) {
|
||||
throw new LengthException(
|
||||
'Length must not be negative.',
|
||||
LengthException::CODE_INVALID,
|
||||
null,
|
||||
$length
|
||||
);
|
||||
} elseif ($length < 0x80) {
|
||||
return chr($length);
|
||||
} elseif ($length < 0x4000) {
|
||||
return pack('n', $length |= 0x8000);
|
||||
} elseif ($length < 0x200000) {
|
||||
$length |= 0xC00000;
|
||||
return pack('n', $length >> 8) . chr($length & 0xFF);
|
||||
} elseif ($length < 0x10000000) {
|
||||
return pack('N', $length |= 0xE0000000);
|
||||
} elseif ($length <= 0xFFFFFFFF) {
|
||||
return chr(0xF0) . pack('N', $length);
|
||||
} elseif ($length <= 0x7FFFFFFFF) {
|
||||
$length = 'f' . base_convert($length, 10, 16);
|
||||
return chr(hexdec(substr($length, 0, 2))) .
|
||||
pack('N', hexdec(substr($length, 2)));
|
||||
}
|
||||
throw new LengthException(
|
||||
'Length must not be above 0x7FFFFFFFF.',
|
||||
LengthException::CODE_BEYOND_SHEME,
|
||||
null,
|
||||
$length
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next word in queue as a string.
|
||||
*
|
||||
* Get the next word in queue as a string, after automatically decoding its
|
||||
* length.
|
||||
*
|
||||
* @return string The word.
|
||||
* @see close()
|
||||
*/
|
||||
public function getNextWord()
|
||||
{
|
||||
if ($this->trans->isPersistent()) {
|
||||
$old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE);
|
||||
$word = $this->trans->receive(
|
||||
self::decodeLength($this->trans),
|
||||
'word'
|
||||
);
|
||||
$this->trans->lock($old, true);
|
||||
} else {
|
||||
$word = $this->trans->receive(
|
||||
self::decodeLength($this->trans),
|
||||
'word'
|
||||
);
|
||||
}
|
||||
|
||||
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
|
||||
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
|
||||
) {
|
||||
$word = iconv(
|
||||
$remoteCharset,
|
||||
$localCharset . '//IGNORE//TRANSLIT',
|
||||
$word
|
||||
);
|
||||
}
|
||||
|
||||
return $word;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next word in queue as a stream.
|
||||
*
|
||||
* Get the next word in queue as a stream, after automatically decoding its
|
||||
* length.
|
||||
*
|
||||
* @return resource The word, as a stream.
|
||||
* @see close()
|
||||
*/
|
||||
public function getNextWordAsStream()
|
||||
{
|
||||
$filters = new T\FilterCollection();
|
||||
if (null !== ($remoteCharset = $this->getCharset(self::CHARSET_REMOTE))
|
||||
&& null !== ($localCharset = $this->getCharset(self::CHARSET_LOCAL))
|
||||
) {
|
||||
$filters->append(
|
||||
'convert.iconv.' .
|
||||
$remoteCharset . '.' . $localCharset . '//IGNORE//TRANSLIT'
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->trans->isPersistent()) {
|
||||
$old = $this->trans->lock(T\Stream::DIRECTION_RECEIVE);
|
||||
$stream = $this->trans->receiveStream(
|
||||
self::decodeLength($this->trans),
|
||||
$filters,
|
||||
'stream word'
|
||||
);
|
||||
$this->trans->lock($old, true);
|
||||
} else {
|
||||
$stream = $this->trans->receiveStream(
|
||||
self::decodeLength($this->trans),
|
||||
$filters,
|
||||
'stream word'
|
||||
);
|
||||
}
|
||||
|
||||
return $stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the lenght of the incoming message.
|
||||
*
|
||||
* Decodes the lenght of the incoming message, as specified by the RouterOS
|
||||
* API.
|
||||
*
|
||||
* @param T\Stream $trans The transmitter from which to decode the length of
|
||||
* the incoming message.
|
||||
*
|
||||
* @return int The decoded length.
|
||||
*/
|
||||
public static function decodeLength(T\Stream $trans)
|
||||
{
|
||||
if ($trans->isPersistent() && $trans instanceof T\TcpClient) {
|
||||
$old = $trans->lock($trans::DIRECTION_RECEIVE);
|
||||
$length = self::_decodeLength($trans);
|
||||
$trans->lock($old, true);
|
||||
return $length;
|
||||
}
|
||||
return self::_decodeLength($trans);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes the lenght of the incoming message.
|
||||
*
|
||||
* Decodes the lenght of the incoming message, as specified by the RouterOS
|
||||
* API.
|
||||
*
|
||||
* Difference with the non private function is that this one doesn't perform
|
||||
* locking if the connection is a persistent one.
|
||||
*
|
||||
* @param T\Stream $trans The transmitter from which to decode the length of
|
||||
* the incoming message.
|
||||
*
|
||||
* @return int The decoded length.
|
||||
*/
|
||||
private static function _decodeLength(T\Stream $trans)
|
||||
{
|
||||
$byte = ord($trans->receive(1, 'initial length byte'));
|
||||
if ($byte & 0x80) {
|
||||
if (($byte & 0xC0) === 0x80) {
|
||||
return (($byte & 077) << 8 ) + ord($trans->receive(1));
|
||||
} elseif (($byte & 0xE0) === 0xC0) {
|
||||
$rem = unpack('n~', $trans->receive(2));
|
||||
return (($byte & 037) << 16 ) + $rem['~'];
|
||||
} elseif (($byte & 0xF0) === 0xE0) {
|
||||
$rem = unpack('n~/C~~', $trans->receive(3));
|
||||
return (($byte & 017) << 24 ) + ($rem['~'] << 8) + $rem['~~'];
|
||||
} elseif (($byte & 0xF8) === 0xF0) {
|
||||
$rem = unpack('N~', $trans->receive(4));
|
||||
return (($byte & 007) * 0x100000000/* '<< 32' or '2^32' */)
|
||||
+ (double) sprintf('%u', $rem['~']);
|
||||
}
|
||||
throw new NotSupportedException(
|
||||
'Unknown control byte encountered.',
|
||||
NotSupportedException::CODE_CONTROL_BYTE,
|
||||
null,
|
||||
$byte
|
||||
);
|
||||
} else {
|
||||
return $byte;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the opened connection, even if it is a persistent one.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
return $this->trans->close();
|
||||
}
|
||||
}
|
44
system/autoload/PEAR2/Net/RouterOS/DataFlowException.php
Normal file
44
system/autoload/PEAR2/Net/RouterOS/DataFlowException.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Base of this class.
|
||||
*/
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown when the request/response cycle goes an unexpected way.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class DataFlowException extends RuntimeException implements Exception
|
||||
{
|
||||
const CODE_INVALID_CREDENTIALS = 10000;
|
||||
const CODE_TAG_REQUIRED = 10500;
|
||||
const CODE_TAG_UNIQUE = 10501;
|
||||
const CODE_UNKNOWN_REQUEST = 10900;
|
||||
const CODE_CANCEL_FAIL = 11200;
|
||||
}
|
34
system/autoload/PEAR2/Net/RouterOS/Exception.php
Normal file
34
system/autoload/PEAR2/Net/RouterOS/Exception.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Generic exception class of this package.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
interface Exception
|
||||
{
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
use InvalidArgumentException as I;
|
||||
|
||||
/**
|
||||
* Exception thrown when there's something wrong with message arguments.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class InvalidArgumentException extends I implements Exception
|
||||
{
|
||||
const CODE_SEEKABLE_REQUIRED = 1100;
|
||||
const CODE_NAME_INVALID = 20100;
|
||||
const CODE_ABSOLUTE_REQUIRED = 40200;
|
||||
const CODE_CMD_UNRESOLVABLE = 40201;
|
||||
const CODE_CMD_INVALID = 40202;
|
||||
const CODE_NAME_UNPARSABLE = 41000;
|
||||
const CODE_VALUE_UNPARSABLE = 41001;
|
||||
}
|
93
system/autoload/PEAR2/Net/RouterOS/LengthException.php
Normal file
93
system/autoload/PEAR2/Net/RouterOS/LengthException.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Base of this class.
|
||||
*/
|
||||
use LengthException as L;
|
||||
|
||||
/**
|
||||
* Exception thrown when there is a problem with a word's length.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class LengthException extends L implements Exception
|
||||
{
|
||||
|
||||
const CODE_UNSUPPORTED = 1200;
|
||||
const CODE_INVALID = 1300;
|
||||
const CODE_BEYOND_SHEME = 1301;
|
||||
|
||||
/**
|
||||
*
|
||||
* @var mixed The problematic length.
|
||||
*/
|
||||
private $_length;
|
||||
|
||||
/**
|
||||
* Creates a new LengthException.
|
||||
*
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param \Exception $previous The previous exception used for the exception
|
||||
* chaining.
|
||||
* @param number $length The length.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
$previous = null,
|
||||
$length = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->_length = $length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the length.
|
||||
*
|
||||
* @return number The length.
|
||||
*/
|
||||
public function getLength()
|
||||
{
|
||||
return $this->_length;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// String representation is not reliable in testing
|
||||
|
||||
/**
|
||||
* Returns a string representation of the exception.
|
||||
*
|
||||
* @return string The exception as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return parent::__toString() . "\nLength:{$this->_length}";
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
237
system/autoload/PEAR2/Net/RouterOS/Message.php
Normal file
237
system/autoload/PEAR2/Net/RouterOS/Message.php
Normal file
@ -0,0 +1,237 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Implements this interface.
|
||||
*/
|
||||
use Countable;
|
||||
|
||||
/**
|
||||
* Implements this interface.
|
||||
*/
|
||||
use IteratorAggregate;
|
||||
|
||||
/**
|
||||
* Requred for IteratorAggregate::getIterator() to work properly with foreach.
|
||||
*/
|
||||
use ArrayObject;
|
||||
|
||||
/**
|
||||
* Represents a RouterOS message.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
abstract class Message implements IteratorAggregate, Countable
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array An array with message attributes. Each array key is the the
|
||||
* name of an attribute, and the correspding array value is the value
|
||||
* for that attribute.
|
||||
*/
|
||||
protected $attributes = array();
|
||||
|
||||
/**
|
||||
* @var string An optional tag to associate the message with.
|
||||
*/
|
||||
private $_tag = null;
|
||||
|
||||
/**
|
||||
* A shorthand gateway.
|
||||
*
|
||||
* This is a magic PHP method that allows you to call the object as a
|
||||
* function. Depending on the argument given, one of the other functions in
|
||||
* the class is invoked and its returned value is returned by this function.
|
||||
*
|
||||
* @param string $name The name of an attribute to get the value of, or NULL
|
||||
* to get the tag.
|
||||
*
|
||||
* @return string|resource The value of the specified attribute,
|
||||
* or the tag if NULL is provided.
|
||||
*/
|
||||
public function __invoke($name = null)
|
||||
{
|
||||
if (null === $name) {
|
||||
return $this->getTag();
|
||||
}
|
||||
return $this->getAttribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a name of an attribute (message or query one).
|
||||
*
|
||||
* @param mixed $name The name to sanitize.
|
||||
*
|
||||
* @return string The sanitized name.
|
||||
*/
|
||||
public static function sanitizeAttributeName($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if ((empty($name) && $name !== '0')
|
||||
|| preg_match('/[=\s]/s', $name)
|
||||
) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid name of argument supplied.',
|
||||
InvalidArgumentException::CODE_NAME_INVALID
|
||||
);
|
||||
}
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes a value of an attribute (message or query one).
|
||||
*
|
||||
* @param mixed $value The value to sanitize.
|
||||
*
|
||||
* @return string The sanitized value.
|
||||
*/
|
||||
public static function sanitizeAttributeValue($value)
|
||||
{
|
||||
if (Communicator::isSeekableStream($value)) {
|
||||
return $value;
|
||||
} else {
|
||||
return (string) $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tag that the message is associated with.
|
||||
*
|
||||
* @return string The current tag or NULL if there isn't a tag.
|
||||
* @see setTag()
|
||||
*/
|
||||
public function getTag()
|
||||
{
|
||||
return $this->_tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tag to associate the request with.
|
||||
*
|
||||
* Sets the tag to associate the message with. Setting NULL erases the
|
||||
* currently set tag.
|
||||
*
|
||||
* @param string $tag The tag to set.
|
||||
*
|
||||
* @return $this The message object.
|
||||
* @see getTag()
|
||||
*/
|
||||
protected function setTag($tag)
|
||||
{
|
||||
$this->_tag = (null === $tag) ? null : (string) $tag;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an attribute.
|
||||
*
|
||||
* @param string $name The name of the attribute.
|
||||
*
|
||||
* @return string|resource|null The value of the specified attribute.
|
||||
* Returns NULL if such an attribute is not set.
|
||||
* @see setAttribute()
|
||||
*/
|
||||
protected function getAttribute($name)
|
||||
{
|
||||
$name = self::sanitizeAttributeName($name);
|
||||
if (array_key_exists($name, $this->attributes)) {
|
||||
return $this->attributes[$name];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all arguments in an array.
|
||||
*
|
||||
* @return ArrayObject An ArrayObject with the keys being argument names,
|
||||
* and the array values being argument values.
|
||||
* @see getArgument()
|
||||
* @see setArgument()
|
||||
*/
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayObject($this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of arguments.
|
||||
*
|
||||
* @param int $mode The counter mode.
|
||||
* Either COUNT_NORMAL or COUNT_RECURSIVE.
|
||||
* When in normal mode, counts the number of arguments.
|
||||
* When in recursive mode, counts the number of API words
|
||||
* (including the empty word at the end).
|
||||
*
|
||||
* @return int The number of arguments/words.
|
||||
*/
|
||||
public function count($mode = COUNT_NORMAL)
|
||||
{
|
||||
$result = count($this->attributes);
|
||||
if ($mode !== COUNT_NORMAL) {
|
||||
$result += 2/*first+last word*/
|
||||
+ (int)(null !== $this->getTag());
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute for the message.
|
||||
*
|
||||
* @param string $name Name of the attribute.
|
||||
* @param string|resource|null $value Value of the attribute as a string or
|
||||
* seekable stream.
|
||||
* Setting the value to NULL removes an argument of this name.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
*
|
||||
* @return $this The message object.
|
||||
* @see getArgument()
|
||||
*/
|
||||
protected function setAttribute($name, $value = '')
|
||||
{
|
||||
if (null === $value) {
|
||||
unset($this->attributes[self::sanitizeAttributeName($name)]);
|
||||
} else {
|
||||
$this->attributes[self::sanitizeAttributeName($name)]
|
||||
= self::sanitizeAttributeValue($value);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all attributes from the message.
|
||||
*
|
||||
* @return $this The message object.
|
||||
*/
|
||||
protected function removeAllAttributes()
|
||||
{
|
||||
$this->attributes = array();
|
||||
return $this;
|
||||
}
|
||||
}
|
91
system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php
Normal file
91
system/autoload/PEAR2/Net/RouterOS/NotSupportedException.php
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Base of this class.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* Exception thrown when encountering something not supported by RouterOS or
|
||||
* this package.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class NotSupportedException extends E implements Exception
|
||||
{
|
||||
|
||||
const CODE_CONTROL_BYTE = 1601;
|
||||
|
||||
/**
|
||||
* @var mixed The unsuppported value.
|
||||
*/
|
||||
private $_value;
|
||||
|
||||
/**
|
||||
* Creates a new NotSupportedException.
|
||||
*
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param \Exception $previous The previous exception used for the exception
|
||||
* chaining.
|
||||
* @param mixed $value The unsupported value.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
$previous = null,
|
||||
$value = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unsupported value.
|
||||
*
|
||||
* @return mixed The unsupported value.
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// String representation is not reliable in testing
|
||||
|
||||
/**
|
||||
* Returns a string representation of the exception.
|
||||
*
|
||||
* @return string The exception as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return parent::__toString() . "\nValue:{$this->_value}";
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
267
system/autoload/PEAR2/Net/RouterOS/Query.php
Normal file
267
system/autoload/PEAR2/Net/RouterOS/Query.php
Normal file
@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Refers to transmitter direction constants.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter as T;
|
||||
|
||||
/**
|
||||
* Represents a query for RouterOS requests.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class Query
|
||||
{
|
||||
|
||||
/**
|
||||
* Checks if the property exists.
|
||||
*/
|
||||
const OP_EX = '';
|
||||
|
||||
/**
|
||||
* Checks if the property does not exist.
|
||||
*/
|
||||
const OP_NEX = '-';
|
||||
|
||||
/**
|
||||
* Checks if the property equals a certain value.
|
||||
*/
|
||||
const OP_EQ = '=';
|
||||
|
||||
/**
|
||||
* Checks if the property is less than a certain value.
|
||||
*/
|
||||
const OP_LT = '<';
|
||||
|
||||
/**
|
||||
* Checks if the property is greather than a certain value.
|
||||
*/
|
||||
const OP_GT = '>';
|
||||
|
||||
/**
|
||||
* @var array An array of the words forming the query. Each value is an
|
||||
* array with the first member being the predicate (operator and name),
|
||||
* and the second member being the value for the predicate.
|
||||
*/
|
||||
protected $words = array();
|
||||
|
||||
/**
|
||||
* This class is not to be instantiated normally, but by static methods
|
||||
* instead. Use {@link static::where()} to create an instance of it.
|
||||
*/
|
||||
private function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the operator of a condition.
|
||||
*
|
||||
* @param string $operator The operator to sanitize.
|
||||
*
|
||||
* @return string The sanitized operator.
|
||||
*/
|
||||
protected static function sanitizeOperator($operator)
|
||||
{
|
||||
$operator = (string) $operator;
|
||||
switch ($operator) {
|
||||
case Query::OP_EX:
|
||||
case Query::OP_NEX:
|
||||
case Query::OP_EQ:
|
||||
case Query::OP_LT:
|
||||
case Query::OP_GT:
|
||||
return $operator;
|
||||
default:
|
||||
throw new UnexpectedValueException(
|
||||
'Unknown operator specified',
|
||||
UnexpectedValueException::CODE_ACTION_UNKNOWN,
|
||||
null,
|
||||
$operator
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new query with an initial condition.
|
||||
*
|
||||
* @param string $name The name of the property to test.
|
||||
* @param string|resource|null $value Value of the property as a string
|
||||
* or seekable stream. Not required for existence tests.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
* @param string $operator One of the OP_* constants.
|
||||
* Describes the operation to perform.
|
||||
*
|
||||
* @return static A new query object.
|
||||
*/
|
||||
public static function where(
|
||||
$name,
|
||||
$value = null,
|
||||
$operator = self::OP_EX
|
||||
) {
|
||||
$query = new static;
|
||||
return $query->addWhere($name, $value, $operator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Negates the query.
|
||||
*
|
||||
* @return $this The query object.
|
||||
*/
|
||||
public function not()
|
||||
{
|
||||
$this->words[] = array('#!', null);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition as an alternative to the query.
|
||||
*
|
||||
* @param string $name The name of the property to test.
|
||||
* @param string|resource|null $value Value of the property as a string
|
||||
* or seekable stream. Not required for existence tests.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
* @param string $operator One of the OP_* constants.
|
||||
* Describes the operation to perform.
|
||||
*
|
||||
* @return $this The query object.
|
||||
*/
|
||||
public function orWhere($name, $value = null, $operator = self::OP_EX)
|
||||
{
|
||||
$this->addWhere($name, $value, $operator)->words[] = array('#|', null);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition in addition to the query.
|
||||
*
|
||||
* @param string $name The name of the property to test.
|
||||
* @param string|resource|null $value Value of the property as a string
|
||||
* or seekable stream. Not required for existence tests.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
* @param string $operator One of the OP_* constants.
|
||||
* Describes the operation to perform.
|
||||
*
|
||||
* @return $this The query object.
|
||||
*/
|
||||
public function andWhere($name, $value = null, $operator = self::OP_EX)
|
||||
{
|
||||
$this->addWhere($name, $value, $operator)->words[] = array('#&', null);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the query over a communicator.
|
||||
*
|
||||
* @param Communicator $com The communicator to send the query over.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
*/
|
||||
public function send(Communicator $com)
|
||||
{
|
||||
if ($com->getTransmitter()->isPersistent()) {
|
||||
$old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND);
|
||||
$bytes = $this->_send($com);
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
return $bytes;
|
||||
}
|
||||
return $this->_send($com);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the query over a communicator.
|
||||
*
|
||||
* The only difference with the non private equivalent is that this one does
|
||||
* not do locking.
|
||||
*
|
||||
* @param Communicator $com The communicator to send the query over.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
*/
|
||||
private function _send(Communicator $com)
|
||||
{
|
||||
if (!$com->getTransmitter()->isAcceptingData()) {
|
||||
throw new SocketException(
|
||||
'Transmitter is invalid. Sending aborted.',
|
||||
SocketException::CODE_QUERY_SEND_FAIL
|
||||
);
|
||||
}
|
||||
$bytes = 0;
|
||||
foreach ($this->words as $queryWord) {
|
||||
list($predicate, $value) = $queryWord;
|
||||
$prefix = '?' . $predicate;
|
||||
if (null === $value) {
|
||||
$bytes += $com->sendWord($prefix);
|
||||
} else {
|
||||
$prefix .= '=';
|
||||
if (is_string($value)) {
|
||||
$bytes += $com->sendWord($prefix . $value);
|
||||
} else {
|
||||
$bytes += $com->sendWordFromStream($prefix, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a condition.
|
||||
*
|
||||
* @param string $name The name of the property to test.
|
||||
* @param string|resource|null $value Value of the property as a string
|
||||
* or seekable stream. Not required for existence tests.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
* @param string $operator One of the ACTION_* constants.
|
||||
* Describes the operation to perform.
|
||||
*
|
||||
* @return $this The query object.
|
||||
*/
|
||||
protected function addWhere($name, $value, $operator)
|
||||
{
|
||||
$this->words[] = array(
|
||||
static::sanitizeOperator($operator)
|
||||
. Message::sanitizeAttributeName($name),
|
||||
(null === $value ? null : Message::sanitizeAttributeValue($value))
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
}
|
279
system/autoload/PEAR2/Net/RouterOS/Registry.php
Normal file
279
system/autoload/PEAR2/Net/RouterOS/Registry.php
Normal file
@ -0,0 +1,279 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Uses shared memory to keep responses in.
|
||||
*/
|
||||
use PEAR2\Cache\SHM;
|
||||
|
||||
/**
|
||||
* A RouterOS registry.
|
||||
*
|
||||
* Provides functionality for managing the request/response flow. Particularly
|
||||
* useful in persistent connections.
|
||||
*
|
||||
* Note that this class is not meant to be called directly.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
/**
|
||||
* @var SHM The storage.
|
||||
*/
|
||||
protected $shm;
|
||||
|
||||
/**
|
||||
* @var int ID of request. Populated at first instance in request.
|
||||
*/
|
||||
protected static $requestId = -1;
|
||||
|
||||
/**
|
||||
* @var int ID to be given to next instance, after incrementing it.
|
||||
*/
|
||||
protected static $instanceIdSeed = -1;
|
||||
|
||||
/**
|
||||
* @var int ID of instance within the request.
|
||||
*/
|
||||
protected $instanceId;
|
||||
|
||||
/**
|
||||
* Creates a registry.
|
||||
*
|
||||
* @param string $uri An URI to bind the registry to.
|
||||
*/
|
||||
public function __construct($uri)
|
||||
{
|
||||
$this->shm = SHM::factory(__CLASS__ . ' ' . $uri);
|
||||
if (-1 === self::$requestId) {
|
||||
self::$requestId = $this->shm->add('requestId', 0)
|
||||
? 0 : $this->shm->inc('requestId');
|
||||
}
|
||||
$this->instanceId = ++self::$instanceIdSeed;
|
||||
$this->shm->add('responseBuffer_' . $this->getOwnershipTag(), array());
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a tag.
|
||||
*
|
||||
* Parses a tag to reveal the ownership part of it, and the original tag.
|
||||
*
|
||||
* @param string $tag The tag (as received) to parse.
|
||||
*
|
||||
* @return array An array with the first member being the ownership tag, and
|
||||
* the second one being the original tag.
|
||||
*/
|
||||
public static function parseTag($tag)
|
||||
{
|
||||
if (null === $tag) {
|
||||
return array(null, null);
|
||||
}
|
||||
$result = explode('__', $tag, 2);
|
||||
$result[0] .= '__';
|
||||
if ('' === $result[1]) {
|
||||
$result[1] = null;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if this instance is the tagless mode owner.
|
||||
*
|
||||
* @return bool TRUE if this instance is the tagless mode owner, FALSE
|
||||
* otherwise.
|
||||
*/
|
||||
public function isTaglessModeOwner()
|
||||
{
|
||||
$this->shm->lock('taglessModeOwner');
|
||||
$result = $this->shm->exists('taglessModeOwner')
|
||||
&& $this->getOwnershipTag() === $this->shm->get('taglessModeOwner');
|
||||
$this->shm->unlock('taglessModeOwner');
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the "tagless mode" setting.
|
||||
*
|
||||
* While in tagless mode, this instance will claim owhership of any
|
||||
* responses without a tag. While not in this mode, any requests without a
|
||||
* tag will be given to all instances.
|
||||
*
|
||||
* Regardless of mode, if the type of the response is
|
||||
* {@link Response::TYPE_FATAL}, it will be given to all instances.
|
||||
*
|
||||
* @param bool $taglessMode TRUE to claim tagless ownership, FALSE to
|
||||
* release such ownership, if taken.
|
||||
*
|
||||
* @return bool TRUE on success, FALSE on failure.
|
||||
*/
|
||||
public function setTaglessMode($taglessMode)
|
||||
{
|
||||
return $taglessMode
|
||||
? ($this->shm->lock('taglessMode')
|
||||
&& $this->shm->lock('taglessModeOwner')
|
||||
&& $this->shm->add('taglessModeOwner', $this->getOwnershipTag())
|
||||
&& $this->shm->unlock('taglessModeOwner'))
|
||||
: ($this->isTaglessModeOwner()
|
||||
&& $this->shm->lock('taglessModeOwner')
|
||||
&& $this->shm->delete('taglessModeOwner')
|
||||
&& $this->shm->unlock('taglessModeOwner')
|
||||
&& $this->shm->unlock('taglessMode'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the ownership tag for this instance.
|
||||
*
|
||||
* @return string The ownership tag for this registry instance.
|
||||
*/
|
||||
public function getOwnershipTag()
|
||||
{
|
||||
return self::$requestId . '_' . $this->instanceId . '__';
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a response to the registry.
|
||||
*
|
||||
* @param Response $response The response to add. The caller of this
|
||||
* function is responsible for ensuring that the ownership tag and the
|
||||
* original tag are separated, so that only the original one remains in
|
||||
* the response.
|
||||
* @param string $ownershipTag The ownership tag that the response had.
|
||||
*
|
||||
* @return bool TRUE if the request was added to its buffer, FALSE if
|
||||
* this instance owns the response, and therefore doesn't need to add
|
||||
* the response to its buffer.
|
||||
*/
|
||||
public function add(Response $response, $ownershipTag)
|
||||
{
|
||||
if ($this->getOwnershipTag() === $ownershipTag
|
||||
|| ($this->isTaglessModeOwner()
|
||||
&& $response->getType() !== Response::TYPE_FATAL)
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (null === $ownershipTag) {
|
||||
$this->shm->lock('taglessModeOwner');
|
||||
if ($this->shm->exists('taglessModeOwner')
|
||||
&& $response->getType() !== Response::TYPE_FATAL
|
||||
) {
|
||||
$ownershipTag = $this->shm->get('taglessModeOwner');
|
||||
$this->shm->unlock('taglessModeOwner');
|
||||
} else {
|
||||
$this->shm->unlock('taglessModeOwner');
|
||||
foreach ($this->shm->getIterator(
|
||||
'/^(responseBuffer\_)/',
|
||||
true
|
||||
) as $targetBufferName) {
|
||||
$this->_add($response, $targetBufferName);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_add($response, 'responseBuffer_' . $ownershipTag);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a response to a buffer.
|
||||
*
|
||||
* @param Response $response The response to add.
|
||||
* @param string $targetBufferName The name of the buffer to add the
|
||||
* response to.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _add(Response $response, $targetBufferName)
|
||||
{
|
||||
if ($this->shm->lock($targetBufferName)) {
|
||||
$targetBuffer = $this->shm->get($targetBufferName);
|
||||
$targetBuffer[] = $response;
|
||||
$this->shm->set($targetBufferName, $targetBuffer);
|
||||
$this->shm->unlock($targetBufferName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next response from this instance's buffer.
|
||||
*
|
||||
* @return Response|null The next response, or NULL if there isn't one.
|
||||
*/
|
||||
public function getNextResponse()
|
||||
{
|
||||
$response = null;
|
||||
$targetBufferName = 'responseBuffer_' . $this->getOwnershipTag();
|
||||
if ($this->shm->exists($targetBufferName)
|
||||
&& $this->shm->lock($targetBufferName)
|
||||
) {
|
||||
$targetBuffer = $this->shm->get($targetBufferName);
|
||||
if (!empty($targetBuffer)) {
|
||||
$response = array_shift($targetBuffer);
|
||||
$this->shm->set($targetBufferName, $targetBuffer);
|
||||
}
|
||||
$this->shm->unlock($targetBufferName);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the registry.
|
||||
*
|
||||
* Closes the registry, meaning that all buffers are cleared.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
self::$requestId = -1;
|
||||
self::$instanceIdSeed = -1;
|
||||
$this->shm->clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a buffer.
|
||||
*
|
||||
* @param string $targetBufferName The buffer to remove.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _close($targetBufferName)
|
||||
{
|
||||
if ($this->shm->lock($targetBufferName)) {
|
||||
$this->shm->delete($targetBufferName);
|
||||
$this->shm->unlock($targetBufferName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this instance's buffer.
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
$this->_close('responseBuffer_' . $this->getOwnershipTag());
|
||||
}
|
||||
}
|
403
system/autoload/PEAR2/Net/RouterOS/Request.php
Normal file
403
system/autoload/PEAR2/Net/RouterOS/Request.php
Normal file
@ -0,0 +1,403 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Refers to transmitter direction constants.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter as T;
|
||||
|
||||
/**
|
||||
* Represents a RouterOS request.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class Request extends Message
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string The command to be executed.
|
||||
*/
|
||||
private $_command;
|
||||
|
||||
/**
|
||||
* @var Query A query for the command.
|
||||
*/
|
||||
private $_query;
|
||||
|
||||
/**
|
||||
* Creates a request to send to RouterOS.
|
||||
*
|
||||
* @param string $command The command to send. Can also contain arguments
|
||||
* expressed in a shell-like syntax.
|
||||
* @param Query $query A query to associate with the request.
|
||||
* @param string $tag The tag for the request.
|
||||
*
|
||||
* @see setCommand()
|
||||
* @see setArgument()
|
||||
* @see setTag()
|
||||
* @see setQuery()
|
||||
*/
|
||||
public function __construct($command, Query $query = null, $tag = null)
|
||||
{
|
||||
if (false !== strpos($command, '=')
|
||||
&& false !== ($spaceBeforeEquals = strrpos(
|
||||
strstr($command, '=', true),
|
||||
' '
|
||||
))
|
||||
) {
|
||||
$this->parseArgumentString(substr($command, $spaceBeforeEquals));
|
||||
$command = rtrim(substr($command, 0, $spaceBeforeEquals));
|
||||
}
|
||||
$this->setCommand($command);
|
||||
$this->setQuery($query);
|
||||
$this->setTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* A shorthand gateway.
|
||||
*
|
||||
* This is a magic PHP method that allows you to call the object as a
|
||||
* function. Depending on the argument given, one of the other functions in
|
||||
* the class is invoked and its returned value is returned by this function.
|
||||
*
|
||||
* @param Query|Communicator|string|null $arg A {@link Query} to associate
|
||||
* the request with, a {@link Communicator} to send the request over,
|
||||
* an argument to get the value of, or NULL to get the tag. If a
|
||||
* second argument is provided, this becomes the name of the argument to
|
||||
* set the value of, and the second argument is the value to set.
|
||||
*
|
||||
* @return string|resource|int|$this Whatever the long form
|
||||
* function returns.
|
||||
*/
|
||||
public function __invoke($arg = null)
|
||||
{
|
||||
if (func_num_args() > 1) {
|
||||
return $this->setArgument(func_get_arg(0), func_get_arg(1));
|
||||
}
|
||||
if ($arg instanceof Query) {
|
||||
return $this->setQuery($arg);
|
||||
}
|
||||
if ($arg instanceof Communicator) {
|
||||
return $this->send($arg);
|
||||
}
|
||||
return parent::__invoke($arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the command to send to RouterOS.
|
||||
*
|
||||
* Sets the command to send to RouterOS. The command can use the API or CLI
|
||||
* syntax of RouterOS, but either way, it must be absolute (begin with a
|
||||
* "/") and without arguments.
|
||||
*
|
||||
* @param string $command The command to send.
|
||||
*
|
||||
* @return $this The request object.
|
||||
* @see getCommand()
|
||||
* @see setArgument()
|
||||
*/
|
||||
public function setCommand($command)
|
||||
{
|
||||
$command = (string) $command;
|
||||
if (strpos($command, '/') !== 0) {
|
||||
throw new InvalidArgumentException(
|
||||
'Commands must be absolute.',
|
||||
InvalidArgumentException::CODE_ABSOLUTE_REQUIRED
|
||||
);
|
||||
}
|
||||
if (substr_count($command, '/') === 1) {
|
||||
//Command line syntax convertion
|
||||
$cmdParts = preg_split('#[\s/]+#sm', $command);
|
||||
$cmdRes = array($cmdParts[0]);
|
||||
for ($i = 1, $n = count($cmdParts); $i < $n; $i++) {
|
||||
if ('..' === $cmdParts[$i]) {
|
||||
$delIndex = count($cmdRes) - 1;
|
||||
if ($delIndex < 1) {
|
||||
throw new InvalidArgumentException(
|
||||
'Unable to resolve command',
|
||||
InvalidArgumentException::CODE_CMD_UNRESOLVABLE
|
||||
);
|
||||
}
|
||||
unset($cmdRes[$delIndex]);
|
||||
$cmdRes = array_values($cmdRes);
|
||||
} else {
|
||||
$cmdRes[] = $cmdParts[$i];
|
||||
}
|
||||
}
|
||||
$command = implode('/', $cmdRes);
|
||||
}
|
||||
if (!preg_match('#^/\S+$#sm', $command)) {
|
||||
throw new InvalidArgumentException(
|
||||
'Invalid command supplied.',
|
||||
InvalidArgumentException::CODE_CMD_INVALID
|
||||
);
|
||||
}
|
||||
$this->_command = $command;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the command that will be send to RouterOS.
|
||||
*
|
||||
* Gets the command that will be send to RouterOS in its API syntax.
|
||||
*
|
||||
* @return string The command to send.
|
||||
* @see setCommand()
|
||||
*/
|
||||
public function getCommand()
|
||||
{
|
||||
return $this->_command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the query to send with the command.
|
||||
*
|
||||
* @param Query $query The query to be set. Setting NULL will remove the
|
||||
* currently associated query.
|
||||
*
|
||||
* @return $this The request object.
|
||||
* @see getQuery()
|
||||
*/
|
||||
public function setQuery(Query $query = null)
|
||||
{
|
||||
$this->_query = $query;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently associated query
|
||||
*
|
||||
* @return Query The currently associated query.
|
||||
* @see setQuery()
|
||||
*/
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tag to associate the request with.
|
||||
*
|
||||
* Sets the tag to associate the request with. Setting NULL erases the
|
||||
* currently set tag.
|
||||
*
|
||||
* @param string $tag The tag to set.
|
||||
*
|
||||
* @return $this The request object.
|
||||
* @see getTag()
|
||||
*/
|
||||
public function setTag($tag)
|
||||
{
|
||||
return parent::setTag($tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an argument for the request.
|
||||
*
|
||||
* @param string $name Name of the argument.
|
||||
* @param string|resource|null $value Value of the argument as a string or
|
||||
* seekable stream.
|
||||
* Setting the value to NULL removes an argument of this name.
|
||||
* If a seekable stream is provided, it is sent from its current
|
||||
* posistion to its end, and the pointer is seeked back to its current
|
||||
* position after sending.
|
||||
* Non seekable streams, as well as all other types, are casted to a
|
||||
* string.
|
||||
*
|
||||
* @return $this The request object.
|
||||
* @see getArgument()
|
||||
*/
|
||||
public function setArgument($name, $value = '')
|
||||
{
|
||||
return parent::setAttribute($name, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an argument.
|
||||
*
|
||||
* @param string $name The name of the argument.
|
||||
*
|
||||
* @return string|resource|null The value of the specified argument.
|
||||
* Returns NULL if such an argument is not set.
|
||||
* @see setAttribute()
|
||||
*/
|
||||
public function getArgument($name)
|
||||
{
|
||||
return parent::getAttribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all arguments from the request.
|
||||
*
|
||||
* @return $this The request object.
|
||||
*/
|
||||
public function removeAllArguments()
|
||||
{
|
||||
return parent::removeAllAttributes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request over a communicator.
|
||||
*
|
||||
* @param Communicator $com The communicator to send the request over.
|
||||
* @param Registry $reg An optional registry to sync the request with.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
* @see Client::sendSync()
|
||||
* @see Client::sendAsync()
|
||||
*/
|
||||
public function send(Communicator $com, Registry $reg = null)
|
||||
{
|
||||
if (null !== $reg
|
||||
&& (null != $this->getTag() || !$reg->isTaglessModeOwner())
|
||||
) {
|
||||
$originalTag = $this->getTag();
|
||||
$this->setTag($reg->getOwnershipTag() . $originalTag);
|
||||
$bytes = $this->send($com);
|
||||
$this->setTag($originalTag);
|
||||
return $bytes;
|
||||
}
|
||||
if ($com->getTransmitter()->isPersistent()) {
|
||||
$old = $com->getTransmitter()->lock(T\Stream::DIRECTION_SEND);
|
||||
$bytes = $this->_send($com);
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
return $bytes;
|
||||
}
|
||||
return $this->_send($com);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a request over a communicator.
|
||||
*
|
||||
* The only difference with the non private equivalent is that this one does
|
||||
* not do locking.
|
||||
*
|
||||
* @param Communicator $com The communicator to send the request over.
|
||||
*
|
||||
* @return int The number of bytes sent.
|
||||
* @see Client::sendSync()
|
||||
* @see Client::sendAsync()
|
||||
*/
|
||||
private function _send(Communicator $com)
|
||||
{
|
||||
if (!$com->getTransmitter()->isAcceptingData()) {
|
||||
throw new SocketException(
|
||||
'Transmitter is invalid. Sending aborted.',
|
||||
SocketException::CODE_REQUEST_SEND_FAIL
|
||||
);
|
||||
}
|
||||
$bytes = 0;
|
||||
$bytes += $com->sendWord($this->getCommand());
|
||||
if (null !== ($tag = $this->getTag())) {
|
||||
$bytes += $com->sendWord('.tag=' . $tag);
|
||||
}
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$prefix = '=' . $name . '=';
|
||||
if (is_string($value)) {
|
||||
$bytes += $com->sendWord($prefix . $value);
|
||||
} else {
|
||||
$bytes += $com->sendWordFromStream($prefix, $value);
|
||||
}
|
||||
}
|
||||
$query = $this->getQuery();
|
||||
if ($query instanceof Query) {
|
||||
$bytes += $query->send($com);
|
||||
}
|
||||
$bytes += $com->sendWord('');
|
||||
return $bytes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the arguments of a command.
|
||||
*
|
||||
* @param string $string The argument string to parse.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function parseArgumentString($string)
|
||||
{
|
||||
/*
|
||||
* Grammar:
|
||||
*
|
||||
* <arguments> := (<<\s+>>, <argument>)*,
|
||||
* <argument> := <name>, <value>?
|
||||
* <name> := <<[^\=\s]+>>
|
||||
* <value> := "=", (<quoted string> | <unquoted string>)
|
||||
* <quotedString> := <<">>, <<([^"]|\\"|\\\\)*>>, <<">>
|
||||
* <unquotedString> := <<\S+>>
|
||||
*/
|
||||
|
||||
$token = '';
|
||||
$name = null;
|
||||
while ($string = substr($string, strlen($token))) {
|
||||
if (null === $name) {
|
||||
if (preg_match('/^\s+([^\s=]+)/sS', $string, $matches)) {
|
||||
$token = $matches[0];
|
||||
$name = $matches[1];
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"Parsing of argument name failed near '{$string}'",
|
||||
InvalidArgumentException::CODE_NAME_UNPARSABLE
|
||||
);
|
||||
}
|
||||
} elseif (preg_match('/^\s/s', $string, $matches)) {
|
||||
//Empty argument
|
||||
$token = '';
|
||||
$this->setArgument($name);
|
||||
$name = null;
|
||||
} elseif (preg_match(
|
||||
'/^="(([^\\\"]|\\\"|\\\\)*)"/sS',
|
||||
$string,
|
||||
$matches
|
||||
)) {
|
||||
$token = $matches[0];
|
||||
$this->setArgument(
|
||||
$name,
|
||||
str_replace(
|
||||
array('\\"', '\\\\'),
|
||||
array('"', '\\'),
|
||||
$matches[1]
|
||||
)
|
||||
);
|
||||
$name = null;
|
||||
} elseif (preg_match('/^=(\S+)/sS', $string, $matches)) {
|
||||
$token = $matches[0];
|
||||
$this->setArgument($name, $matches[1]);
|
||||
$name = null;
|
||||
} else {
|
||||
throw new InvalidArgumentException(
|
||||
"Parsing of argument value failed near '{$string}'",
|
||||
InvalidArgumentException::CODE_VALUE_UNPARSABLE
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $name && ('' !== ($name = trim($name)))) {
|
||||
$this->setArgument($name, '');
|
||||
}
|
||||
|
||||
}
|
||||
}
|
335
system/autoload/PEAR2/Net/RouterOS/Response.php
Normal file
335
system/autoload/PEAR2/Net/RouterOS/Response.php
Normal file
@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Refers to transmitter direction constants.
|
||||
*/
|
||||
use PEAR2\Net\Transmitter as T;
|
||||
|
||||
/**
|
||||
* Locks are released upon any exception from anywhere.
|
||||
*/
|
||||
use Exception as E;
|
||||
|
||||
/**
|
||||
* Represents a RouterOS response.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class Response extends Message
|
||||
{
|
||||
|
||||
/**
|
||||
* The last response for a request.
|
||||
*/
|
||||
const TYPE_FINAL = '!done';
|
||||
|
||||
/**
|
||||
* A response with data.
|
||||
*/
|
||||
const TYPE_DATA = '!re';
|
||||
|
||||
/**
|
||||
* A response signifying error.
|
||||
*/
|
||||
const TYPE_ERROR = '!trap';
|
||||
|
||||
/**
|
||||
* A response signifying a fatal error, due to which the connection would be
|
||||
* terminated.
|
||||
*/
|
||||
const TYPE_FATAL = '!fatal';
|
||||
|
||||
/**
|
||||
* @var array An array of unrecognized words in network order.
|
||||
*/
|
||||
protected $unrecognizedWords = array();
|
||||
|
||||
/**
|
||||
* @var string The response type.
|
||||
*/
|
||||
private $_type;
|
||||
|
||||
/**
|
||||
* Extracts a new response from a communicator.
|
||||
*
|
||||
* @param Communicator $com The communicator from which to extract
|
||||
* the new response.
|
||||
* @param bool $asStream Whether to populate the argument values
|
||||
* with streams instead of strings.
|
||||
* @param int $sTimeout If a response is not immediatly
|
||||
* available, wait this many seconds. If NULL, wait indefinetly.
|
||||
* @param int $usTimeout Microseconds to add to the waiting time.
|
||||
* @param Registry $reg An optional registry to sync the
|
||||
* response with.
|
||||
*
|
||||
* @see getType()
|
||||
* @see getArgument()
|
||||
*/
|
||||
public function __construct(
|
||||
Communicator $com,
|
||||
$asStream = false,
|
||||
$sTimeout = 0,
|
||||
$usTimeout = null,
|
||||
Registry $reg = null
|
||||
) {
|
||||
if (null === $reg) {
|
||||
if ($com->getTransmitter()->isPersistent()) {
|
||||
$old = $com->getTransmitter()
|
||||
->lock(T\Stream::DIRECTION_RECEIVE);
|
||||
try {
|
||||
$this->_receive($com, $asStream, $sTimeout, $usTimeout);
|
||||
} catch (E $e) {
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
throw $e;
|
||||
}
|
||||
$com->getTransmitter()->lock($old, true);
|
||||
} else {
|
||||
$this->_receive($com, $asStream, $sTimeout, $usTimeout);
|
||||
}
|
||||
} else {
|
||||
while (null === ($response = $reg->getNextResponse())) {
|
||||
$newResponse = new self($com, true, $sTimeout, $usTimeout);
|
||||
$tagInfo = $reg::parseTag($newResponse->getTag());
|
||||
$newResponse->setTag($tagInfo[1]);
|
||||
if (!$reg->add($newResponse, $tagInfo[0])) {
|
||||
$response = $newResponse;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->_type = $response->_type;
|
||||
$this->attributes = $response->attributes;
|
||||
$this->unrecognizedWords = $response->unrecognizedWords;
|
||||
$this->setTag($response->getTag());
|
||||
|
||||
if (!$asStream) {
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$this->setAttribute(
|
||||
$name,
|
||||
stream_get_contents($value)
|
||||
);
|
||||
}
|
||||
foreach ($response->unrecognizedWords as $i => $value) {
|
||||
$this->unrecognizedWords[$i] = stream_get_contents($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts a new response from a communicator.
|
||||
*
|
||||
* This is the function that performs the actual receiving, while the
|
||||
* constructor is also involved in locks and registry sync.
|
||||
*
|
||||
* @param Communicator $com The communicator from which to extract
|
||||
* the new response.
|
||||
* @param bool $asStream Whether to populate the argument values
|
||||
* with streams instead of strings.
|
||||
* @param int $sTimeout If a response is not immediatly
|
||||
* available, wait this many seconds. If NULL, wait indefinetly.
|
||||
* Note that if an empty sentence is received, the timeout will be
|
||||
* reset for another sentence receiving.
|
||||
* @param int $usTimeout Microseconds to add to the waiting time.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function _receive(
|
||||
Communicator $com,
|
||||
$asStream = false,
|
||||
$sTimeout = 0,
|
||||
$usTimeout = null
|
||||
) {
|
||||
do {
|
||||
if (!$com->getTransmitter()->isDataAwaiting(
|
||||
$sTimeout,
|
||||
$usTimeout
|
||||
)) {
|
||||
throw new SocketException(
|
||||
'No data within the time limit',
|
||||
SocketException::CODE_NO_DATA
|
||||
);
|
||||
}
|
||||
$type = $com->getNextWord();
|
||||
} while ('' === $type);
|
||||
$this->setType($type);
|
||||
if ($asStream) {
|
||||
for ($word = $com->getNextWordAsStream(), fseek($word, 0, SEEK_END);
|
||||
ftell($word) !== 0;
|
||||
$word = $com->getNextWordAsStream(), fseek(
|
||||
$word,
|
||||
0,
|
||||
SEEK_END
|
||||
)) {
|
||||
rewind($word);
|
||||
$ind = fread($word, 1);
|
||||
if ('=' === $ind || '.' === $ind) {
|
||||
$prefix = stream_get_line($word, null, '=');
|
||||
}
|
||||
if ('=' === $ind) {
|
||||
$value = fopen('php://temp', 'r+b');
|
||||
$bytesCopied = ftell($word);
|
||||
while (!feof($word)) {
|
||||
$bytesCopied += stream_copy_to_stream(
|
||||
$word,
|
||||
$value,
|
||||
0xFFFFF,
|
||||
$bytesCopied
|
||||
);
|
||||
}
|
||||
rewind($value);
|
||||
$this->setAttribute($prefix, $value);
|
||||
continue;
|
||||
}
|
||||
if ('.' === $ind && 'tag' === $prefix) {
|
||||
$this->setTag(stream_get_contents($word, -1, -1));
|
||||
continue;
|
||||
}
|
||||
rewind($word);
|
||||
$this->unrecognizedWords[] = $word;
|
||||
}
|
||||
} else {
|
||||
for ($word = $com->getNextWord(); '' !== $word;
|
||||
$word = $com->getNextWord()) {
|
||||
if (preg_match('/^=([^=]+)=(.*)$/sS', $word, $matches)) {
|
||||
$this->setAttribute($matches[1], $matches[2]);
|
||||
} elseif (preg_match('/^\.tag=(.*)$/sS', $word, $matches)) {
|
||||
$this->setTag($matches[1]);
|
||||
} else {
|
||||
$this->unrecognizedWords[] = $word;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the response type.
|
||||
*
|
||||
* Sets the response type. Valid values are the TYPE_* constants.
|
||||
*
|
||||
* @param string $type The new response type.
|
||||
*
|
||||
* @return $this The response object.
|
||||
* @see getType()
|
||||
*/
|
||||
protected function setType($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::TYPE_FINAL:
|
||||
case self::TYPE_DATA:
|
||||
case self::TYPE_ERROR:
|
||||
case self::TYPE_FATAL:
|
||||
$this->_type = $type;
|
||||
return $this;
|
||||
default:
|
||||
throw new UnexpectedValueException(
|
||||
'Unrecognized response type.',
|
||||
UnexpectedValueException::CODE_RESPONSE_TYPE_UNKNOWN,
|
||||
null,
|
||||
$type
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response type.
|
||||
*
|
||||
* @return string The response type.
|
||||
* @see setType()
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of an argument.
|
||||
*
|
||||
* @param string $name The name of the argument.
|
||||
*
|
||||
* @return string|resource|null The value of the specified argument.
|
||||
* Returns NULL if such an argument is not set.
|
||||
* @deprecated 1.0.0b5 Use {@link static::getProperty()} instead.
|
||||
* This method will be removed upon final release, and is currently
|
||||
* left standing merely because it can't be easily search&replaced in
|
||||
* existing code, due to the fact the name "getArgument()" is shared
|
||||
* with {@link Request::getArgument()}, which is still valid.
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function getArgument($name)
|
||||
{
|
||||
trigger_error(
|
||||
'Response::getArgument() is deprecated in favor of ' .
|
||||
'Response::getProperty() (but note that Request::getArgument() ' .
|
||||
'is still valid)',
|
||||
E_USER_DEPRECATED
|
||||
);
|
||||
return $this->getAttribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a property.
|
||||
*
|
||||
* @param string $name The name of the property.
|
||||
*
|
||||
* @return string|resource|null The value of the specified property.
|
||||
* Returns NULL if such a property is not set.
|
||||
*/
|
||||
public function getProperty($name)
|
||||
{
|
||||
return parent::getAttribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a list of unrecognized words.
|
||||
*
|
||||
* @return array The list of unrecognized words.
|
||||
*/
|
||||
public function getUnrecognizedWords()
|
||||
{
|
||||
return $this->unrecognizedWords;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of arguments or words.
|
||||
*
|
||||
* @param int $mode The counter mode.
|
||||
* Either COUNT_NORMAL or COUNT_RECURSIVE.
|
||||
* When in normal mode, counts the number of arguments.
|
||||
* When in recursive mode, counts the number of API words.
|
||||
*
|
||||
* @return int The number of arguments/words.
|
||||
*/
|
||||
public function count($mode = COUNT_NORMAL)
|
||||
{
|
||||
$result = parent::count($mode);
|
||||
if ($mode !== COUNT_NORMAL) {
|
||||
$result += count($this->unrecognizedWords);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
569
system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php
Normal file
569
system/autoload/PEAR2/Net/RouterOS/ResponseCollection.php
Normal file
@ -0,0 +1,569 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Implemented by this class.
|
||||
*/
|
||||
use ArrayAccess;
|
||||
|
||||
/**
|
||||
* Implemented by this class.
|
||||
*/
|
||||
use Countable;
|
||||
|
||||
/**
|
||||
* Implemented by this class.
|
||||
*/
|
||||
use SeekableIterator;
|
||||
|
||||
/**
|
||||
* Represents a collection of RouterOS responses.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*
|
||||
* @method string getType()
|
||||
* Calls {@link Response::getType()}
|
||||
* on the response pointed by the pointer.
|
||||
* @method string[] getUnrecognizedWords()
|
||||
* Calls {@link Response::getUnrecognizedWords()}
|
||||
* on the response pointed by the pointer.
|
||||
* @method string|resource|null getProperty(string $name)
|
||||
* Calls {@link Response::getProperty()}
|
||||
* on the response pointed by the pointer.
|
||||
* @method string getTag()
|
||||
* Calls {@link Response::getTag()}
|
||||
* on the response pointed by the pointer.
|
||||
*/
|
||||
class ResponseCollection implements ArrayAccess, SeekableIterator, Countable
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array An array with all {@link Response} objects.
|
||||
*/
|
||||
protected $responses = array();
|
||||
|
||||
/**
|
||||
* @var array An array with each {@link Response} object's type.
|
||||
*/
|
||||
protected $responseTypes = array();
|
||||
|
||||
/**
|
||||
* @var array An array with each {@link Response} object's tag.
|
||||
*/
|
||||
protected $responseTags = array();
|
||||
|
||||
/**
|
||||
* @var array An array with positions of responses, based on an property
|
||||
* name. The name of each property is the array key, and the array value
|
||||
* is another array where the key is the value for that property, and
|
||||
* the value is the posistion of the response. For performance reasons,
|
||||
* each key is built only when {@link static::setIndex()} is called with
|
||||
* that property, and remains available for the lifetime of this
|
||||
* collection.
|
||||
*/
|
||||
protected $responsesIndex = array();
|
||||
|
||||
/**
|
||||
* @var array An array with all distinct properties across all
|
||||
* {@link Response} objects. Created at the first call of
|
||||
* {@link static::getPropertyMap()}.
|
||||
*/
|
||||
protected $propertyMap = null;
|
||||
|
||||
/**
|
||||
* @var int A pointer, as required by SeekableIterator.
|
||||
*/
|
||||
protected $position = 0;
|
||||
|
||||
/**
|
||||
* @var string|null Name of property to use as index. NULL when disabled.
|
||||
*/
|
||||
protected $index = null;
|
||||
|
||||
/**
|
||||
* @var array Criterias used by {@link compare()} to determine the order
|
||||
* between two respones. See {@link orderBy()} for a detailed
|
||||
* description of this array's format.
|
||||
*/
|
||||
protected $compareBy = array();
|
||||
|
||||
/**
|
||||
* Creates a new collection.
|
||||
*
|
||||
* @param array $responses An array of responses, in network order.
|
||||
*/
|
||||
public function __construct(array $responses)
|
||||
{
|
||||
$pos = 0;
|
||||
foreach ($responses as $response) {
|
||||
if ($response instanceof Response) {
|
||||
$this->responseTypes[$pos] = $response->getType();
|
||||
$this->responseTags[$pos] = $response->getTag();
|
||||
$this->responses[$pos++] = $response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A shorthand gateway.
|
||||
*
|
||||
* This is a magic PHP method that allows you to call the object as a
|
||||
* function. Depending on the argument given, one of the other functions in
|
||||
* the class is invoked and its returned value is returned by this function.
|
||||
*
|
||||
* @param int|string|null $offset The offset of the response to seek to.
|
||||
* If the offset is negative, seek to that relative to the end.
|
||||
* If the collection is indexed, you can also supply a value to seek to.
|
||||
* Setting NULL will get the current response's interator.
|
||||
*
|
||||
* @return Response|ArrayObject The {@link Response} at the specified
|
||||
* offset, the current response's iterator (which is an ArrayObject)
|
||||
* when NULL is given, or FALSE if the offset is invalid
|
||||
* or the collection is empty.
|
||||
*/
|
||||
public function __invoke($offset = null)
|
||||
{
|
||||
return null === $offset
|
||||
? $this->current()->getIterator()
|
||||
: $this->seek($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a property to be usable as a key in the collection.
|
||||
*
|
||||
* @param string|null $name The name of the property to use. Future calls
|
||||
* that accept a position will then also be able to search values of
|
||||
* that property for a matching value.
|
||||
* Specifying NULL will disable such lookups (as is by default).
|
||||
* Note that in case this value occures multiple times within the
|
||||
* collection, only the last matching response will be accessible by
|
||||
* that value.
|
||||
*
|
||||
* @return $this The object itself.
|
||||
*/
|
||||
public function setIndex($name)
|
||||
{
|
||||
if (null !== $name) {
|
||||
$name = (string)$name;
|
||||
if (!isset($this->responsesIndex[$name])) {
|
||||
$this->responsesIndex[$name] = array();
|
||||
foreach ($this->responses as $pos => $response) {
|
||||
$val = $response->getProperty($name);
|
||||
if (null !== $val) {
|
||||
$this->responsesIndex[$name][$val] = $pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->index = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of the property used as an index.
|
||||
*
|
||||
* @return string|null Name of property used as index. NULL when disabled.
|
||||
*/
|
||||
public function getIndex()
|
||||
{
|
||||
return $this->index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the whole collection as an array.
|
||||
*
|
||||
* @param bool $useIndex Whether to use the index values as keys for the
|
||||
* resulting array.
|
||||
*
|
||||
* @return array An array with all responses, in network order.
|
||||
*/
|
||||
public function toArray($useIndex = false)
|
||||
{
|
||||
if ($useIndex) {
|
||||
$positions = $this->responsesIndex[$this->index];
|
||||
asort($positions, SORT_NUMERIC);
|
||||
$positions = array_flip($positions);
|
||||
return array_combine(
|
||||
$positions,
|
||||
array_intersect_key($this->responses, $positions)
|
||||
);
|
||||
}
|
||||
return $this->responses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the responses/words in the collection.
|
||||
*
|
||||
* @param int $mode The counter mode.
|
||||
* Either COUNT_NORMAL or COUNT_RECURSIVE.
|
||||
* When in normal mode, counts the number of responses.
|
||||
* When in recursive mode, counts the total number of API words.
|
||||
*
|
||||
* @return int The number of responses in the collection.
|
||||
*/
|
||||
public function count($mode = COUNT_NORMAL)
|
||||
{
|
||||
if ($mode !== COUNT_NORMAL) {
|
||||
$result = 0;
|
||||
foreach ($this->responses as $response) {
|
||||
$result += $response->count($mode);
|
||||
}
|
||||
return $result;
|
||||
} else {
|
||||
return count($this->responses);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an offset exists.
|
||||
*
|
||||
* @param int|string $offset The offset to check. If the
|
||||
* collection is indexed, you can also supply a value to check.
|
||||
* Note that negative numeric offsets are NOT accepted.
|
||||
*
|
||||
* @return bool TRUE if the offset exists, FALSE otherwise.
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return is_int($offset)
|
||||
? array_key_exists($offset, $this->responses)
|
||||
: array_key_exists($offset, $this->responsesIndex[$this->index]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a {@link Response} from a specified offset.
|
||||
*
|
||||
* @param int|string $offset The offset of the desired response. If the
|
||||
* collection is indexed, you can also supply the value to search for.
|
||||
*
|
||||
* @return Response The response at the specified offset.
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return is_int($offset)
|
||||
? $this->responses[$offset >= 0
|
||||
? $offset
|
||||
: count($this->responses) + $offset]
|
||||
: $this->responses[$this->responsesIndex[$this->index][$offset]];
|
||||
}
|
||||
|
||||
/**
|
||||
* N/A
|
||||
*
|
||||
* This method exists only because it is required for ArrayAccess. The
|
||||
* collection is read only.
|
||||
*
|
||||
* @param int|string $offset N/A
|
||||
* @param Response $value N/A
|
||||
*
|
||||
* @return void
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function offsetSet($offset, $value)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* N/A
|
||||
*
|
||||
* This method exists only because it is required for ArrayAccess. The
|
||||
* collection is read only.
|
||||
*
|
||||
* @param int|string $offset N/A
|
||||
*
|
||||
* @return void
|
||||
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
|
||||
*/
|
||||
public function offsetUnset($offset)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the pointer to 0, and returns the first response.
|
||||
*
|
||||
* @return Response The first response in the collection, or FALSE if the
|
||||
* collection is empty.
|
||||
*/
|
||||
public function rewind()
|
||||
{
|
||||
return $this->seek(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the position pointer to a specified position.
|
||||
*
|
||||
* @param int|string $position The position to move to. If the collection is
|
||||
* indexed, you can also supply a value to move the pointer to.
|
||||
* A non-existent index will move the pointer to "-1".
|
||||
*
|
||||
* @return Response The {@link Response} at the specified position, or FALSE
|
||||
* if the specified position is not valid.
|
||||
*/
|
||||
public function seek($position)
|
||||
{
|
||||
$this->position = is_int($position)
|
||||
? ($position >= 0
|
||||
? $position
|
||||
: count($this->responses) + $position)
|
||||
: ($this->offsetExists($position)
|
||||
? $this->responsesIndex[$this->index][$position]
|
||||
: -1);
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer forward by 1, and gets the next response.
|
||||
*
|
||||
* @return Response The next {@link Response} object, or FALSE if the
|
||||
* position is not valid.
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
++$this->position;
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the response at the current pointer position.
|
||||
*
|
||||
* @return Response The response at the current pointer position, or FALSE
|
||||
* if the position is not valid.
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
return $this->valid() ? $this->responses[$this->position] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer backwards by 1, and gets the previous response.
|
||||
*
|
||||
* @return Response The next {@link Response} object, or FALSE if the
|
||||
* position is not valid.
|
||||
*/
|
||||
public function prev()
|
||||
{
|
||||
--$this->position;
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the pointer to the last valid position, and returns the last
|
||||
* response.
|
||||
*
|
||||
* @return Response The last response in the collection, or FALSE if the
|
||||
* collection is empty.
|
||||
*/
|
||||
public function end()
|
||||
{
|
||||
$this->position = count($this->responses) - 1;
|
||||
return $this->current();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the key at the current pointer position.
|
||||
*
|
||||
* @return int The key at the current pointer position, i.e. the pointer
|
||||
* position itself, or FALSE if the position is not valid.
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
return $this->valid() ? $this->position : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the pointer is still pointing to an existing offset.
|
||||
*
|
||||
* @return bool TRUE if the pointer is valid, FALSE otherwise.
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
return $this->offsetExists($this->position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all distinct property names.
|
||||
*
|
||||
* Gets all distinct property names across all responses.
|
||||
*
|
||||
* @return array An array with all distinct property names as keys, and the
|
||||
* indexes at which they occur as values.
|
||||
*/
|
||||
public function getPropertyMap()
|
||||
{
|
||||
if (null === $this->propertyMap) {
|
||||
$properties = array();
|
||||
foreach ($this->responses as $index => $response) {
|
||||
$names = array_keys($response->getIterator()->getArrayCopy());
|
||||
foreach ($names as $name) {
|
||||
if (!isset($properties[$name])) {
|
||||
$properties[$name] = array();
|
||||
}
|
||||
$properties[$name][] = $index;
|
||||
}
|
||||
}
|
||||
$this->propertyMap = $properties;
|
||||
}
|
||||
return $this->propertyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all responses of a specified type.
|
||||
*
|
||||
* @param string $type The response type to filter by. Valid values are the
|
||||
* Response::TYPE_* constants.
|
||||
*
|
||||
* @return static A new collection with responses of the
|
||||
* specified type.
|
||||
*/
|
||||
public function getAllOfType($type)
|
||||
{
|
||||
$result = array();
|
||||
foreach (array_keys($this->responseTypes, $type, true) as $index) {
|
||||
$result[] = $this->responses[$index];
|
||||
}
|
||||
return new static($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all responses with a specified tag.
|
||||
*
|
||||
* @param string $tag The tag to filter by.
|
||||
*
|
||||
* @return static A new collection with responses having the
|
||||
* specified tag.
|
||||
*/
|
||||
public function getAllTagged($tag)
|
||||
{
|
||||
$result = array();
|
||||
foreach (array_keys($this->responseTags, $tag, true) as $index) {
|
||||
$result[] = $this->responses[$index];
|
||||
}
|
||||
return new static($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Order resones by criteria.
|
||||
*
|
||||
* @param mixed[] $criteria The criteria to order respones by. It takes the
|
||||
* form of an array where each key is the name of the property to use
|
||||
* as (N+1)th sorting key. The value of each member can be either NULL
|
||||
* (for that property, sort normally in ascending order), a single sort
|
||||
* order constant (SORT_ASC or SORT_DESC) to sort normally in the
|
||||
* specified order, an array where the first member is an order
|
||||
* constant, and the second one is sorting flags (same as built in PHP
|
||||
* array functions) or a callback.
|
||||
* If a callback is provided, it must accept two arguments
|
||||
* (the two values to be compared), and return -1, 0 or 1 if the first
|
||||
* value is respectively less than, equal to or greather than the second
|
||||
* one.
|
||||
* Each key of $criteria can also be numeric, in which case the
|
||||
* value is the name of the property, and sorting is done normally in
|
||||
* ascending order.
|
||||
*
|
||||
* @return static A new collection with the responses sorted in the
|
||||
* specified order.
|
||||
*/
|
||||
public function orderBy(array $criteria)
|
||||
{
|
||||
$this->compareBy = $criteria;
|
||||
$sortedResponses = $this->responses;
|
||||
usort($sortedResponses, array($this, 'compare'));
|
||||
return new static($sortedResponses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a method of the response pointed by the pointer.
|
||||
*
|
||||
* Calls a method of the response pointed by the pointer. This is a magic
|
||||
* PHP method, thanks to which any function you call on the collection that
|
||||
* is not defined will be redirected to the response.
|
||||
*
|
||||
* @param string $method The name of the method to call.
|
||||
* @param array $args The arguments to pass to the method.
|
||||
*
|
||||
* @return mixed Whatever the called function returns.
|
||||
*/
|
||||
public function __call($method, array $args)
|
||||
{
|
||||
return call_user_func_array(
|
||||
array($this->current(), $method),
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two respones.
|
||||
*
|
||||
* Compares two respones, based on criteria defined in
|
||||
* {@link static::$compareBy}.
|
||||
*
|
||||
* @param Response $itemA The response to compare.
|
||||
* @param Response $itemB The response to compare $a against.
|
||||
*
|
||||
* @return int Returns 0 if the two respones are equal according to every
|
||||
* criteria specified, -1 if $a should be placed before $b, and 1 if $b
|
||||
* should be placed before $a.
|
||||
*/
|
||||
protected function compare(Response $itemA, Response $itemB)
|
||||
{
|
||||
foreach ($this->compareBy as $name => $spec) {
|
||||
if (!is_string($name)) {
|
||||
$name = $spec;
|
||||
$spec = null;
|
||||
}
|
||||
|
||||
$members = array(
|
||||
0 => $itemA->getProperty($name),
|
||||
1 => $itemB->getProperty($name)
|
||||
);
|
||||
|
||||
if (is_callable($spec)) {
|
||||
uasort($members, $spec);
|
||||
} elseif ($members[0] === $members[1]) {
|
||||
continue;
|
||||
} else {
|
||||
$flags = SORT_REGULAR;
|
||||
$order = SORT_ASC;
|
||||
if (is_array($spec)) {
|
||||
list($order, $flags) = $spec;
|
||||
} elseif (null !== $spec) {
|
||||
$order = $spec;
|
||||
}
|
||||
|
||||
if (SORT_ASC === $order) {
|
||||
asort($members, $flags);
|
||||
} else {
|
||||
arsort($members, $flags);
|
||||
}
|
||||
}
|
||||
return (key($members) === 0) ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
44
system/autoload/PEAR2/Net/RouterOS/SocketException.php
Normal file
44
system/autoload/PEAR2/Net/RouterOS/SocketException.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
/**
|
||||
* Base of this class.
|
||||
*/
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Exception thrown when something goes wrong with the connection.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class SocketException extends RuntimeException implements Exception
|
||||
{
|
||||
const CODE_SERVICE_INCOMPATIBLE = 10200;
|
||||
const CODE_CONNECTION_FAIL = 100;
|
||||
const CODE_QUERY_SEND_FAIL = 30600;
|
||||
const CODE_REQUEST_SEND_FAIL = 40900;
|
||||
const CODE_NO_DATA = 50000;
|
||||
}
|
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* RouterOS API client implementation.
|
||||
*
|
||||
* RouterOS is the flag product of the company MikroTik and is a powerful router software. One of its many abilities is to allow control over it via an API. This package provides a client for that API, in turn allowing you to use PHP to control RouterOS hosts.
|
||||
*
|
||||
* PHP version 5
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @copyright 2011 Vasil Rangelov
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @version 1.0.0b5
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
/**
|
||||
* The namespace declaration.
|
||||
*/
|
||||
namespace PEAR2\Net\RouterOS;
|
||||
|
||||
use UnexpectedValueException as U;
|
||||
|
||||
/**
|
||||
* Exception thrown when encountering an invalid value in a function argument.
|
||||
*
|
||||
* @category Net
|
||||
* @package PEAR2_Net_RouterOS
|
||||
* @author Vasil Rangelov <boen.robot@gmail.com>
|
||||
* @license http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
|
||||
* @link http://pear2.php.net/PEAR2_Net_RouterOS
|
||||
*/
|
||||
class UnexpectedValueException extends U implements Exception
|
||||
{
|
||||
const CODE_CALLBACK_INVALID = 10502;
|
||||
const CODE_ACTION_UNKNOWN = 30100;
|
||||
const CODE_RESPONSE_TYPE_UNKNOWN = 50100;
|
||||
|
||||
/**
|
||||
* @var mixed The unexpected value.
|
||||
*/
|
||||
private $_value;
|
||||
|
||||
/**
|
||||
* Creates a new UnexpectedValueException.
|
||||
*
|
||||
* @param string $message The Exception message to throw.
|
||||
* @param int $code The Exception code.
|
||||
* @param \Exception $previous The previous exception used for the exception
|
||||
* chaining.
|
||||
* @param mixed $value The unexpected value.
|
||||
*/
|
||||
public function __construct(
|
||||
$message,
|
||||
$code = 0,
|
||||
$previous = null,
|
||||
$value = null
|
||||
) {
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->_value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the unexpected value.
|
||||
*
|
||||
* @return mixed The unexpected value.
|
||||
*/
|
||||
public function getValue()
|
||||
{
|
||||
return $this->_value;
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreStart
|
||||
// String representation is not reliable in testing
|
||||
|
||||
/**
|
||||
* Returns a string representation of the exception.
|
||||
*
|
||||
* @return string The exception as a string.
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return parent::__toString() . "\nValue:{$this->_value}";
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
}
|
1177
system/autoload/PEAR2/Net/RouterOS/Util.php
Normal file
1177
system/autoload/PEAR2/Net/RouterOS/Util.php
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user