1<?php 2/** 3 * Parameter input functions. 4 * This file contains functions for getting input from get/post variables. 5 */ 6 7/** 8 * Get some input from variables passed submitted through GET or POST. 9 * 10 * If using any data obtained from get_input() in a web page, please be aware that 11 * it is a possible vector for a reflected XSS attack. If you are expecting an 12 * integer, cast it to an int. If it is a string, escape quotes. 13 * 14 * @param string $variable The variable name we want. 15 * @param mixed $default A default value for the variable if it is not found. 16 * @param bool $filter_result If true, then the result is filtered for bad tags. 17 * 18 * @return mixed 19 */ 20function get_input($variable, $default = null, $filter_result = true) { 21 return _elgg_services()->request->getParam($variable, $default, $filter_result); 22} 23 24/** 25 * Sets an input value that may later be retrieved by get_input 26 * 27 * Note: this function does not handle nested arrays (ex: form input of param[m][n]) 28 * 29 * @param string $variable The name of the variable 30 * @param string|string[] $value The value of the variable 31 * 32 * @return void 33 */ 34function set_input($variable, $value) { 35 _elgg_services()->request->setParam($variable, $value, true); 36} 37 38/** 39 * Returns all values parsed from the current request, including $_GET and $_POST values, 40 * as well as any values set with set_input() 41 * 42 * @see get_input() 43 * @see set_input() 44 * 45 * @param bool $filter_result Sanitize input values 46 * 47 * @return array 48 * 49 * @since 3.0 50 */ 51function elgg_get_request_data($filter_result = true) { 52 return _elgg_services()->request->getParams($filter_result); 53} 54 55/** 56 * Get an HTML-escaped title from input. E.g. "How to use <b> tags" 57 * 58 * @param string $variable The desired variable name 59 * @param string $default The default if none given 60 * 61 * @return string 62 * @since 3.0 63 */ 64function elgg_get_title_input($variable = 'title', $default = '') { 65 $raw_input = get_input($variable, $default, false); 66 return htmlspecialchars($raw_input, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); 67} 68 69/** 70 * Filter tags from a given string based on registered hooks. 71 * 72 * @param mixed $var Anything that does not include an object (strings, ints, arrays) 73 * This includes multi-dimensional arrays. 74 * 75 * @return mixed The filtered result - everything will be strings 76 */ 77function filter_tags($var) { 78 return elgg_trigger_plugin_hook('validate', 'input', null, $var); 79} 80 81/** 82 * Returns the current page's complete URL. 83 * 84 * It uses the configured site URL for the hostname rather than depending on 85 * what the server uses to populate $_SERVER. 86 * 87 * @return string The current page URL. 88 */ 89function current_page_url() { 90 return _elgg_services()->request->getCurrentURL(); 91} 92 93/** 94 * Validates an email address. 95 * 96 * @param string $address Email address. 97 * 98 * @return bool 99 */ 100function is_email_address($address) { 101 return elgg()->accounts->isValidEmail($address); 102} 103 104/** 105 * Save form submission data (all GET and POST vars) into a session cache 106 * 107 * Call this from an action when you want all your submitted variables 108 * available if the submission fails validation and is sent back to the form 109 * 110 * @param string $form_name Name of the sticky form 111 * 112 * @return void 113 * @since 1.8.0 114 */ 115function elgg_make_sticky_form($form_name) { 116 _elgg_services()->stickyForms->makeStickyForm($form_name); 117} 118 119/** 120 * Remove form submission data from the session 121 * 122 * Call this if validation is successful in the action handler or 123 * when they sticky values have been used to repopulate the form 124 * after a validation error. 125 * 126 * @param string $form_name Form namespace 127 * 128 * @return void 129 * @since 1.8.0 130 */ 131function elgg_clear_sticky_form($form_name) { 132 _elgg_services()->stickyForms->clearStickyForm($form_name); 133} 134 135/** 136 * Does form submission data exist for this form? 137 * 138 * @param string $form_name Form namespace 139 * 140 * @return boolean 141 * @since 1.8.0 142 */ 143function elgg_is_sticky_form($form_name) { 144 return _elgg_services()->stickyForms->isStickyForm($form_name); 145} 146 147/** 148 * Get a specific value from cached form submission data 149 * 150 * @param string $form_name The name of the form 151 * @param string $variable The name of the variable 152 * @param mixed $default Default value if the variable does not exist in sticky cache 153 * @param boolean $filter_result Filter for bad input if true 154 * 155 * @return mixed 156 * 157 * @todo should this filter the default value? 158 * @since 1.8.0 159 */ 160function elgg_get_sticky_value($form_name, $variable = '', $default = null, $filter_result = true) { 161 return _elgg_services()->stickyForms->getStickyValue($form_name, $variable, $default, $filter_result); 162} 163 164/** 165 * Get all submission data cached for a form 166 * 167 * @param string $form_name The name of the form 168 * @param bool $filter_result Filter for bad input if true 169 * 170 * @return array 171 * @since 1.8.0 172 */ 173function elgg_get_sticky_values($form_name, $filter_result = true) { 174 return _elgg_services()->stickyForms->getStickyValues($form_name, $filter_result); 175} 176 177/** 178 * Remove one value of form submission data from the session 179 * 180 * @param string $form_name The name of the form 181 * @param string $variable The name of the variable to clear 182 * 183 * @return void 184 * @since 1.8.0 185 */ 186function elgg_clear_sticky_value($form_name, $variable) { 187 _elgg_services()->stickyForms->clearStickyValue($form_name, $variable); 188} 189 190/** 191 * Check if a value isn't empty, but allow 0 and '0' 192 * 193 * @param mixed $value the value to check 194 * 195 * @see empty() 196 * @see Elgg\Values::isEmpty() 197 * 198 * @return bool 199 * @since 3.0.0 200 */ 201function elgg_is_empty($value) { 202 return Elgg\Values::isEmpty($value); 203} 204 205/** 206 * htmLawed filtering of data 207 * 208 * Called on the 'validate', 'input' plugin hook 209 * 210 * htmLawed's $config argument is filtered by the [config, htmlawed] hook. 211 * htmLawed's $spec argument is filtered by the [spec, htmlawed] hook. 212 * 213 * For information on these arguments, see 214 * http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/htmLawed_README.htm#s2.2 215 * 216 * @param \Elgg\Hook $hook 'validate', 'input' 217 * 218 * @return mixed 219 */ 220function _elgg_htmlawed_filter_tags(\Elgg\Hook $hook) { 221 $var = $hook->getValue(); 222 223 $config = [ 224 // seems to handle about everything we need. 225 'safe' => true, 226 227 // remove comments/CDATA instead of converting to text 228 'comment' => 1, 229 'cdata' => 1, 230 231 // do not check for unique ids as the full input stack could be checked multiple times 232 // @see https://github.com/Elgg/Elgg/issues/12934 233 'unique_ids' => 0, 234 235 'elements' => '*-applet-button-form-input-textarea-iframe-script-style-embed-object', 236 'deny_attribute' => 'class, on*, formaction', 237 'hook_tag' => '_elgg_htmlawed_tag_post_processor', 238 239 'schemes' => '*:http,https,ftp,news,mailto,rtsp,teamspeak,gopher,mms,callto', 240 // apparent this doesn't work. 241 // 'style:color,cursor,text-align,font-size,font-weight,font-style,border,margin,padding,float' 242 ]; 243 244 // add nofollow to all links on output 245 if (!elgg_in_context('input')) { 246 $config['anti_link_spam'] = ['/./', '']; 247 } 248 249 $config = elgg_trigger_plugin_hook('config', 'htmlawed', null, $config); 250 $spec = elgg_trigger_plugin_hook('spec', 'htmlawed', null, ''); 251 252 if (!is_array($var)) { 253 return Htmlawed::filter($var, $config, $spec); 254 } else { 255 $callback = function (&$v, $k, $config_spec) { 256 list ($config, $spec) = $config_spec; 257 $v = Htmlawed::filter($v, $config, $spec); 258 }; 259 260 array_walk_recursive($var, $callback, [$config, $spec]); 261 262 return $var; 263 } 264} 265 266/** 267 * Post processor for tags in htmlawed 268 * 269 * This runs after htmlawed has filtered. It runs for each tag and filters out 270 * style attributes we don't want. 271 * 272 * This function triggers the 'allowed_styles', 'htmlawed' plugin hook. 273 * 274 * @param string $element The tag element name 275 * @param array $attributes An array of attributes 276 * @return string 277 */ 278function _elgg_htmlawed_tag_post_processor($element, $attributes = false) { 279 if ($attributes === false) { 280 // This is a closing tag. Prevent further processing to avoid inserting a duplicate tag 281 return "</${element}>"; 282 } 283 284 // this list should be coordinated with the WYSIWYG editor used (tinymce, ckeditor, etc.) 285 $allowed_styles = [ 286 'color', 'cursor', 'text-align', 'vertical-align', 'font-size', 287 'font-weight', 'font-style', 'border', 'border-top', 'background-color', 288 'border-bottom', 'border-left', 'border-right', 289 'margin', 'margin-top', 'margin-bottom', 'margin-left', 290 'margin-right', 'padding', 'float', 'text-decoration' 291 ]; 292 293 $params = ['tag' => $element]; 294 $allowed_styles = elgg_trigger_plugin_hook('allowed_styles', 'htmlawed', $params, $allowed_styles); 295 296 // must return something. 297 $string = ''; 298 299 foreach ($attributes as $attr => $value) { 300 if ($attr == 'style') { 301 $styles = explode(';', $value); 302 303 $style_str = ''; 304 foreach ($styles as $style) { 305 if (!trim($style)) { 306 continue; 307 } 308 list($style_attr, $style_value) = explode(':', trim($style)); 309 $style_attr = trim($style_attr); 310 $style_value = trim($style_value); 311 312 if (in_array($style_attr, $allowed_styles)) { 313 $style_str .= "$style_attr: $style_value; "; 314 } 315 } 316 317 if ($style_str) { 318 $style_str = trim($style_str); 319 $string .= " style=\"$style_str\""; 320 } 321 } else { 322 $string .= " $attr=\"$value\""; 323 } 324 } 325 326 // Some WYSIWYG editors do not like tags like <p > so only add a space if needed. 327 if ($string = trim($string)) { 328 $string = " $string"; 329 } 330 331 $r = "<$element$string>"; 332 return $r; 333} 334 335/** 336 * Disable the autocomplete feature on password fields 337 * 338 * @param \Elgg\Hook $hook 'view_vars', 'input/password' 339 * 340 * @return void|array 341 */ 342function _elgg_disable_password_autocomplete(\Elgg\Hook $hook) { 343 344 if (!_elgg_config()->security_disable_password_autocomplete) { 345 return; 346 } 347 348 $return_value = $hook->getValue(); 349 350 $return_value['autocomplete'] = 'off'; 351 352 return $return_value; 353} 354 355/** 356 * Initialize the input library 357 * 358 * @return void 359 * @internal 360 */ 361function _elgg_input_init() { 362 363 elgg_register_plugin_hook_handler('validate', 'input', '_elgg_htmlawed_filter_tags', 1); 364 365 elgg_register_plugin_hook_handler('view_vars', 'input/password', '_elgg_disable_password_autocomplete'); 366 elgg_register_plugin_hook_handler('view_vars', 'input/password', [_elgg_services()->passwordGenerator, 'addInputRequirements']); 367} 368 369/** 370 * @see \Elgg\Application::loadCore Do not do work here. Just register for events. 371 */ 372return function(\Elgg\EventsService $events) { 373 $events->registerHandler('init', 'system', '_elgg_input_init'); 374}; 375