Example #1
0
 /**
  * Puts a file on RouterOS's file system.
  * 
  * Puts a file on RouterOS's file system, regardless of the current menu.
  * Note that this is a **VERY VERY VERY** time consuming method - it takes a
  * minimum of a little over 4 seconds, most of which are in sleep. It waits
  * 2 seconds after a file is first created (required to actually start
  * writing to the file), and another 2 seconds after its contents is written
  * (performed in order to verify success afterwards).
  * Similarly for removal (when $data is NULL) - there are two seconds in
  * sleep, used to verify the file was really deleted.
  * 
  * If you want an efficient way of transferring files, use (T)FTP.
  * If you want an efficient way of removing files, use
  * {@link static::setMenu()} to move to the "/file" menu, and call
  * {@link static::remove()} without performing verification afterwards.
  * 
  * @param string               $filename  The filename to write data in.
  * @param string|resource|null $data      The data the file is going to have
  *     as a string or a seekable stream.
  *     Setting the value to NULL removes a file 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.
  * @param bool                 $overwrite Whether to overwrite the file if
  *     it exists.
  * 
  * @return bool TRUE on success, FALSE on failure.
  */
 public function filePutContents($filename, $data, $overwrite = false)
 {
     $printRequest = new Request('/file/print .proplist=""', Query::where('name', $filename));
     $fileExists = count($this->client->sendSync($printRequest)) > 1;
     if (null === $data) {
         if (!$fileExists) {
             return false;
         }
         $removeRequest = new Request('/file/remove');
         $this->client->sendSync($removeRequest->setArgument('numbers', $filename));
         //Required for RouterOS to REALLY remove the file.
         sleep(2);
         return !(count($this->client->sendSync($printRequest)) > 1);
     }
     if (!$overwrite && $fileExists) {
         return false;
     }
     $result = $this->client->sendSync($printRequest->setArgument('file', $filename));
     if (count($result->getAllOfType(Response::TYPE_ERROR)) > 0) {
         return false;
     }
     //Required for RouterOS to write the initial file.
     sleep(2);
     $setRequest = new Request('/file/set contents=""');
     $setRequest->setArgument('numbers', $filename);
     $this->client->sendSync($setRequest);
     $this->client->sendSync($setRequest->setArgument('contents', $data));
     //Required for RouterOS to write the file's new contents.
     sleep(2);
     $fileSize = $this->client->sendSync($printRequest->setArgument('file', null)->setArgument('.proplist', 'size'))->getProperty('size');
     if (Stream::isStream($fileSize)) {
         $fileSize = stream_get_contents($fileSize);
     }
     if (Communicator::isSeekableStream($data)) {
         return Communicator::seekableStreamLength($data) == $fileSize;
     } else {
         return sprintf('%u', strlen((string) $data)) === $fileSize;
     }
 }
Example #2
0
 /**
  * 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;
             }
         }
     }
 }
Example #3
0
 /**
  * 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;
 }
Example #4
0
 /**
  * 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;
     }
 }
 public function testSetDefaultCharset()
 {
     $com = new Communicator(HOSTNAME, PORT);
     $this->assertNull($com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull($com->getCharset(Communicator::CHARSET_LOCAL));
     Communicator::setDefaultCharset('windows-1251');
     $this->assertNull($com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull($com->getCharset(Communicator::CHARSET_LOCAL));
     $com = new Communicator(HOSTNAME, PORT);
     $this->assertEquals('windows-1251', $com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertEquals('windows-1251', $com->getCharset(Communicator::CHARSET_LOCAL));
     Communicator::setDefaultCharset(array(Communicator::CHARSET_REMOTE => 'ISO-8859-1', Communicator::CHARSET_LOCAL => 'ISO-8859-1'));
     $this->assertEquals('windows-1251', $com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertEquals('windows-1251', $com->getCharset(Communicator::CHARSET_LOCAL));
     $com = new Communicator(HOSTNAME, PORT);
     $this->assertEquals('ISO-8859-1', $com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertEquals('ISO-8859-1', $com->getCharset(Communicator::CHARSET_LOCAL));
     Communicator::setDefaultCharset(null);
     $this->assertEquals('ISO-8859-1', $com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertEquals('ISO-8859-1', $com->getCharset(Communicator::CHARSET_LOCAL));
     $com = new Communicator(HOSTNAME, PORT);
     $this->assertNull($com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull($com->getCharset(Communicator::CHARSET_LOCAL));
     Communicator::setDefaultCharset('windows-1251', Communicator::CHARSET_REMOTE);
     Communicator::setDefaultCharset('ISO-8859-1', Communicator::CHARSET_LOCAL);
     $this->assertNull($com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull($com->getCharset(Communicator::CHARSET_LOCAL));
     $com = new Communicator(HOSTNAME, PORT);
     $this->assertEquals('windows-1251', $com->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertEquals('ISO-8859-1', $com->getCharset(Communicator::CHARSET_LOCAL));
     Communicator::setDefaultCharset(null);
 }
Example #6
0
    fwrite(STDOUT, implode("\n", array(implode($cSep, array(str_pad('MODE', $cColumns['mode'], ' ', STR_PAD_RIGHT), str_pad('LENGTH', $cColumns['length'], ' ', STR_PAD_BOTH), str_pad('LENGTH', $cColumns['encodedLength'], ' ', STR_PAD_BOTH), ' CONTENTS')), implode($cSep, array(str_repeat(' ', $cColumns['mode']), str_pad('(decoded)', $cColumns['length'], ' ', STR_PAD_BOTH), str_pad('(encoded)', $cColumns['encodedLength'], ' ', STR_PAD_BOTH), '')), implode('-|-', array(str_repeat('-', $cColumns['mode']), str_repeat('-', $cColumns['length']), str_repeat('-', $cColumns['encodedLength']), str_repeat('-', $cColumns['contents']))))) . "\n");
    $cRegexWrap = '/([^\\n]{1,' . $cColumns['contents'] . '})/sS';
    $printWord = function ($mode, $word, $msg = '') use($cSep, $cColumns, $cRegexWrap, $cColors) {
        $wordFragments = preg_split($cRegexWrap, $word, null, PREG_SPLIT_DELIM_CAPTURE);
        for ($i = 0, $l = count($wordFragments); $i < $l; $i += 2) {
            unset($wordFragments[$i]);
        }
        if ('' !== $cColors['']) {
            $wordFragments = str_replace("", "[27@", $wordFragments);
        }
        $isAbnormal = 'ERR' === $mode || 'NOTE' === $mode;
        if ($isAbnormal) {
            $details = str_pad($msg, $cColumns['length'] + $cColumns['encodedLength'] + 3, ' ', STR_PAD_BOTH);
        } else {
            $length = strlen($word);
            $lengthBytes = RouterOS\Communicator::encodeLength($length);
            $encodedLength = '';
            for ($i = 0, $l = strlen($lengthBytes); $i < $l; ++$i) {
                $encodedLength .= str_pad(dechex(ord($lengthBytes[$i])), 2, '0', STR_PAD_LEFT);
            }
            $details = str_pad($length, $cColumns['length'], ' ', STR_PAD_LEFT) . $cSep . str_pad('0x' . strtoupper($encodedLength), $cColumns['encodedLength'], ' ', STR_PAD_LEFT);
        }
        fwrite(STDOUT, $cColors[$mode] . str_pad($mode, $cColumns['mode'], ' ', STR_PAD_RIGHT) . $cColors[''] . "{$cSep}{$details}{$cSep}{$cColors[$mode]}" . implode("\n{$cColors['']}" . str_repeat(' ', $cColumns['mode']) . $cSep . implode($isAbnormal ? '   ' : $cSep, array(str_repeat(' ', $cColumns['length']), str_repeat(' ', $cColumns['encodedLength']))) . $cSep . $cColors[$mode], $wordFragments) . "\n{$cColors['']}");
    };
} else {
    $printWord = function ($mode, $word, $msg = '') use($cColors) {
        if ('' !== $cColors['']) {
            $word = str_replace("", "[27@", $word);
            $msg = str_replace("", "[27@", $msg);
        }
        if ('ERR' === $mode || 'NOTE' === $mode) {
 public function testNormalAnsiConnection()
 {
     $oldCharsets = Communicator::setDefaultCharset(array(Communicator::CHARSET_LOCAL => 'UTF-8', Communicator::CHARSET_REMOTE => ANSI_PASSWORD_CHARSET));
     try {
         $routerOS = new Client(\HOSTNAME, ANSI_USERNAME, ANSI_PASSWORD, PORT);
         $this->assertInstanceOf(ROS_NAMESPACE . '\\Client', $routerOS, 'Object initialization failed.');
         Communicator::setDefaultCharset($oldCharsets);
     } catch (Exception $e) {
         Communicator::setDefaultCharset($oldCharsets);
         $this->fail('Unable to connect normally:' . (string) $e);
     }
 }
Example #8
0
 /**
  * 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;
     }
 }
Example #9
0
 /**
  * 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;
 }
Example #10
0
 public function testDefaultCharsets()
 {
     $this->assertNull($this->object->getCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull($this->object->getCharset(Communicator::CHARSET_LOCAL));
     $this->assertEquals(array(Communicator::CHARSET_REMOTE => null, Communicator::CHARSET_LOCAL => null), $this->object->getCharset(Communicator::CHARSET_ALL));
     $this->assertEquals(array(Communicator::CHARSET_REMOTE => null, Communicator::CHARSET_LOCAL => null), Communicator::getDefaultCharset(Communicator::CHARSET_ALL));
     $this->assertNull(Communicator::getDefaultCharset(Communicator::CHARSET_REMOTE));
     $this->assertNull(Communicator::getDefaultCharset(Communicator::CHARSET_LOCAL));
 }