mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-25 18:19:22 +03:00
Improve html parsing
This commit is contained in:
parent
7ad02eaf6a
commit
58b9eed8e8
2 changed files with 170 additions and 121 deletions
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "RainLoop",
|
"name": "RainLoop",
|
||||||
"title": "RainLoop Webmail",
|
"title": "RainLoop Webmail",
|
||||||
"version": "1.10.0",
|
"version": "1.10.0",
|
||||||
"release": "107",
|
"release": "108",
|
||||||
"ownCloudPackageVersion": "4.16",
|
"ownCloudPackageVersion": "4.16",
|
||||||
"description": "Simple, modern & fast web-based email client",
|
"description": "Simple, modern & fast web-based email client",
|
||||||
"homepage": "http://rainloop.net",
|
"homepage": "http://rainloop.net",
|
||||||
|
|
|
||||||
|
|
@ -28,12 +28,10 @@ class HtmlUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sText
|
* @param string $sText
|
||||||
* @param string $sHtmlAttrs = ''
|
|
||||||
* @param string $sBodyAttrs = ''
|
|
||||||
*
|
*
|
||||||
* @return \DOMDocument|bool
|
* @return \DOMDocument|bool
|
||||||
*/
|
*/
|
||||||
public static function GetDomFromText($sText, $sHtmlAttrs = '', $sBodyAttrs = '')
|
public static function GetDomFromText($sText)
|
||||||
{
|
{
|
||||||
$bState = true;
|
$bState = true;
|
||||||
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors'))
|
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors'))
|
||||||
|
|
@ -45,13 +43,20 @@ class HtmlUtils
|
||||||
$oDom->encoding = 'UTF-8';
|
$oDom->encoding = 'UTF-8';
|
||||||
$oDom->strictErrorChecking = false;
|
$oDom->strictErrorChecking = false;
|
||||||
$oDom->formatOutput = false;
|
$oDom->formatOutput = false;
|
||||||
|
$oDom->preserveWhiteSpace = false;
|
||||||
|
|
||||||
|
$sHtmlAttrs = $sBodyAttrs = '';
|
||||||
|
|
||||||
|
$sText = \MailSo\Base\HtmlUtils::FixSchemas($sText);
|
||||||
|
$sText = \MailSo\Base\HtmlUtils::ClearFastTags($sText);
|
||||||
|
$sText = \MailSo\Base\HtmlUtils::ClearBodyAndHtmlTag($sText, $sHtmlAttrs, $sBodyAttrs);
|
||||||
|
|
||||||
@$oDom->loadHTML('<'.'?xml version="1.0" encoding="utf-8"?'.'>'.
|
@$oDom->loadHTML('<'.'?xml version="1.0" encoding="utf-8"?'.'>'.
|
||||||
'<html '.$sHtmlAttrs.'><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body '.$sBodyAttrs.'>'.$sText.'</body></html>');
|
'<html '.$sHtmlAttrs.'><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body '.$sBodyAttrs.'><div data-wrp="rainloop">'.$sText.'</div></body></html>');
|
||||||
|
|
||||||
@$oDom->normalizeDocument();
|
@$oDom->normalizeDocument();
|
||||||
|
|
||||||
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_use_internal_errors'))
|
if (\MailSo\Base\Utils::FunctionExistsAndEnabled('libxml_clear_errors'))
|
||||||
{
|
{
|
||||||
@\libxml_clear_errors();
|
@\libxml_clear_errors();
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +69,91 @@ class HtmlUtils
|
||||||
return $oDom;
|
return $oDom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DOMElement $oElement
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public static function GetElementAttributesAsArray($oElement)
|
||||||
|
{
|
||||||
|
$aResult = array();
|
||||||
|
if ($oElement)
|
||||||
|
{
|
||||||
|
if ($oElement->hasAttributes() && isset($oElement->attributes) && $oElement->attributes)
|
||||||
|
{
|
||||||
|
foreach ($oElement->attributes as $oAttr)
|
||||||
|
{
|
||||||
|
if ($oAttr && !empty($oAttr->nodeName))
|
||||||
|
{
|
||||||
|
$sAttrName = \trim(\strtolower($oAttr->nodeName));
|
||||||
|
$aResult[$sAttrName] = $oAttr->nodeValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $aResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param \DOMDocument $oDom
|
||||||
|
* @param bool $bWrapByFakeHtmlAndBodyDiv = true
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public static function GetTextFromDom($oDom, $bWrapByFakeHtmlAndBodyDiv = true)
|
||||||
|
{
|
||||||
|
$sResult = '';
|
||||||
|
|
||||||
|
$aHtmlAttrs = $aBodylAttrs = array();
|
||||||
|
if ($bWrapByFakeHtmlAndBodyDiv)
|
||||||
|
{
|
||||||
|
$oHtml = $oDom->getElementsByTagName('html')->item(0);
|
||||||
|
$oBody = $oDom->getElementsByTagName('body')->item(0);
|
||||||
|
|
||||||
|
$aHtmlAttrs = \MailSo\Base\HtmlUtils::GetElementAttributesAsArray($oHtml);
|
||||||
|
$aBodylAttrs = \MailSo\Base\HtmlUtils::GetElementAttributesAsArray($oBody);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oDiv = $oDom->getElementsByTagName('div')->item(0);
|
||||||
|
if ($oDiv && $oDiv->hasAttribute('data-wrp') && 'rainloop' === $oDiv->getAttribute('data-wrp'))
|
||||||
|
{
|
||||||
|
$oDiv->removeAttribute('data-wrp');
|
||||||
|
if ($bWrapByFakeHtmlAndBodyDiv)
|
||||||
|
{
|
||||||
|
$oWrap = $oDom->createElement('div');
|
||||||
|
|
||||||
|
$oWrap->setAttribute('data-x-div-type', 'html');
|
||||||
|
foreach ($aHtmlAttrs as $sKey => $sValue)
|
||||||
|
{
|
||||||
|
$oWrap->setAttribute($sKey, $sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oDiv->setAttribute('data-x-div-type', 'body');
|
||||||
|
foreach ($aBodylAttrs as $sKey => $sValue)
|
||||||
|
{
|
||||||
|
$oDiv->setAttribute($sKey, $sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
$oWrap->appendChild($oDiv);
|
||||||
|
$sResult = \trim($oDom->saveHTML($oWrap));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sResult = \trim($oDom->saveHTML($oDiv));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$sResult = \trim($oDom->saveHTML());
|
||||||
|
}
|
||||||
|
|
||||||
|
$sResult = \str_replace(\MailSo\Base\HtmlUtils::$KOS, ':', $sResult);
|
||||||
|
$sResult = \MailSo\Base\Utils::StripSpaces($sResult);
|
||||||
|
|
||||||
|
return $sResult;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sHtml
|
* @param string $sHtml
|
||||||
* @param string $sHtmlAttrs = '
|
* @param string $sHtmlAttrs = '
|
||||||
|
|
@ -118,14 +208,54 @@ class HtmlUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $sHtml
|
* @param string $sHtml
|
||||||
* @param bool $bClearStyleAndHead = true
|
|
||||||
*
|
*
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function ClearTags($sHtml, $bClearStyleAndHead = true)
|
public static function ClearFastTags($sHtml)
|
||||||
|
{
|
||||||
|
return \preg_replace(array(
|
||||||
|
'/<p[^>]*><\/p>/i',
|
||||||
|
'/<!doctype[^>]*>/msi',
|
||||||
|
'/<\?xml [^>]*\?>/msi'
|
||||||
|
), '', $sHtml);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $oDom
|
||||||
|
*/
|
||||||
|
public static function ClearComments(&$oDom)
|
||||||
|
{
|
||||||
|
$aRemove = array();
|
||||||
|
|
||||||
|
$oXpath = new \DOMXpath($oDom);
|
||||||
|
$oComments = $oXpath->query('//comment()');
|
||||||
|
if ($oComments)
|
||||||
|
{
|
||||||
|
foreach ($oComments as $oComment)
|
||||||
|
{
|
||||||
|
$aRemove[] = $oComment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($oXpath, $oComments);
|
||||||
|
|
||||||
|
foreach ($aRemove as /* @var $oElement \DOMElement */ $oElement)
|
||||||
|
{
|
||||||
|
if (isset($oElement->parentNode))
|
||||||
|
{
|
||||||
|
@$oElement->parentNode->removeChild($oElement);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $oDom
|
||||||
|
* @param bool $bClearStyleAndHead = true
|
||||||
|
*/
|
||||||
|
public static function ClearTags(&$oDom, $bClearStyleAndHead = true)
|
||||||
{
|
{
|
||||||
$aRemoveTags = array(
|
$aRemoveTags = array(
|
||||||
'link', 'base', 'meta', 'title', 'script', 'bgsound', 'keygen', 'source',
|
'svg', 'link', 'base', 'meta', 'title', 'x-script', 'script', 'bgsound', 'keygen', 'source',
|
||||||
'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio', 'area', 'map'
|
'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset', 'video', 'audio', 'area', 'map'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -135,61 +265,30 @@ class HtmlUtils
|
||||||
$aRemoveTags[] = 'style';
|
$aRemoveTags[] = 'style';
|
||||||
}
|
}
|
||||||
|
|
||||||
$aToRemove = array(
|
$aRemove = array();
|
||||||
'/<p[^>]*><\/p>/i',
|
$aNodes = $oDom->getElementsByTagName('*');
|
||||||
'/<!doctype[^>]*>/msi',
|
foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement)
|
||||||
'/<\?xml [^>]*\?>/msi'
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach ($aRemoveTags as $sTag)
|
|
||||||
{
|
{
|
||||||
$aToRemove[] = '\'<'.$sTag.'[^>]*>.*?</[\s]*'.$sTag.'>\'msi';
|
if ($oElement)
|
||||||
$aToRemove[] = '\'<'.$sTag.'[^>]*>\'msi';
|
{
|
||||||
$aToRemove[] = '\'</[\s]*'.$sTag.'[^>]*>\'msi';
|
$sTagNameLower = \strtolower($oElement->tagName);
|
||||||
|
if ('' !== $sTagNameLower && \in_array($sTagNameLower, $aRemoveTags))
|
||||||
|
{
|
||||||
|
$aRemove[] = @$oElement;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return \preg_replace($aToRemove, '', $sHtml);
|
foreach ($aRemove as /* @var $oElement \DOMElement */ $oElement)
|
||||||
}
|
{
|
||||||
|
if (isset($oElement->parentNode))
|
||||||
/**
|
{
|
||||||
* @param string $sHtml
|
@$oElement->parentNode->removeChild($oElement);
|
||||||
*
|
}
|
||||||
* @return string
|
}
|
||||||
*/
|
|
||||||
public static function ClearOn($sHtml)
|
|
||||||
{
|
|
||||||
$aToReplace = array(
|
|
||||||
'/on(Blur)/si',
|
|
||||||
'/on(Change)/si',
|
|
||||||
'/on(Click)/si',
|
|
||||||
'/on(DblClick)/si',
|
|
||||||
'/on(Error)/si',
|
|
||||||
'/on(Focus)/si',
|
|
||||||
'/on(FormChange)/si',
|
|
||||||
'/on(KeyDown)/si',
|
|
||||||
'/on(KeyPress)/si',
|
|
||||||
'/on(KeyUp)/si',
|
|
||||||
'/on(Load)/si',
|
|
||||||
'/on(MouseDown)/si',
|
|
||||||
'/on(MouseEnter)/si',
|
|
||||||
'/on(MouseLeave)/si',
|
|
||||||
'/on(MouseMove)/si',
|
|
||||||
'/on(MouseOut)/si',
|
|
||||||
'/on(MouseOver)/si',
|
|
||||||
'/on(MouseUp)/si',
|
|
||||||
'/on(Move)/si',
|
|
||||||
'/on(Resize)/si',
|
|
||||||
'/on(ResizeEnd)/si',
|
|
||||||
'/on(ResizeStart)/si',
|
|
||||||
'/on(Scroll)/si',
|
|
||||||
'/on(Select)/si',
|
|
||||||
'/on(Submit)/si',
|
|
||||||
'/on(Unload)/si'
|
|
||||||
);
|
|
||||||
|
|
||||||
return \preg_replace($aToReplace, 'оn\\1', $sHtml);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// public static function ClearStyleUrlValueParserHelper($oUrlValue, $oRule, $oRuleSet,
|
// public static function ClearStyleUrlValueParserHelper($oUrlValue, $oRule, $oRuleSet,
|
||||||
// $oElem = null,
|
// $oElem = null,
|
||||||
// &$bHasExternals = false, &$aFoundCIDs = array(),
|
// &$bHasExternals = false, &$aFoundCIDs = array(),
|
||||||
|
|
@ -410,6 +509,7 @@ class HtmlUtils
|
||||||
//
|
//
|
||||||
// return $mResult;
|
// return $mResult;
|
||||||
// }
|
// }
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
|
@ -592,7 +692,7 @@ class HtmlUtils
|
||||||
->CompileText()
|
->CompileText()
|
||||||
;
|
;
|
||||||
|
|
||||||
$oSubDom = \MailSo\Base\HtmlUtils::GetDomFromText('<html><body>'.$sText.'</body></html>');
|
$oSubDom = \MailSo\Base\HtmlUtils::GetDomFromText($sText);
|
||||||
if ($oSubDom)
|
if ($oSubDom)
|
||||||
{
|
{
|
||||||
$oBodyNodes = $oSubDom->getElementsByTagName('body');
|
$oBodyNodes = $oSubDom->getElementsByTagName('body');
|
||||||
|
|
@ -701,16 +801,8 @@ class HtmlUtils
|
||||||
|
|
||||||
$bHasExternals = false;
|
$bHasExternals = false;
|
||||||
|
|
||||||
$sHtml = \MailSo\Base\HtmlUtils::FixSchemas($sHtml);
|
|
||||||
|
|
||||||
$sHtml = \MailSo\Base\HtmlUtils::ClearTags($sHtml, false);
|
|
||||||
$sHtml = \MailSo\Base\HtmlUtils::ClearOn($sHtml);
|
|
||||||
|
|
||||||
$sHtmlAttrs = $sBodyAttrs = '';
|
|
||||||
$sHtml = \MailSo\Base\HtmlUtils::ClearBodyAndHtmlTag($sHtml, $sHtmlAttrs, $sBodyAttrs);
|
|
||||||
|
|
||||||
// Dom Part
|
// Dom Part
|
||||||
$oDom = \MailSo\Base\HtmlUtils::GetDomFromText($sHtml, $sHtmlAttrs, $sBodyAttrs);
|
$oDom = \MailSo\Base\HtmlUtils::GetDomFromText($sHtml);
|
||||||
unset($sHtml);
|
unset($sHtml);
|
||||||
|
|
||||||
if ($oDom)
|
if ($oDom)
|
||||||
|
|
@ -731,40 +823,8 @@ class HtmlUtils
|
||||||
\MailSo\Base\HtmlUtils::FindLinksInDOM($oDom);
|
\MailSo\Base\HtmlUtils::FindLinksInDOM($oDom);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oXpath = new \DOMXpath($oDom);
|
\MailSo\Base\HtmlUtils::ClearComments($oDom);
|
||||||
$oComments = $oXpath->query('//comment()');
|
\MailSo\Base\HtmlUtils::ClearTags($oDom);
|
||||||
if ($oComments)
|
|
||||||
{
|
|
||||||
foreach ($oComments as $oComment)
|
|
||||||
{
|
|
||||||
if (isset($oComment->parentNode))
|
|
||||||
{
|
|
||||||
@$oComment->parentNode->removeChild($oComment);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($oXpath, $oComments);
|
|
||||||
|
|
||||||
$aNodes = $oDom->getElementsByTagName('*');
|
|
||||||
foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement)
|
|
||||||
{
|
|
||||||
if ($oElement)
|
|
||||||
{
|
|
||||||
$sTagNameLower = \strtolower($oElement->tagName);
|
|
||||||
|
|
||||||
if ('' !== $sTagNameLower && \in_array($sTagNameLower, array('svg', 'head', 'link',
|
|
||||||
'base', 'meta', 'title', 'style', 'x-script', 'script', 'bgsound', 'keygen', 'source',
|
|
||||||
'object', 'embed', 'applet', 'mocha', 'iframe', 'frame', 'frameset',
|
|
||||||
'video', 'audio', 'area', 'map')))
|
|
||||||
{
|
|
||||||
if (isset($oElement->parentNode))
|
|
||||||
{
|
|
||||||
@$oElement->parentNode->removeChild($oElement);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$sLinkColor = '';
|
$sLinkColor = '';
|
||||||
$aNodes = $oDom->getElementsByTagName('*');
|
$aNodes = $oDom->getElementsByTagName('*');
|
||||||
|
|
@ -873,7 +933,7 @@ class HtmlUtils
|
||||||
{
|
{
|
||||||
$sAttrName = \trim(\strtolower($oAttr->nodeName));
|
$sAttrName = \trim(\strtolower($oAttr->nodeName));
|
||||||
if ('on' === \substr($sAttrName, 0, 2) || in_array($sAttrName, array(
|
if ('on' === \substr($sAttrName, 0, 2) || in_array($sAttrName, array(
|
||||||
'id', 'class', 'contenteditable', 'designmode', 'formaction',
|
'id', 'class', 'contenteditable', 'designmode', 'formaction', 'manifest',
|
||||||
'data-bind', 'data-reactid', 'xmlns', 'srcset', 'data-x-skip-style',
|
'data-bind', 'data-reactid', 'xmlns', 'srcset', 'data-x-skip-style',
|
||||||
'fscommand', 'seeksegmenttime'
|
'fscommand', 'seeksegmenttime'
|
||||||
)))
|
)))
|
||||||
|
|
@ -1036,22 +1096,12 @@ class HtmlUtils
|
||||||
$oElement->removeAttribute('data-x-skip-style');
|
$oElement->removeAttribute('data-x-skip-style');
|
||||||
}
|
}
|
||||||
|
|
||||||
$sResult = $oDom->saveHTML();
|
$sResult = \MailSo\Base\HtmlUtils::GetTextFromDom($oDom);
|
||||||
}
|
}
|
||||||
|
|
||||||
unset($oDom);
|
unset($oDom);
|
||||||
|
|
||||||
$sResult = \MailSo\Base\HtmlUtils::ClearTags($sResult);
|
return $sResult;
|
||||||
|
|
||||||
$sHtmlAttrs = $sBodyAttrs = '';
|
|
||||||
$sResult = \MailSo\Base\HtmlUtils::ClearBodyAndHtmlTag($sResult, $sHtmlAttrs, $sBodyAttrs);
|
|
||||||
$sResult = '<div data-x-div-type="body" '.$sBodyAttrs.'>'.$sResult.'</div>';
|
|
||||||
$sResult = '<div data-x-div-type="html" '.$sHtmlAttrs.'>'.$sResult.'</div>';
|
|
||||||
|
|
||||||
$sResult = \str_replace(\MailSo\Base\HtmlUtils::$KOS, ':', $sResult);
|
|
||||||
$sResult = \MailSo\Base\Utils::StripSpaces($sResult);
|
|
||||||
|
|
||||||
return \trim($sResult);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -1065,6 +1115,8 @@ class HtmlUtils
|
||||||
public static function BuildHtml($sHtml, &$aFoundCids = array(), &$mFoundDataURL = null, &$aFoundedContentLocationUrls = array())
|
public static function BuildHtml($sHtml, &$aFoundCids = array(), &$mFoundDataURL = null, &$aFoundedContentLocationUrls = array())
|
||||||
{
|
{
|
||||||
$oDom = \MailSo\Base\HtmlUtils::GetDomFromText($sHtml);
|
$oDom = \MailSo\Base\HtmlUtils::GetDomFromText($sHtml);
|
||||||
|
|
||||||
|
\MailSo\Base\HtmlUtils::ClearTags($oDom);
|
||||||
unset($sHtml);
|
unset($sHtml);
|
||||||
|
|
||||||
$aNodes = $oDom->getElementsByTagName('*');
|
$aNodes = $oDom->getElementsByTagName('*');
|
||||||
|
|
@ -1197,14 +1249,11 @@ class HtmlUtils
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$sResult = $oDom->saveHTML();
|
$sResult = \MailSo\Base\HtmlUtils::GetTextFromDom($oDom, false);
|
||||||
unset($oDom);
|
unset($oDom);
|
||||||
|
|
||||||
$sResult = \MailSo\Base\HtmlUtils::ClearTags($sResult);
|
|
||||||
$sResult = \MailSo\Base\HtmlUtils::ClearBodyAndHtmlTag($sResult);
|
|
||||||
|
|
||||||
return '<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>'.
|
return '<!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head>'.
|
||||||
'<body>'.\trim($sResult).'</body></html>';
|
'<body>'.$sResult.'</body></html>';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue