diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php index b38670d80..0a3c96b94 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Base/HtmlUtils.php @@ -272,7 +272,7 @@ class HtmlUtils $sHtml = \str_replace('', '', $sHtml); } - $sHtml = \str_replace('', '', $sHtml); + $sHtml = \str_replace('', '', $sHtml); $sHtml = \str_replace('', '', $sHtml); return $sHtml; @@ -337,16 +337,23 @@ class HtmlUtils $aRemoveTags[] = 'style'; } + $aHtmlAllowedTags = isset(\MailSo\Config::$HtmlStrictAllowedTags) && + \is_array(\MailSo\Config::$HtmlStrictAllowedTags) && 0 < \count(\MailSo\Config::$HtmlStrictAllowedTags) ? + \MailSo\Config::$HtmlStrictAllowedTags : null; + $aRemove = array(); $aNodes = $oDom->getElementsByTagName('*'); foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement) { if ($oElement) { - $sTagNameLower = \strtolower($oElement->tagName); - if ('' !== $sTagNameLower && \in_array($sTagNameLower, $aRemoveTags)) + $sTagNameLower = \trim(\strtolower($oElement->tagName)); + if ('' !== $sTagNameLower) { - $aRemove[] = @$oElement; + if (\in_array($sTagNameLower, $aRemoveTags) || ($aHtmlAllowedTags && !\in_array($sTagNameLower, $aHtmlAllowedTags))) + { + $aRemove[] = @$oElement; + } } } } @@ -913,6 +920,7 @@ class HtmlUtils $aNodes = $oDom->getElementsByTagName('*'); foreach ($aNodes as /* @var $oElement \DOMElement */ $oElement) { + $aRemovedAttrs = array(); $sTagNameLower = \strtolower($oElement->tagName); // convert body attributes to styles @@ -929,14 +937,14 @@ class HtmlUtils if (isset($oElement->attributes)) { - foreach ($oElement->attributes as $sAttributeName => /* @var $oAttributeNode \DOMNode */ $oAttributeNode) + foreach ($oElement->attributes as $sAttrName => /* @var $oAttributeNode \DOMNode */ $oAttributeNode) { if ($oAttributeNode && isset($oAttributeNode->nodeValue)) { - $sAttributeNameLower = \strtolower($sAttributeName); - if (isset($aAttrs[$sAttributeNameLower]) && '' === $aAttrs[$sAttributeNameLower]) + $sAttrNameLower = \trim(\strtolower($sAttrName)); + if (isset($aAttrs[$sAttrNameLower]) && '' === $aAttrs[$sAttrNameLower]) { - $aAttrs[$sAttributeNameLower] = array($sAttributeName, \trim($oAttributeNode->nodeValue)); + $aAttrs[$sAttrNameLower] = array($sAttrName, \trim($oAttributeNode->nodeValue)); } } } @@ -998,33 +1006,43 @@ class HtmlUtils 'color: '.$sLinkColor.\trim((empty($sStyles) ? '' : '; '.$sStyles))); } - if (\in_array($sTagNameLower, array('a', 'form', 'area'))) - { - $oElement->setAttribute('target', '_blank'); - } - - if (\in_array($sTagNameLower, array('a', 'form', 'area', 'input', 'button', 'textarea'))) - { - $oElement->setAttribute('tabindex', '-1'); - } - if ($oElement->hasAttributes() && isset($oElement->attributes) && $oElement->attributes) { - foreach ($oElement->attributes as $oAttr) + $aHtmlAllowedAttributes = isset(\MailSo\Config::$HtmlStrictAllowedAttributes) && + \is_array(\MailSo\Config::$HtmlStrictAllowedAttributes) && 0 < \count(\MailSo\Config::$HtmlStrictAllowedAttributes) ? + \MailSo\Config::$HtmlStrictAllowedAttributes : null; + + $sAttrsForRemove = array(); + foreach ($oElement->attributes as $sAttrName => $oAttr) { - if ($oAttr && !empty($oAttr->nodeName)) + if ($sAttrName && $oAttr) { - $sAttrName = \trim(\strtolower($oAttr->nodeName)); - if ('on' === \substr($sAttrName, 0, 2) || in_array($sAttrName, array( - 'id', 'class', 'contenteditable', 'designmode', 'formaction', 'manifest', + $sAttrNameLower = \trim(\strtolower($sAttrName)); + if ($aHtmlAllowedAttributes && !\in_array($sAttrNameLower, $aHtmlAllowedAttributes)) + { + $sAttrsForRemove[] = $sAttrName; + } + else if ('on' === \substr($sAttrNameLower, 0, 2) || in_array($sAttrNameLower, array( + 'id', 'class', 'contenteditable', 'designmode', 'formaction', 'manifest', 'action', 'data-bind', 'data-reactid', 'xmlns', 'srcset', 'data-x-skip-style', 'fscommand', 'seeksegmenttime' ))) { - @$oElement->removeAttribute($oAttr->nodeName); + $sAttrsForRemove[] = $sAttrName; } } } + + if (0 < \count($sAttrsForRemove)) + { + foreach ($sAttrsForRemove as $sName) + { + @$oElement->removeAttribute($sName); + $aRemovedAttrs[\trim(\strtolower($sName))] = true; + } + } + + unset($sAttrsForRemove); } if ($oElement->hasAttribute('href')) @@ -1038,10 +1056,20 @@ class HtmlUtils if ('a' === $sTagNameLower) { - $oElement->setAttribute('rel', 'external nofollow'); + $oElement->setAttribute('rel', 'external nofollow noopener noreferrer'); } } + if (\in_array($sTagNameLower, array('a', 'form', 'area'))) + { + $oElement->setAttribute('target', '_blank'); + } + + if (\in_array($sTagNameLower, array('a', 'form', 'area', 'input', 'button', 'textarea'))) + { + $oElement->setAttribute('tabindex', '-1'); + } + if ($bTryToDetectHiddenImages && 'img' === $sTagNameLower) { $sAlt = $oElement->hasAttribute('alt') @@ -1177,6 +1205,17 @@ class HtmlUtils } $oElement->removeAttribute('data-x-skip-style'); + + if (\MailSo\Config::$HtmlStrictDebug && 0 < \count($aRemovedAttrs)) + { + unset($aRemovedAttrs['class'], $aRemovedAttrs['target'], $aRemovedAttrs['id'], $aRemovedAttrs['name']); + + $aRemovedAttrs = \array_keys($aRemovedAttrs); + if (0 < \count($aRemovedAttrs)) + { + $oElement->setAttribute('data-removed-attrs', \implode(',', $aRemovedAttrs)); + } + } } $sResult = \MailSo\Base\HtmlUtils::GetTextFromDom($oDom, $bWrapByFakeHtmlAndBodyDiv); @@ -1251,16 +1290,6 @@ class HtmlUtils $oElement->removeAttribute('data-x-href'); } - if ($oElement->hasAttribute('data-x-additional-src')) - { - $oElement->removeAttribute('data-x-additional-src'); - } - - if ($oElement->hasAttribute('data-x-additional-style-url')) - { - $oElement->removeAttribute('data-x-additional-style-url'); - } - if ($oElement->hasAttribute('data-x-style-cid-name') && $oElement->hasAttribute('data-x-style-cid')) { $sCidName = $oElement->getAttribute('data-x-style-cid-name'); @@ -1290,14 +1319,15 @@ class HtmlUtils } } - if ($oElement->hasAttribute('data-original')) + foreach (array( + 'data-x-additional-src', 'data-x-additional-style-url', 'data-removed-attrs', + 'data-original', 'data-x-div-type', 'data-wrp', 'data-bind' + ) as $sName) { - $oElement->removeAttribute('data-original'); - } - - if ($oElement->hasAttribute('data-x-div-type')) - { - $oElement->removeAttribute('data-x-div-type'); + if ($oElement->hasAttribute($sName)) + { + $oElement->removeAttribute($sName); + } } if ($oElement->hasAttribute('data-x-style-url')) diff --git a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php index f5b84ab45..3ffb0f5db 100644 --- a/rainloop/v/0.0.0/app/libraries/MailSo/Config.php +++ b/rainloop/v/0.0.0/app/libraries/MailSo/Config.php @@ -26,6 +26,21 @@ class Config */ public static $MBSTRING = true; + /** + * @var array|null + */ + public static $HtmlStrictAllowedTags = null; + + /** + * @var array|null + */ + public static $HtmlStrictAllowedAttributes = null; + + /** + * @var boolean + */ + public static $HtmlStrictDebug = false; + /** * @var bool */ diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php index ff44a088f..c9e4d3fb3 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Actions.php @@ -9968,24 +9968,6 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack $mResult['InReplyTo'] = $mResponse->InReplyTo(); $mResult['References'] = $mResponse->References(); - $fAdditionalDomReader = null; - if (0 < \strlen($sHtml) && $this->Config()->Get('labs', 'emogrifier', false)) - { - if (!\class_exists('RainLoopVendor\Pelago\Emogrifier', false)) - { - include_once APP_VERSION_ROOT_PATH.'app/libraries/emogrifier/Emogrifier.php'; - } - - if (\class_exists('RainLoopVendor\Pelago\Emogrifier', false)) - { - $fAdditionalDomReader = function ($oDom) { - $oEmogrifier = new \RainLoopVendor\Pelago\Emogrifier(); - $oEmogrifier->preserveEncoding = false; - return $oEmogrifier->emogrify($oDom); - }; - } - } - $fAdditionalExternalFilter = null; if (!!$this->Config()->Get('labs', 'use_local_proxy_for_external_images', false)) { @@ -10004,8 +9986,7 @@ NewThemeLink IncludeCss LoadingDescriptionEsc TemplatesLink LangLink IncludeBack $mResult['Html'] = 0 === \strlen($sHtml) ? '' : \MailSo\Base\HtmlUtils::ClearHtml( $sHtml, $bHasExternals, $mFoundedCIDs, $aContentLocationUrls, $mFoundedContentLocationUrls, false, false, - $fAdditionalExternalFilter, $fAdditionalDomReader, - !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) + $fAdditionalExternalFilter, null, !!$this->Config()->Get('labs', 'try_to_detect_hidden_images', false) ); $mResult['ExternalProxy'] = null !== $fAdditionalExternalFilter; diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php index 04af66ea2..2bf4f75e0 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Api.php @@ -134,6 +134,52 @@ class Api } }); } + + \MailSo\Config::$HtmlStrictDebug = !!\RainLoop\Api::Config()->Get('debug', 'enable', false); + + if (\RainLoop\Api::Config()->Get('labs', 'strict_html_parser', true)) + { + \MailSo\Config::$HtmlStrictAllowedAttributes = array( + // rainloop + 'data-wrp', + // defaults + 'name', + 'dir', 'lang', 'style', 'title', + 'background', 'bgcolor', 'alt', 'height', 'width', 'src', 'href', + 'border', 'bordercolor', 'charset', 'direction', 'language', + // a + 'coords', 'download', 'hreflang', 'shape', + // body + 'alink', 'bgproperties', 'bottommargin', 'leftmargin', 'link', 'rightmargin', 'text', 'topmargin', 'vlink', + 'marginwidth', 'marginheight', 'offset', + // button, + 'disabled', 'type', 'value', + // col + 'align', 'valign', + // font + 'color', 'face', 'size', + // form + 'novalidate', + // hr + 'noshade', + // img + 'hspace', 'sizes', 'srcset', 'vspace', 'usemap', + // input, textarea + 'checked', 'max', 'min', 'maxlength', 'multiple', 'pattern', 'placeholder', 'readonly', 'required', 'step', 'wrap', + // label + 'for', + // meter + 'low', 'high', 'optimum', + // ol + 'reversed', 'start', + // option + 'selected', 'label', + // table + 'cols', 'rows', 'frame', 'rules', 'summary', 'cellpadding', 'cellspacing', + // td + 'abbr', 'axis', 'colspan', 'rowspan', 'headers', 'nowrap' + ); + } } } diff --git a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php index 7e0bac54c..15b589e1d 100644 --- a/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php +++ b/rainloop/v/0.0.0/app/libraries/RainLoop/Config/Application.php @@ -446,8 +446,8 @@ Enables caching in the system'), 'cookie_default_secure' => array(false), 'replace_env_in_configuration' => array(''), 'startup_url' => array(''), - 'emogrifier' => array(true), 'nice_social_redirect' => array(true), + 'strict_html_parser' => array(false), 'dev_email' => array(''), 'dev_password' => array('') ), diff --git a/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT b/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT deleted file mode 100644 index 55c062e72..000000000 --- a/rainloop/v/0.0.0/app/libraries/emogrifier/CHANGELOG.TXT +++ /dev/null @@ -1,73 +0,0 @@ -2012-05-01 -Made removal of invisible nodes operate in a case-insensitive manner... Thanks Juha P.! - -2012-02-07 -Fixed some recent code introductions to use class constants rather than global constants. -Fixed some recent code introductions to make it cleaner to read. - -2012-01-31 -Fixed a bug that was introduced with the 2011-12-22 revision... Thanks Sagi L. and M. Bąkowski! -Added extraction of