Index: core/units/images/image_tag_processor.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/images/image_tag_processor.php (revision 16023) +++ core/units/images/image_tag_processor.php (revision ) @@ -400,7 +400,7 @@ // TODO: change to urlToPath usage later // relative url (we add sort of does - return FULL_PATH . '/' . mb_substr(THEMES_PATH, 1) . '/' . rawurldecode($path); + return FULL_PATH . '/' . mb_substr(THEMES_PATH, 1) . '/' . kUtil::unescape($path, kUtil::ESCAPE_URL); } /** Index: core/kernel/processors/main_processor.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/processors/main_processor.php (revision 15905) +++ core/kernel/processors/main_processor.php (revision ) @@ -412,7 +412,7 @@ } if (array_key_exists('no_html_escape', $params) && $params['no_html_escape']) { - return htmlspecialchars_decode($ret); + return kUtil::unescape($ret, kUtil::ESCAPE_HTML); } return $ret; Index: core/kernel/db/cat_event_handler.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/db/cat_event_handler.php (revision 16016) +++ core/kernel/db/cat_event_handler.php (revision ) @@ -1103,7 +1103,7 @@ $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('HTTPQuery'); /* @var $query_object kHTTPQuery */ @@ -1664,14 +1664,14 @@ $condition = ''; switch ($record['FieldType']) { case 'select': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if ($keywords[$field]) { $condition = sprintf($condition_patterns['is'], $field_name, $this->Conn->qstr( $keywords[$field] )); } break; case 'multiselect': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if ($keywords[$field]) { $condition = Array (); $values = explode('|', substr($keywords[$field], 1, -1)); @@ -1683,7 +1683,7 @@ break; case 'text': - $keywords[$field] = htmlspecialchars_decode( $keywords[$field] ); + $keywords[$field] = $this->Application->unescapeRequestVariable($keywords[$field]); if (mb_strlen($keywords[$field]) >= $this->Application->ConfigValue('Search_MinKeyword_Length')) { $highlight_keywords[] = $keywords[$field]; Index: core/units/thesaurus/thesaurus_tp.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/thesaurus/thesaurus_tp.php (revision 15892) +++ core/units/thesaurus/thesaurus_tp.php (revision ) @@ -30,7 +30,7 @@ function _getThesaurusRecords() { - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $table_name = $this->Application->getUnitOption($this->Prefix, 'TableName'); $sql = 'SELECT * Index: core/kernel/globals.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/globals.php (revision 16000) +++ core/kernel/globals.php (revision ) @@ -901,6 +901,64 @@ throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } + /** + * Unescapes a string. + * + * @param string $text Text to unescape. + * @param string $strategy Escape strategy. + * + * @return string + * @throws InvalidArgumentException When unknown escape strategy is given. + */ + public static function unescape($text, $strategy = null) + { + if ( !isset($strategy) ) { + $strategy = self::$escapeStrategy; + } + + if ( strpos($strategy, '+') !== false ) { + $previous_strategy = ''; + $strategies = explode('+', $strategy); + + foreach ($strategies as $current_strategy) { + // apply default strategy + if ( $current_strategy == '' ) { + $current_strategy = self::$escapeStrategy; + } + + // don't double-unescape + if ( $current_strategy != $previous_strategy ) { + $text = self::unescape($text, $current_strategy); + $previous_strategy = $current_strategy; + } + } + + return $text; + } + + if ( $strategy == self::ESCAPE_HTML ) { + return htmlspecialchars_decode($text, ENT_QUOTES); + } + + if ( $strategy == self::ESCAPE_JS ) { + // TODO: consider using "stripcslashes", because "stripslashes" isn't really for JavaScript unescaping (according to docs) + $text = str_replace("", '', $text); + $text = str_replace(array('\r', '\n'), array("\r", "\n"), $text); + $text = stripslashes($text); + + return $text; + } + + if ( $strategy == self::ESCAPE_URL ) { + return rawurldecode($text); + } + + if ( $strategy == self::ESCAPE_RAW ) { + return $text; + } + + throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); + } } /** @@ -1046,4 +1104,4 @@ return $res; } -} \ No newline at end of file +} Index: core/kernel/utility/formatters/upload_formatter.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/utility/formatters/upload_formatter.php (revision 15892) +++ core/kernel/utility/formatters/upload_formatter.php (revision ) @@ -62,11 +62,7 @@ */ public function Parse($value, $field_name, &$object) { - if ( !$this->Application->isAdmin ) { - // this allows to revert kUtil::escape() call for each field submitted on front-end - $value = is_array($value) ? array_map('htmlspecialchars_decode', $value) : htmlspecialchars_decode($value); - } - + $value = $this->Application->HttpQuery->unescapeRequestVariable($value); $options = $object->GetFieldOptions($field_name); if ( getArrayValue($options, 'upload_dir') ) { @@ -628,4 +624,4 @@ return parent::GetFormatted($value, $field_name, $object, $format); } -} \ No newline at end of file +} Index: core/units/categories/categories_event_handler.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/categories/categories_event_handler.php (revision 15892) +++ core/units/categories/categories_event_handler.php (revision ) @@ -2431,7 +2431,7 @@ function OnUpdatePreviewBlock($event) { $event->status = kEvent::erSTOP; - $string = htmlspecialchars_decode($this->Application->GetVar('preview_content')); + $string = $this->Application->unescapeRequestVariable($this->Application->GetVar('preview_content')); $category_helper = $this->Application->recallObject('CategoryHelper'); /* @var $category_helper CategoryHelper */ @@ -2452,7 +2452,7 @@ $event->redirect = false; $search_table = TABLE_PREFIX.'ses_'.$this->Application->GetSID().'_'.TABLE_PREFIX.'Search'; - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $query_object = $this->Application->recallObject('HTTPQuery'); /* @var $query_object kHTTPQuery */ @@ -3137,4 +3137,4 @@ $object->SetDBField('ResourceId', 0); // this will reset it } - } \ No newline at end of file + } Index: core/kernel/application.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/application.php (revision 15892) +++ core/kernel/application.php (revision ) @@ -1381,6 +1381,20 @@ } /** + * Removes forceful escaping done to the variable upon Front-End submission. + * + * @param string|array $value Value. + * + * @return string|array + * @see kHttpQuery::StripSlashes + * @todo Temporary method for marking problematic places to take care of, when forceful escaping will be removed. + */ + public function unescapeRequestVariable($value) + { + return $this->HttpQuery->unescapeRequestVariable($value); + } + + /** * Returns variable passed to the script as $type * * @param string $name Name of variable to retrieve @@ -3054,4 +3068,4 @@ { return $this->HttpQuery->getClientIp(); } -} \ No newline at end of file +} Index: core/units/user_profile/user_profile_eh.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/user_profile/user_profile_eh.php (revision 15892) +++ core/units/user_profile/user_profile_eh.php (revision ) @@ -67,7 +67,7 @@ } } else { - $this->Application->StorePersistentVar($variable_name, htmlspecialchars_decode($variable_value)); + $this->Application->StorePersistentVar($variable_name, $this->Application->unescapeRequestVariable($variable_value)); } } @@ -90,4 +90,4 @@ } } } - } \ No newline at end of file + } Index: core/kernel/utility/formatters/formatter.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/utility/formatters/formatter.php (revision 15892) +++ core/kernel/utility/formatters/formatter.php (revision ) @@ -73,9 +73,8 @@ $field_type = 'float'; } elseif ( $field_type == 'string' ) { - if ( !$this->Application->isAdmin && isset($options['allow_html']) && $options['allow_html'] ) { - // this allows to revert kUtil::escape() call for each field submitted on Front-End - $value = htmlspecialchars_decode($value); + if ( isset($options['allow_html']) && $options['allow_html'] ) { + $value = $this->Application->unescapeRequestVariable($value); } return $value; @@ -300,4 +299,4 @@ return isset($options['sample_value']) ? $options['sample_value'] : ''; } -} \ No newline at end of file +} Index: core/units/categories/categories_tag_processor.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/categories/categories_tag_processor.php (revision 15892) +++ core/units/categories/categories_tag_processor.php (revision ) @@ -958,7 +958,7 @@ */ protected function SpellingSuggestions($params) { - $keywords = htmlspecialchars_decode( trim($this->Application->GetVar('keywords')) ); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); if ( !$keywords ) { return ''; } @@ -2256,4 +2256,4 @@ return $this->Application->ParseBlock($block_params); } -} \ No newline at end of file +} Index: core/units/helpers/file_helper.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/helpers/file_helper.php (revision 15892) +++ core/units/helpers/file_helper.php (revision ) @@ -414,7 +414,7 @@ $full_path = preg_replace('/(\\\[\d]+)/', '\\\\\1', FULL_PATH); $path = preg_replace('/^' . preg_quote($base_url, '/') . '(.*)/', $full_path . '\\1', $url, 1); - return str_replace('/', DIRECTORY_SEPARATOR, rawurldecode($path)); + return str_replace('/', DIRECTORY_SEPARATOR, kUtil::unescape($path, kUtil::ESCAPE_URL)); } /** @@ -460,4 +460,4 @@ return $new_name; } - } \ No newline at end of file + } Index: core/units/forms/form_submissions/form_submissions_eh.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/forms/form_submissions/form_submissions_eh.php (revision 15892) +++ core/units/forms/form_submissions/form_submissions_eh.php (revision ) @@ -262,7 +262,7 @@ $theme = $this->Application->recallObject('theme.current'); /* @var $theme kDBItem */ - $template = htmlspecialchars_decode($this->Application->GetVar('success_template')); // kHTTPQuery do kUtil::escape() on everything on Front-End + $template = $this->Application->unescapeRequestVariable($this->Application->GetVar('success_template')); $alias_template = $theme->GetField('TemplateAliases', $template); $event->redirect = $alias_template ? $alias_template : $template; @@ -549,4 +549,4 @@ } } } - } \ No newline at end of file + } Index: core/kernel/db/db_event_handler.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/db/db_event_handler.php (revision 16016) +++ core/kernel/db/db_event_handler.php (revision ) @@ -3145,9 +3145,7 @@ return; } - if ( !$this->Application->isAdmin ) { - $value = array_map('htmlspecialchars_decode', $value); - } + $value = $this->Application->HttpQuery->unescapeRequestVariable($value); $tmp_path = WRITEABLE . '/tmp/'; $filename = $value['name'] . '.tmp'; @@ -3375,10 +3373,7 @@ protected function _getSafeFilename() { $filename = $this->Application->GetVar('file'); - - if ( !$this->Application->isAdmin ) { - $filename = htmlspecialchars_decode($filename); - } + $filename = $this->Application->unescapeRequestVariable($filename); if ( (strpos($filename, '../') !== false) || (trim($filename) !== $filename) ) { // when relative paths or special chars are found template names from url, then it's hacking attempt Index: core/kernel/utility/http_query.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/kernel/utility/http_query.php (revision 15892) +++ core/kernel/utility/http_query.php (revision ) @@ -645,6 +645,32 @@ } /** + * Removes forceful escaping done to the variable upon Front-End submission. + * + * @param string|array $value Value. + * + * @return string|array + * @see StripSlashes + */ + public function unescapeRequestVariable($value) + { + if ( $this->Application->isAdmin ) { + return $value; + } + + // This allows to revert kUtil::escape() call for each field submitted on front-end. + if ( is_array($value) ) { + foreach ( $value as $param_name => $param_value ) { + $value[$param_name] = $this->unescapeRequestVariable($param_value); + } + + return $value; + } + + return kUtil::unescape($value, kUtil::ESCAPE_HTML); + } + + /** * Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls * * @param bool $access_error Method is called during no_permission, require login, session expiration link preparation @@ -800,4 +826,4 @@ return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } -} \ No newline at end of file +} Index: core/units/thesaurus/thesaurus_eh.php IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- core/units/thesaurus/thesaurus_eh.php (revision 15892) +++ core/units/thesaurus/thesaurus_eh.php (revision ) @@ -32,8 +32,8 @@ /* @var $object kDBList */ if ( !$this->Application->isAdminUser ) { - $keywords = htmlspecialchars_decode(trim($this->Application->GetVar('keywords'))); + $keywords = $this->Application->unescapeRequestVariable(trim($this->Application->GetVar('keywords'))); $object->addFilter('search_filter', '%1$s.SearchTerm LIKE ' . $this->Conn->qstr($keywords) . ' OR %1$s.SearchTerm LIKE ' . $this->Conn->qstr($keywords . '_')); } } - } \ No newline at end of file + }