diff options
Diffstat (limited to 'engine/lib/input.php')
| -rw-r--r-- | engine/lib/input.php | 455 |
1 files changed, 282 insertions, 173 deletions
diff --git a/engine/lib/input.php b/engine/lib/input.php index 26416d646..80b0b8766 100644 --- a/engine/lib/input.php +++ b/engine/lib/input.php @@ -3,51 +3,56 @@ * Parameter input functions. * This file contains functions for getting input from get/post variables. * - * @package Elgg - * @subpackage Core - * @author Curverider Ltd <info@elgg.com> - * @link http://elgg.org/ + * @package Elgg.Core + * @subpackage Input */ /** - * Get some input from variables passed on the GET or POST line. + * Get some input from variables passed submitted through GET or POST. + * + * If using any data obtained from get_input() in a web page, please be aware that + * it is a possible vector for a reflected XSS attack. If you are expecting an + * integer, cast it to an int. If it is a string, escape quotes. * * Note: this function does not handle nested arrays (ex: form input of param[m][n]) * because of the filtering done in htmlawed from the filter_tags call. + * @todo Is this ^ still true? + * + * @param string $variable The variable name we want. + * @param mixed $default A default value for the variable if it is not found. + * @param bool $filter_result If true, then the result is filtered for bad tags. * - * @param $variable string The variable we want to return. - * @param $default mixed A default value for the variable if it is not found. - * @param $filter_result If true then the result is filtered for bad tags. + * @return mixed */ -function get_input($variable, $default = "", $filter_result = true) { +function get_input($variable, $default = NULL, $filter_result = TRUE) { global $CONFIG; + $result = $default; + + elgg_push_context('input'); + if (isset($CONFIG->input[$variable])) { - $var = $CONFIG->input[$variable]; + $result = $CONFIG->input[$variable]; if ($filter_result) { - $var = filter_tags($var); + $result = filter_tags($result); } - - return $var; - } - - if (isset($_REQUEST[$variable])) { + } elseif (isset($_REQUEST[$variable])) { if (is_array($_REQUEST[$variable])) { - $var = $_REQUEST[$variable]; + $result = $_REQUEST[$variable]; } else { - $var = trim($_REQUEST[$variable]); + $result = trim($_REQUEST[$variable]); } if ($filter_result) { - $var = filter_tags($var); + $result = filter_tags($result); } - - return $var; } - return $default; + elgg_pop_context(); + + return $result; } /** @@ -55,8 +60,10 @@ function get_input($variable, $default = "", $filter_result = true) { * * Note: this function does not handle nested arrays (ex: form input of param[m][n]) * - * @param string $variable The name of the variable - * @param string $value The value of the variable + * @param string $variable The name of the variable + * @param string|string[] $value The value of the variable + * + * @return void */ function set_input($variable, $value) { global $CONFIG; @@ -65,10 +72,7 @@ function set_input($variable, $value) { } if (is_array($value)) { - foreach ($value as $key => $val) { - $value[$key] = trim($val); - } - + array_walk_recursive($value, create_function('&$v, $k', '$v = trim($v);')); $CONFIG->input[trim($variable)] = $value; } else { $CONFIG->input[trim($variable)] = trim($value); @@ -79,142 +83,171 @@ function set_input($variable, $value) { * Filter tags from a given string based on registered hooks. * * @param mixed $var Anything that does not include an object (strings, ints, arrays) - * This includes multi-dimensional arrays. + * This includes multi-dimensional arrays. + * * @return mixed The filtered result - everything will be strings */ function filter_tags($var) { - return trigger_plugin_hook('validate', 'input', null, $var); + return elgg_trigger_plugin_hook('validate', 'input', null, $var); +} + +/** + * Validates an email address. + * + * @param string $address Email address. + * + * @return bool + */ +function is_email_address($address) { + return filter_var($address, FILTER_VALIDATE_EMAIL) === $address; } /** - * Sanitise file paths for input, ensuring that they begin and end with slashes etc. + * Load all the REQUEST variables into the sticky form cache + * + * Call this from an action when you want all your submitted variables + * available if the submission fails validation and is sent back to the form * - * @param string $path The path - * @return string + * @param string $form_name Name of the sticky form + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 */ -function sanitise_filepath($path) { - // Convert to correct UNIX paths - $path = str_replace('\\', '/', $path); +function elgg_make_sticky_form($form_name) { - // Sort trailing slash - $path = trim($path); - // rtrim defaults plus / - $path = rtrim($path, " \n\t\0\x0B/"); - $path = $path . "/"; + elgg_clear_sticky_form($form_name); - return $path; -} + if (!isset($_SESSION['sticky_forms'])) { + $_SESSION['sticky_forms'] = array(); + } + $_SESSION['sticky_forms'][$form_name] = array(); + foreach ($_REQUEST as $key => $var) { + // will go through XSS filtering on the get function + $_SESSION['sticky_forms'][$form_name][$key] = $var; + } +} /** - * Takes a string and turns any URLs into formatted links - * - * @param string $text The input string - * @return string The output stirng with formatted links - **/ -function parse_urls($text) { - // @todo this causes problems with <attr = "val"> - // must be ing <attr="val"> format (no space). - // By default htmlawed rewrites tags to this format. - // if PHP supported conditional negative lookbehinds we could use this: - // $r = preg_replace_callback('/(?<!=)(?<![ ])?(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i', - // - // we can put , in the list of excluded char but need to keep . because of domain names. - // it is removed in the callback. - $r = preg_replace_callback('/(?<!=)(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i', - create_function( - '$matches', - ' - $url = $matches[1]; - $period = \'\'; - if (substr($url, -1, 1) == \'.\') { - $period = \'.\'; - $url = trim($url, \'.\'); - } - $urltext = str_replace("/", "/<wbr />", $url); - return "<a href=\"$url\" style=\"text-decoration:underline;\">$urltext</a>$period"; - ' - ), $text); + * Clear the sticky form cache + * + * Call this if validation is successful in the action handler or + * when they sticky values have been used to repopulate the form + * after a validation error. + * + * @param string $form_name Form namespace + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_clear_sticky_form($form_name) { + unset($_SESSION['sticky_forms'][$form_name]); +} - return $r; +/** + * Has this form been made sticky? + * + * @param string $form_name Form namespace + * + * @return boolean + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_is_sticky_form($form_name) { + return isset($_SESSION['sticky_forms'][$form_name]); } /** + * Get a specific sticky variable * - * Adds P tags. - * Borrowed from Wordpress. - * - **/ -function autop($pee, $br = 1) { - $pee = $pee . "\n"; // just to make things a little easier, pad the end - $pee = preg_replace('|<br />\s*<br />|', "\n\n", $pee); - // Space things out a little - $allblocks = '(?:table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|map|area|blockquote|address|math|style|input|p|h[1-6]|hr)'; - $pee = preg_replace('!(<' . $allblocks . '[^>]*>)!', "\n$1", $pee); - $pee = preg_replace('!(</' . $allblocks . '>)!', "$1\n\n", $pee); - $pee = str_replace(array("\r\n", "\r"), "\n", $pee); // cross-platform newlines - if ( strpos($pee, '<object') !== false ) { - $pee = preg_replace('|\s*<param([^>]*)>\s*|', "<param$1>", $pee); // no pee inside object/embed - $pee = preg_replace('|\s*</embed>\s*|', '</embed>', $pee); - } - $pee = preg_replace("/\n\n+/", "\n\n", $pee); // take care of duplicates - $pee = preg_replace('/\n?(.+?)(?:\n\s*\n|\z)/s', "<p>$1</p>\n", $pee); // make paragraphs, including one at the end - $pee = preg_replace('|<p>\s*?</p>|', '', $pee); // under certain strange conditions it could create a P of entirely whitespace - $pee = preg_replace('!<p>([^<]+)\s*?(</(?:div|address|form)[^>]*>)!', "<p>$1</p>$2", $pee); - $pee = preg_replace( '|<p>|', "$1<p>", $pee ); - $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); // don't pee all over a tag - $pee = preg_replace("|<p>(<li.+?)</p>|", "$1", $pee); // problem with nested lists - $pee = preg_replace('|<p><blockquote([^>]*)>|i', "<blockquote$1><p>", $pee); - $pee = str_replace('</blockquote></p>', '</p></blockquote>', $pee); - $pee = preg_replace('!<p>\s*(</?' . $allblocks . '[^>]*>)!', "$1", $pee); - $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*</p>!', "$1", $pee); - if ($br) { - $pee = preg_replace_callback('/<(script|style).*?<\/\\1>/s', create_function('$matches', 'return str_replace("\n", "<WPPreserveNewline />", $matches[0]);'), $pee); - $pee = preg_replace('|(?<!<br />)\s*\n|', "<br />\n", $pee); // optionally make line breaks - $pee = str_replace('<WPPreserveNewline />', "\n", $pee); + * @param string $form_name The name of the form + * @param string $variable The name of the variable + * @param mixed $default Default value if the variable does not exist in sticky cache + * @param boolean $filter_result Filter for bad input if true + * + * @return mixed + * + * @todo should this filter the default value? + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_get_sticky_value($form_name, $variable = '', $default = NULL, $filter_result = true) { + if (isset($_SESSION['sticky_forms'][$form_name][$variable])) { + $value = $_SESSION['sticky_forms'][$form_name][$variable]; + if ($filter_result) { + // XSS filter result + $value = filter_tags($value); + } + return $value; } - $pee = preg_replace('!(</?' . $allblocks . '[^>]*>)\s*<br />!', "$1", $pee); - $pee = preg_replace('!<br />(\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)!', '$1', $pee); -// if (strpos($pee, '<pre') !== false) { -// mind the space between the ? and >. Only there because of the comment. -// $pee = preg_replace_callback('!(<pre.*? >)(.*?)</pre>!is', 'clean_pre', $pee ); -// } - $pee = preg_replace( "|\n</p>$|", '</p>', $pee ); - - return $pee; + return $default; } /** - * Examins $_SERVER['REQUEST_URI'] and set_input()s on each. - * Required if the params are sent as GET and not forwarded by mod_rewrite. + * Get all the values in a sticky form in an array + * + * @param string $form_name The name of the form + * @param bool $filter_result Filter for bad input if true * - * @return bool on success + * @return array + * @since 1.8.0 */ -function elgg_set_input_from_uri() { - $query = parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY); - $query_arr = elgg_parse_str($query); +function elgg_get_sticky_values($form_name, $filter_result = true) { + if (!isset($_SESSION['sticky_forms'][$form_name])) { + return array(); + } - if (is_array($query_arr)) { - foreach($query_arr as $name => $val) { - set_input($name, $val); + $values = $_SESSION['sticky_forms'][$form_name]; + if ($filter_result) { + foreach ($values as $key => $value) { + // XSS filter result + $values[$key] = filter_tags($value); } } + return $values; +} + +/** + * Clear a specific sticky variable + * + * @param string $form_name The name of the form + * @param string $variable The name of the variable to clear + * + * @return void + * @link http://docs.elgg.org/Tutorials/UI/StickyForms + * @since 1.8.0 + */ +function elgg_clear_sticky_value($form_name, $variable) { + unset($_SESSION['sticky_forms'][$form_name][$variable]); } /** * Page handler for autocomplete endpoint. * - * @param $page - * @return unknown_type + * @todo split this into functions/objects, this is way too big + * + * /livesearch?q=<query> + * + * Other options include: + * match_on string all or array(groups|users|friends) + * match_owner int 0/1 + * limit int default is 10 + * + * @param array $page + * @return string JSON string is returned and then exit + * @access private */ function input_livesearch_page_handler($page) { global $CONFIG; + // only return results to logged in users. - if (!$user = get_loggedin_user()) { + if (!$user = elgg_get_logged_in_user_entity()) { exit; } - if (!$q = get_input('q')) { + if (!$q = get_input('term', get_input('q'))) { exit; } @@ -224,134 +257,203 @@ function input_livesearch_page_handler($page) { $q = str_replace(array('_', '%'), array('\_', '\%'), $q); $match_on = get_input('match_on', 'all'); - if ($match_on == 'all' || $match_on[0] == 'all') { - $match_on = array('users', 'groups'); - } if (!is_array($match_on)) { $match_on = array($match_on); } + // all = users and groups + if (in_array('all', $match_on)) { + $match_on = array('users', 'groups'); + } + if (get_input('match_owner', false)) { - $owner_guid = $user->getGUID(); $owner_where = 'AND e.owner_guid = ' . $user->getGUID(); } else { - $owner_guid = null; $owner_where = ''; } - $limit = get_input('limit', 10); + $limit = sanitise_int(get_input('limit', 10)); // grab a list of entities and send them in json. $results = array(); - foreach ($match_on as $type) { - switch ($type) { - case 'all': - // only need to pull up title from objects. - - if (!$entities = elgg_get_entities(array('owner_guid' => $owner_guid, 'limit' => $limit)) AND is_array($entities)) { - $results = array_merge($results, $entities); - } - break; - + foreach ($match_on as $match_type) { + switch ($match_type) { case 'users': $query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entities as e WHERE e.guid = ue.guid AND e.enabled = 'yes' AND ue.banned = 'no' - AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%') + AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ + if (!$entity) { + continue; + } + + if (in_array('groups', $match_on)) { + $value = $entity->guid; + } else { + $value = $entity->username; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - $results[$entity->name . rand(1,100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $value, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + $results[$entity->name . rand(1, 100)] = $result; } } break; case 'groups': // don't return results if groups aren't enabled. - if (!is_plugin_enabled('groups')) { + if (!elgg_is_active_plugin('groups')) { continue; } $query = "SELECT * FROM {$CONFIG->dbprefix}groups_entity as ge, {$CONFIG->dbprefix}entities as e WHERE e.guid = ge.guid AND e.enabled = 'yes' $owner_where - AND (ge.name LIKE '$q%' OR ge.description LIKE '%$q%') + AND (ge.name LIKE '$q%' OR ge.name LIKE '% $q%' OR ge.description LIKE '% $q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggGroup $entity */ + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'group', 'name' => $entity->name, 'desc' => strip_tags($entity->description), - 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - //$results[$entity->name . rand(1,100)] = "$json|{$entity->guid}"; - $results[$entity->name . rand(1,100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->guid, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + + $results[$entity->name . rand(1, 100)] = $result; } } break; case 'friends': - $access = get_access_sql_suffix(); - $query = "SELECT * FROM {$CONFIG->dbprefix}users_entity as ue, {$CONFIG->dbprefix}entity_relationships as er, {$CONFIG->dbprefix}entities as e + $query = "SELECT * FROM + {$CONFIG->dbprefix}users_entity as ue, + {$CONFIG->dbprefix}entity_relationships as er, + {$CONFIG->dbprefix}entities as e WHERE er.relationship = 'friend' AND er.guid_one = {$user->getGUID()} AND er.guid_two = ue.guid AND e.guid = ue.guid AND e.enabled = 'yes' AND ue.banned = 'no' - AND (ue.name LIKE '$q%' OR ue.username LIKE '$q%') + AND (ue.name LIKE '$q%' OR ue.name LIKE '% $q%' OR ue.username LIKE '$q%') LIMIT $limit "; if ($entities = get_data($query)) { foreach ($entities as $entity) { - $json = json_encode(array( + // @todo use elgg_get_entities (don't query in a loop!) + $entity = get_entity($entity->guid); + /* @var ElggUser $entity */ + if (!$entity) { + continue; + } + + $output = elgg_view_list_item($entity, array( + 'use_hover' => false, + 'class' => 'elgg-autocomplete-item', + )); + + $icon = elgg_view_entity_icon($entity, 'tiny', array( + 'use_hover' => false, + )); + + $result = array( 'type' => 'user', 'name' => $entity->name, 'desc' => $entity->username, - 'icon' => '<img class="livesearch_icon" src="' . get_entity($entity->guid)->getIcon('tiny') . '" />', - 'guid' => $entity->guid - )); - $results[$entity->name . rand(1,100)] = $json; + 'guid' => $entity->guid, + 'label' => $output, + 'value' => $entity->username, + 'icon' => $icon, + 'url' => $entity->getURL(), + ); + $results[$entity->name . rand(1, 100)] = $result; } } break; default: - // arbitrary subtype. - //@todo you cannot specify a subtype without a type. - // did this ever work? - elgg_get_entities(array('subtype' => $type, 'owner_guid' => $owner_guid)); + header("HTTP/1.0 400 Bad Request", true); + echo "livesearch: unknown match_on of $match_type"; + exit; break; } } ksort($results); - echo implode($results, "\n"); + header("Content-Type: application/json"); + echo json_encode(array_values($results)); exit; } - +/** + * Register input functions and sanitize input + * + * @return void + * @access private + */ function input_init() { // register an endpoint for live search / autocomplete. - register_page_handler('livesearch', 'input_livesearch_page_handler'); + elgg_register_page_handler('livesearch', 'input_livesearch_page_handler'); + + if (ini_get_bool('magic_quotes_gpc')) { - if (ini_get_bool('magic_quotes_gpc') ) { - //do keys as well, cos array_map ignores them + /** + * do keys as well, cos array_map ignores them + * + * @param array $array Array of values + * + * @return array Sanitized array + */ function stripslashes_arraykeys($array) { if (is_array($array)) { $array2 = array(); @@ -368,6 +470,13 @@ function input_init() { } } + /** + * Strip slashes on everything + * + * @param mixed $value The value to remove slashes from + * + * @return mixed + */ function stripslashes_deep($value) { if (is_array($value)) { $value = stripslashes_arraykeys($value); @@ -408,4 +517,4 @@ function input_init() { } } -register_elgg_event_handler('init','system','input_init'); +elgg_register_event_handler('init', 'system', 'input_init'); |
