$1OK$2', '$1WARNING$2', '$1CRITICAL$2', '$1UNKNOWN$2', '$1UP$2', '$1DOWN$2', '@@@@@@', ); /** * Patterns to be replaced in html plugin output * * @var array */ protected static $htmlPatterns = array( '~\\\t~', '~\\\n~', '~hookRenderer = (new \Icinga\Module\Monitoring\Web\Helper\PluginOutputHookRenderer())->registerHooks(); } /** * Render plugin output * * @param string $output * @param bool $raw * @param string $command Check command * * @return string */ public function pluginOutput($output, $raw = false, $command = null) { if (empty($output)) { return ''; } if ($command !== null) { $output = $this->hookRenderer->render($command, $output, ! $raw); } if (preg_match('~<\w+(?>\s\w+=[^>]*)?>~', $output)) { // HTML $output = HtmlPurifier::process(preg_replace( self::$htmlPatterns, self::$htmlReplacements, $output )); $isHtml = true; } else { // Plaintext $output = preg_replace( self::$txtPatterns, self::$txtReplacements, // Not using the view here to escape this. The view sets `double_encode` to true htmlspecialchars($output, ENT_COMPAT | ENT_SUBSTITUTE | ENT_HTML5, View::CHARSET, false) ); $isHtml = false; } $output = trim($output); // Add zero-width space after commas which are not followed by a whitespace character // in oder to help browsers to break words in plugin output $output = preg_replace('/,(?=[^\s])/', ',​', $output); if (! $raw) { if ($isHtml) { $output = $this->processHtml($output); $output = '
' . $output . '
'; } else { $output = '
' . $output . '
'; } } return $output; } /** * Replace classic Icinga CGI links with Icinga Web 2 links and color state information, if any * * @param string $html * * @return string */ protected function processHtml($html) { $pattern = '/[([](OK|WARNING|CRITICAL|UNKNOWN|UP|DOWN)[)\]]/'; $doc = new DOMDocument(); $doc->loadXML('
' . $html . '
', LIBXML_NOERROR | LIBXML_NOWARNING); $dom = new RecursiveIteratorIterator(new DomNodeIterator($doc), RecursiveIteratorIterator::SELF_FIRST); $nodesToRemove = array(); foreach ($dom as $node) { /** @var \DOMNode $node */ if ($node->nodeType === XML_TEXT_NODE) { $start = 0; while (preg_match($pattern, $node->nodeValue, $match, PREG_OFFSET_CAPTURE, $start)) { $offsetLeft = $match[0][1]; $matchLength = strlen($match[0][0]); $leftLength = $offsetLeft - $start; // if there is text before the match if ($leftLength) { // create node for leading text $text = new DOMText(substr($node->nodeValue, $start, $leftLength)); $node->parentNode->insertBefore($text, $node); } // create the new element for the match $span = $doc->createElement('span', $match[0][0]); $span->setAttribute('class', 'state-' . strtolower($match[1][0])); $node->parentNode->insertBefore($span, $node); // start for next match $start = $offsetLeft + $matchLength; } if ($start) { // is there text left? if (strlen($node->nodeValue) > $start) { // create node for trailing text $text = new DOMText(substr($node->nodeValue, $start)); $node->parentNode->insertBefore($text, $node); } // delete the old node later $nodesToRemove[] = $node; } } elseif ($node->nodeType === XML_ELEMENT_NODE) { /** @var \DOMElement $node */ if ($node->tagName === 'a' && preg_match('~^/cgi\-bin/status\.cgi\?(.+)$~', $node->getAttribute('href'), $match) ) { parse_str($match[1], $params); if (isset($params['host'])) { $node->setAttribute( 'href', $this->view->baseUrl('/monitoring/host/show?host=' . urlencode($params['host'])) ); } } } } foreach ($nodesToRemove as $node) { /** @var \DOMNode $node */ $node->parentNode->removeChild($node); } return substr($doc->saveHTML(), 5, -7); } }