b2evolution PHP Cross Reference Blogging Systems

Source: /inc/_core/_param.funcs.php - 2437 lines - 70006 bytes - Summary - Text - Print

Description: This file implements parameter handling functions. This includes: - sanity checking of inputs - removing PHP's stupid "magic" quotes - validating specific inputs (urls, regexps...) - memorizing params - regenerating urls with the memorized params - manually reconstructing urls

   1  <?php
   2  /**
   3   * This file implements parameter handling functions.
   4   *
   5   * This includes:
   6   * - sanity checking of inputs
   7   * - removing PHP's stupid "magic" quotes
   8   * - validating specific inputs (urls, regexps...)
   9   * - memorizing params
  10   * - regenerating urls with the memorized params
  11   * - manually reconstructing urls
  12   *
  13   * This file is part of the evoCore framework - {@link http://evocore.net/}
  14   * See also {@link http://sourceforge.net/projects/evocms/}.
  15   *
  16   * @copyright (c)2003-2014 by Francois Planque - {@link http://fplanque.com/}
  17   * Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  18   * Parts of this file are copyright (c)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.
  19   *
  20   * {@internal License choice
  21   * - If you have received this file as part of a package, please find the license.txt file in
  22   *   the same folder or the closest folder above for complete license terms.
  23   * - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  24   *   then you must choose one of the following licenses before using the file:
  25   *   - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  26   *   - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  27   * }}
  28   *
  29   * {@internal Open Source relicensing agreement:
  30   * Daniel HAHLER grants Francois PLANQUE the right to license
  31   * Daniel HAHLER's contributions to this file and the b2evolution project
  32   * under any OSI approved OSS license (http://www.opensource.org/licenses/).
  33   * }}
  34   *
  35   * @package evocore
  36   *
  37   * @author cafelog (team)
  38   * @author blueyed: Daniel HAHLER.
  39   * @author fplanque: Francois PLANQUE.
  40   *
  41   * @version $Id: _param.funcs.php 6136 2014-03-08 07:59:48Z manuel $
  42   */
  43  if( !defined('EVO_MAIN_INIT') ) die( 'Please, do not access this page directly.' );
  44  
  45  
  46  // DEBUG: (Turn switch on or off to log debug info for specified category)
  47  $GLOBALS['debug_params'] = false;
  48  
  49  
  50  /**
  51   * Sets a parameter with values from the request or to provided default,
  52   * except if param is already set!
  53   *
  54   * Also removes magic quotes if they are set automatically by PHP.
  55   * Also forces type.
  56   * Priority order: POST, GET, COOKIE, DEFAULT.
  57   *
  58   * @todo when bad_request_die() gets called, the GLOBAL should not be left set to the invalid value!
  59   * fp> Why? if the process dies anyway
  60   *
  61   * @param string Variable to set
  62   * @param string Force value type to one of:
  63   * - integer
  64   * - float, double
  65   * - string (strips (HTML-)Tags, trims whitespace)
  66   * - text like string but allows multiple lines
  67   * - array/integer (elements of array must be integer)
  68   * - array/string (strips (HTML-)Tags, trims whitespace of array's elements)
  69   * - array/array/integer (two dimensional array and the elements must be integers)
  70   * - array/array/string (strips (HTML-)Tags, trims whitespace of the two dimensional array's elements)
  71   * - html (does nothing, for now)
  72   * - raw (does nothing)
  73   * - '' (does nothing) -- DEPRECATED, use "raw" instead
  74   * - '/^...$/' check regexp pattern match (string)
  75   * - boolean (will force type to boolean, but you can't use 'true' as a default since it has special meaning. There is no real reason to pass booleans on a URL though. Passing 0 and 1 as integers seems to be best practice).
  76   * - url (like string but dies on illegal urls)
  77   * Value type will be forced only if resulting value (probably from default then) is !== NULL
  78   * @param mixed Default value or TRUE if user input required
  79   * @param boolean Do we need to memorize this to regenerate the URL for this page?
  80   * @param boolean Override if variable already set
  81   * @param boolean Force setting of variable to default if no param is sent and var wasn't set before
  82   * @param mixed true will refuse illegal values,
  83   *              false will try to convert illegal to legal values,
  84   *              'allow_empty' will refuse illegal values but will always accept empty values (This helps blocking dirty spambots or borked index bots. Saves a lot of processor time by killing invalid requests)
  85   * @return mixed Final value of Variable, or false if we don't force setting and did not set
  86   */
  87  function param( $var, $type = 'raw', $default = '', $memorize = false,
  88                                  $override = false, $use_default = true, $strict_typing = 'allow_empty' )
  89  {
  90      global $Debuglog, $debug, $evo_charset, $io_charset;
  91      // NOTE: we use $GLOBALS[$var] instead of $$var, because otherwise it would conflict with param names which are used as function params ("var", "type", "default", ..)!
  92  
  93      /*
  94       * STEP 1 : Set the variable
  95       *
  96       * Check if already set
  97       * WARNING: when PHP register globals is ON, COOKIES get priority over GET and POST with this!!!
  98       *   dh> I never understood that comment.. does it refer to "variables_order" php.ini setting?
  99       *        fp> I guess
 100       */
 101      if( ! isset( $GLOBALS[$var] ) || $override )
 102      {
 103          if( isset($_POST[$var]) )
 104          {
 105              $GLOBALS[$var] = remove_magic_quotes( $_POST[$var] );
 106              // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by POST', 'params' );
 107          }
 108          elseif( isset($_GET[$var]) )
 109          {
 110              $GLOBALS[$var] = remove_magic_quotes($_GET[$var]);
 111              // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by GET', 'params' );
 112          }
 113          elseif( isset($_COOKIE[$var]))
 114          {
 115              $GLOBALS[$var] = remove_magic_quotes($_COOKIE[$var]);
 116              // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by COOKIE', 'params' );
 117          }
 118          elseif( $default === true )
 119          {
 120              bad_request_die( sprintf( T_('Parameter &laquo;%s&raquo; is required!'), $var ) );
 121          }
 122          elseif( $use_default )
 123          {    // We haven't set any value yet and we really want one: use default:
 124              if( in_array( $type, array( 'array', 'array/integer', 'array/string', 'array/array/integer', 'array/array/string' ) ) && $default === '' )
 125              { // Change default '' into array() (otherwise there would be a notice with settype() below)
 126                  $default = array();
 127              }
 128              $GLOBALS[$var] = $default;
 129              // echo '<br>param(-): '.$var.'='.$GLOBALS[$var].' set by default';
 130              // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by default', 'params' );
 131          }
 132          else
 133          { // param not found! don't set the variable.
 134              // Won't be memorized nor type-forced!
 135              return false;
 136          }
 137      }
 138      else
 139      { // Variable was already set but we need to remove the auto quotes
 140          $GLOBALS[$var] = remove_magic_quotes($GLOBALS[$var]);
 141  
 142          // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.' already set to ['.var_export($GLOBALS[$var], true).']!', 'params' );
 143      }
 144  
 145      if( isset($io_charset) && ! empty($evo_charset) )
 146      {
 147          $GLOBALS[$var] = convert_charset( $GLOBALS[$var], $evo_charset, $io_charset );
 148      }
 149  
 150      /*
 151       * STEP 2: make sure the data fits the expected type
 152       *
 153       * type will be forced even if it was set before and not overriden
 154       */
 155      if( !empty($type) && $GLOBALS[$var] !== NULL )
 156      { // Force the type
 157          // echo "forcing type!";
 158          switch( $type )
 159          {
 160              case 'html': // Technically does the same as "raw", but may do more in the future.
 161              case 'raw':
 162                  if( ! is_scalar($GLOBALS[$var]) )
 163                  { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 164                      debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 165                  }
 166  
 167                  // do nothing
 168                  if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as RAW Unsecure HTML', 'params' );
 169                  break;
 170  
 171              case 'htmlspecialchars':
 172                  if( ! is_scalar($GLOBALS[$var]) )
 173                  { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 174                      debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 175                  }
 176  
 177                  // convert all html to special characters:
 178                  $GLOBALS[$var] = trim( htmlspecialchars( $GLOBALS[$var] ) );
 179                  // cross-platform newlines:
 180                  $GLOBALS[$var] = preg_replace( "~(\r\n|\r)~", "\n", $GLOBALS[$var] );
 181                  $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as text with html special chars', 'params' );
 182                  break;
 183  
 184              case 'text':
 185                  if( ! is_scalar($GLOBALS[$var]) )
 186                  { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 187                      debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 188                  }
 189  
 190                  // strip out any html:
 191                  $GLOBALS[$var] = trim( strip_tags($GLOBALS[$var]) );
 192                  // cross-platform newlines:
 193                  $GLOBALS[$var] = preg_replace( "~(\r\n|\r)~", "\n", $GLOBALS[$var] );
 194                  $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as text', 'params' );
 195                  break;
 196  
 197              case 'string':
 198                  if( ! is_scalar($GLOBALS[$var]) )
 199                  { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 200                      debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 201                  }
 202  
 203                  // echo $var, '=', $GLOBALS[$var], '<br />';
 204                  // Make sure the string is a single line
 205                  $GLOBALS[$var] = preg_replace( '~\r|\n~', '', $GLOBALS[$var] );
 206                  // strip out any html:
 207                  $GLOBALS[$var] = trim( strip_tags($GLOBALS[$var]) );
 208  
 209                  $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as string', 'params' );
 210                  break;
 211  
 212              case 'url':
 213                  if( ! is_scalar($GLOBALS[$var]) )
 214                  { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 215                      debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 216                  }
 217  
 218                  // Make sure the string is a single line
 219                  $GLOBALS[$var] = preg_replace( '~\r|\n~', '', $GLOBALS[$var] );
 220                  // strip out any html:
 221                  $GLOBALS[$var] = trim( strip_tags( $GLOBALS[$var] ) );
 222                  // Decode url:
 223                  $GLOBALS[$var] = urldecode( $GLOBALS[$var] );
 224  
 225                  if( ( !empty( $GLOBALS[$var] ) ) && ( ( strpos( $GLOBALS[$var], '"' ) !== false ) || ( ! preg_match( '#^(/|\?|https?://)#i', $GLOBALS[$var] ) ) ) )
 226                  { // We cannot accept this MISMATCH:
 227                      bad_request_die( sprintf( T_('Illegal value received for parameter &laquo;%s&raquo;!'), $var ) );
 228                  }
 229  
 230                  $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as url', 'params' );
 231                  break;
 232  
 233              case 'array':
 234              case 'array/integer':
 235              case 'array/string':
 236              case 'array/array/integer':
 237              case 'array/array/string':
 238                  if( ! is_array( $GLOBALS[$var] ) )
 239                  { // This param must be array
 240                      debug_die( 'param(-): <strong>'.$var.'</strong> is not array!' );
 241                  }
 242  
 243                  // Store current array in temp var for checking and preparing
 244                  $globals_var = $GLOBALS[$var];
 245                  // Check if the given array type is one dimensional array
 246                  $one_dimensional = ( ( $type == 'array' ) || ( $type == 'array/integer' ) || ( $type == 'array/string' ) );
 247                  // Check if the given array type should contains string elements
 248                  $contains_strings = ( ( $type == 'array/string' ) || ( $type == 'array/array/string' ) );
 249                  if( $one_dimensional )
 250                  { // Convert to a two dimensional array to handle one and two dimensional arrays the same way
 251                      $globals_var = array( $globals_var );
 252                  }
 253  
 254                  foreach( $globals_var as $i => $var_array )
 255                  {
 256                      if( ! is_array( $var_array ) )
 257                      { // This param must be array
 258                          // Note: In case of one dimensional array params this will never happen
 259                          debug_die( 'param(-): <strong>'.$var.'['.$i.']</strong> is not array!' );
 260                      }
 261  
 262                      if( $type == 'array' )
 263                      { // This param may contain any kind of elements we need to check and validate it recursively
 264                          $globals_var[$i] = param_check_general_array( $var_array );
 265                          break;
 266                      }
 267  
 268                      foreach( $var_array as $j => $var_value )
 269                      {
 270                          if( ! is_scalar( $var_value ) )
 271                          { // This happens if someone uses "foo[][]=x" where "foo[]" is expected as string
 272                              debug_die( 'param(-): element of array <strong>'.$var.'</strong> is not scalar!' );
 273                          }
 274  
 275                          if( $contains_strings )
 276                          { // Prepare string elements of array
 277                              // Make sure the string is a single line
 278                              $var_value = preg_replace( '~\r|\n~', '', $var_value );
 279                              // strip out any html:
 280                              $globals_var[$i][$j] = trim( strip_tags( $var_value ) );
 281                          }
 282                      }
 283                  }
 284  
 285                  if( $one_dimensional )
 286                  { // Extract real array from temp array
 287                      $globals_var = $globals_var[0];
 288                  }
 289                  // Restore current array with prepared data
 290                  $GLOBALS[$var] = $globals_var;
 291  
 292                  $Debuglog->add( 'param(-): <strong>'.$var.'</strong> as '.$type, 'params' );
 293  
 294                  if( $contains_strings || $type == 'array' )
 295                  { // This is an array with string elements so it was processed. The array with integer elements must be checked with regexp, so we can't break in that case.
 296                      if( $GLOBALS[$var] === array() && ( $strict_typing === false ) && $use_default )
 297                      { // We want to consider empty values as invalid and fall back to the default value:
 298                          $GLOBALS[$var] = $default;
 299                      }
 300                      break;
 301                  }
 302  
 303              default:
 304                  if( substr( $type, 0, 1 ) == '/' )
 305                  {    // We want to match against a REGEXP:
 306                      if( ! is_scalar( $GLOBALS[$var] ) )
 307                      { // This happens if someone uses "foo[]=x" where "foo" is expected as string
 308                          debug_die( 'param(-): <strong>'.$var.'</strong> is not scalar!' );
 309                      }
 310                      elseif( preg_match( $type, $GLOBALS[$var] ) )
 311                      {    // Okay, match
 312                          if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.$var.'</strong> matched against '.$type, 'params' );
 313                      }
 314                      elseif( $strict_typing == 'allow_empty' && empty($GLOBALS[$var]) )
 315                      {    // No match but we accept empty value:
 316                          if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.$var.'</strong> is empty: ok', 'params' );
 317                      }
 318                      elseif( $strict_typing )
 319                      {    // We cannot accept this MISMATCH:
 320                          bad_request_die( sprintf( T_('Illegal value received for parameter &laquo;%s&raquo;!'), $var ) );
 321                      }
 322                      else
 323                      { // Fall back to default:
 324                          $GLOBALS[$var] = $default;
 325                          if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.$var.'</strong> DID NOT match '.$type.' set to default value='.$GLOBALS[$var], 'params' );
 326                      }
 327  
 328                      // From now on, consider this as a string: (we need this when memorizing)
 329                      $type = 'string';
 330                  }
 331                  elseif( $GLOBALS[$var] === '' )
 332                  { // Special handling of empty values.
 333                      if( $strict_typing === false && $use_default )
 334                      {    // ADDED BY FP 2006-07-06
 335                          // We want to consider empty values as invalid and fall back to the default value:
 336                          $GLOBALS[$var] = $default;
 337                      }
 338                      else
 339                      {    // We memorize the empty value as NULL:
 340                          // fplanque> note: there might be side effects to this, but we need
 341                          // this to distinguish between 0 and 'no input'
 342                          // Note: we do this after regexps because we may or may not want to allow empty strings in regexps
 343                          $GLOBALS[$var] = NULL;
 344                          if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.$var.'</strong> set to NULL', 'params' );
 345                      }
 346                  }
 347                  elseif( $GLOBALS[$var] === array() )
 348                  {
 349                      if( $strict_typing === false && $use_default )
 350                      {    // ADDED BY FP 2006-09-07
 351                          // We want to consider empty values as invalid and fall back to the default value:
 352                          $GLOBALS[$var] = $default;
 353                      }
 354                  }
 355                  // TODO: dh> if a var (e.g. from POST) comes in as '' but has type "array" it does not get "converted" to array type (nor gets the default used!)
 356                  else
 357                  {
 358                      if( $strict_typing )
 359                      {    // We want to make sure the value is valid:
 360                          $regexp = '';
 361                          switch( $type )
 362                          {
 363                              case 'boolean':
 364                                  $regexp = '/^(0|1|false|true)$/i';
 365                                  break;
 366  
 367                              case 'array/integer':
 368                              case 'array/array/integer':
 369                                  $type = 'array';
 370                              case 'integer':
 371                                  $regexp = '/^(\+|-)?[0-9]+$/';
 372                                  break;
 373  
 374                              case 'float':
 375                              case 'double':
 376                                  $regexp = '/^(\+|-)?[0-9]+(.[0-9]+)?$/';
 377                                  break;
 378  
 379                              // Note: other types are not tested here.
 380                          }
 381                          if( $strict_typing == 'allow_empty' && empty($GLOBALS[$var]) )
 382                          { // We have an empty value and we accept it
 383                              // ok..
 384                          }
 385                          elseif( !empty( $regexp ) )
 386                          {
 387                              if( $type == 'array' )
 388                              { // Check format of array elements
 389                                  $globals_var = $GLOBALS[$var];
 390                                  if( $one_dimensional )
 391                                  { // Convert to a two dimensional array to handle one and two dimensional arrays the same way
 392                                      $globals_var = array( $globals_var );
 393                                  }
 394                                  // Loop through the two dimensional array content and make sure each element is an integer and also set the type
 395                                  foreach( $globals_var as $i => $var_array )
 396                                  {
 397                                      foreach( $var_array as $j => $var_value )
 398                                      {
 399                                          if( !is_scalar( $var_value ) || !preg_match( $regexp, $var_value ) )
 400                                          { // Value of array item does not match!
 401                                              bad_request_die( sprintf( T_('Illegal value received for parameter &laquo;%s&raquo;!'), $var ) );
 402                                          }
 403                                          // Set the type of the array elements to integer
 404                                          settype( $globals_var[$i][$j], 'integer' );
 405                                      }
 406                                  }
 407                                  if( $one_dimensional )
 408                                  { // Extract real array from temp array
 409                                      $globals_var = $globals_var[0];
 410                                  }
 411                                  // Restore current array with prepared data
 412                                  $GLOBALS[$var] = $globals_var;
 413                              }
 414                              elseif( ( $type == 'boolean' ) && ( strtolower( $GLOBALS[$var] ) == 'false' ) )
 415                              { // 'false' string must be interpreted as boolean false value
 416                                  $GLOBALS[$var] = false;
 417                              }
 418                              elseif( !is_scalar( $GLOBALS[$var] ) || !preg_match( $regexp, $GLOBALS[$var] ) )
 419                              { // Value of scalar var does not match!
 420                                  bad_request_die( sprintf( T_('Illegal value received for parameter &laquo;%s&raquo;!'), $var ) );
 421                              }
 422                          }
 423                      }
 424  
 425                      // Change the variable type:
 426                      settype( $GLOBALS[$var], $type );
 427                      if( isset($Debuglog) ) $Debuglog->add( 'param(-): <strong>'.var_export($var, true).'</strong> typed to '.$type.', new value='.var_export($GLOBALS[$var], true), 'params' );
 428                  }
 429          }
 430      }
 431  
 432  
 433      /*
 434       * STEP 3: memorize the value for later url regeneration
 435       */
 436      if( $memorize )
 437      { // Memorize this parameter
 438          memorize_param( $var, $type, $default );
 439      }
 440  
 441      // echo $var, '(', gettype($GLOBALS[$var]), ')=', $GLOBALS[$var], '<br />';
 442      return $GLOBALS[$var];
 443  }
 444  
 445  
 446  /**
 447   * Get the param from an array param's first index instead of the value.
 448   *
 449   * E.g., for "param[value]" as a submit button you can get the value with
 450   *       <code>Request::param_arrayindex( 'param' )</code>.
 451   *
 452   * @see param_action()
 453   * @param string Param name
 454   * @param mixed Default to use
 455   * @return string
 456   */
 457  function param_arrayindex( $param_name, $default = '' )
 458  {
 459      $array = array_keys( param( $param_name, 'array', array() ) );
 460      $value = array_pop( $array );
 461      if( is_string($value) )
 462      {
 463          $value = substr( strip_tags($value), 0, 50 );  // sanitize it
 464      }
 465      elseif( !empty($value) )
 466      { // this is probably a numeric index from '<input name="array[]" .. />'
 467          debug_die( 'Invalid array param!' );
 468      }
 469      else
 470      {
 471          $value = $default;
 472      }
 473  
 474      return $value;
 475  }
 476  
 477  
 478  /**
 479   * Get the action from params.
 480   *
 481   * If we got no "action" param, we'll check for an "actionArray" param
 482   * ( <input type="submit" name="actionArray[real_action]" ...> ).
 483   * And the real $action will be found in the first key...
 484   * When there are multiple submit buttons, this is smarter than checking the value which is a translated string.
 485   * When there is an image button, this allows to work around IE not sending the value (it only sends X & Y coords of the click).
 486   *
 487   * @param mixed Default to use.
 488   * @return string
 489   */
 490  function param_action( $default = '', $memorize = false )
 491  {
 492      if( ! isset($_POST['actionArray']) )
 493      { // if actionArray is POSTed, use this instead of any "action" (which might come via GET)
 494          $action = param( 'action', 'string', NULL, $memorize );
 495      }
 496  
 497      if( ! isset($action) )
 498      { // Check $actionArray
 499          $action = param_arrayindex( 'actionArray', $default );
 500  
 501          set_param( 'action', $action ); // always set "action"
 502      }
 503  
 504      return $action;
 505  }
 506  
 507  
 508  /**
 509   * Get a param from cookie.
 510   *
 511   * {@internal This is just a wrapper around {@link param()} which unsets and
 512   *  restores GET and POST. IMHO this is less hackish, at least performance
 513   *  wise then using a $sources param for param()}}
 514   *
 515   * @uses param()
 516   * @see param()
 517   */
 518  function param_cookie($var, $type = '', $default = '', $memorize = false,
 519          $override = false, $use_default = true, $strict_typing = 'allow_empty')
 520  {
 521      $save_GET = $_GET;
 522      $save_POST = $_POST;
 523  
 524      unset( $_GET, $_POST );
 525  
 526      $r = param( $var, $type, $default, $memorize, $override, $use_default, $strict_typing );
 527  
 528      $_GET = $save_GET;
 529      $_POST = $save_POST;
 530  
 531      return $r;
 532  }
 533  
 534  
 535  /**
 536   * Get total seconds from the following fields: months, days, hours, minutes, seconds
 537   *
 538   * @param string param name
 539   * @return integer seconds
 540   */
 541  function param_duration( $var )
 542  {
 543      $timeout_sessions_months = param( $var.'_months', 'integer', 0 );
 544      $timeout_sessions_days = param( $var.'_days', 'integer', 0 );
 545      $timeout_sessions_hours = param( $var.'_hours', 'integer', 0 );
 546      $timeout_sessions_minutes = param( $var.'_minutes', 'integer', 0 );
 547      $timeout_sessions_seconds = param( $var.'_seconds', 'integer', 0 );
 548  
 549      $timeout_sessions = ( ( ( $timeout_sessions_months*30 + $timeout_sessions_days )*24
 550                  + $timeout_sessions_hours )*60 + $timeout_sessions_minutes )*60 + $timeout_sessions_seconds;
 551  
 552      return $timeout_sessions;
 553  }
 554  
 555  
 556  /**
 557   * @param string param name
 558   * @param string error message
 559   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
 560   * @return boolean true if OK
 561   */
 562  function param_string_not_empty( $var, $err_msg, $field_err_msg = NULL )
 563  {
 564      param( $var, 'string', true );
 565      return param_check_not_empty( $var, $err_msg, $field_err_msg );
 566  }
 567  
 568  
 569  /**
 570   * @param string param name
 571   * @param string error message
 572   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
 573   * @return boolean true if OK
 574   */
 575  function param_check_not_empty( $var, $err_msg = NULL, $field_err_msg = NULL )
 576  {
 577      if( empty( $GLOBALS[$var] ) )
 578      {
 579          if( empty($err_msg) )
 580          {
 581              $err_msg = sprintf( T_('The field &laquo;%s&raquo; cannot be empty.'), substr( $var, strpos( $var, '_' )+1 ) );
 582              $field_err_msg = T_('This field cannot be empty.');
 583          }
 584  
 585          param_error( $var, $err_msg, $field_err_msg );
 586          return false;
 587      }
 588      return true;
 589  }
 590  
 591  
 592  /**
 593   * Checks if the param is an integer (no float, e.g. 3.14).
 594   *
 595   * @param string param name
 596   * @param string error message
 597   * @return boolean true if OK
 598   */
 599  function param_check_number( $var, $err_msg, $required = false )
 600  {
 601      return param_validate( $var, 'check_is_number', $required, $err_msg );
 602  }
 603  
 604  
 605  /**
 606   * Check for interval params
 607   *
 608   * @param string Min param name
 609   * @param string Max param name
 610   * @param string error message if value is NOT a number
 611   * @param string error message if min value greater than max
 612   * @return boolean true if OK
 613   */
 614  function param_check_interval( $var_min, $var_max, $err_msg_number, $err_msg_compare, $required = false )
 615  {
 616      $result_min = param_validate( $var_min, 'check_is_number', $required, $err_msg_number );
 617      $result_max = param_validate( $var_max, 'check_is_number', $required, $err_msg_number );
 618      $result_compare = true;
 619  
 620      if( $result_min && $result_max )
 621      {
 622          if( empty( $GLOBALS[$var_min] ) && !empty( $GLOBALS[$var_max] ) )
 623          {    // Get min value from max value
 624              $GLOBALS[$var_min] = $GLOBALS[$var_max];
 625          }
 626          if( !empty( $GLOBALS[$var_min] ) && empty( $GLOBALS[$var_max] ) )
 627          {    // Get max value from min value
 628              $GLOBALS[$var_max] = $GLOBALS[$var_min];
 629          }
 630  
 631          $result_compare = $GLOBALS[$var_min] <= $GLOBALS[$var_max];
 632          if( !$result_compare )
 633          {    // Min value greater than max
 634              param_error( $var_min, $err_msg_compare );
 635          }
 636      }
 637  
 638      return $result_min && $result_max && $result_compare;
 639  }
 640  
 641  
 642  /**
 643   * Checks if the param is an integer (no float, e.g. 3.14).
 644   *
 645   * @param string number to check
 646   * @return string error message if number is not valid
 647   */
 648  function check_is_number( $number )
 649  {
 650      if( !is_number( $number ) )
 651      {
 652          return T_('The number value is invalid.');
 653      }
 654  }
 655  
 656  
 657  /**
 658   * Checks if the param is a decimal number
 659   *
 660   * @param string param name
 661   * @param string error message
 662   * @return boolean true if OK
 663   */
 664  function param_check_decimal( $var, $err_msg, $required = false )
 665  {
 666      return param_validate( $var, 'check_is_decimal', $required, $err_msg );
 667  }
 668  
 669  
 670  /**
 671   * Checks if the param is a decimal number
 672   *
 673   * @param string decimal to check
 674   * @return string error message if decimal is not valid
 675   */
 676  function check_is_decimal( $decimal )
 677  {
 678      if( !is_decimal( $decimal ) )
 679      {
 680          return T_('The decimal value is invalid.');
 681      }
 682  }
 683  
 684  
 685  /**
 686   * Gets a param and makes sure it's a decimal number (no float, e.g. 3.14) in a given range.
 687   *
 688   * @param string param name
 689   * @param integer min value
 690   * @param integer max value
 691   * @param string error message (gets printf'ed with $min and $max)
 692   * @return boolean true if OK
 693   */
 694  function param_integer_range( $var, $min, $max, $err_msg, $required = true )
 695  {
 696      param( $var, 'integer', $required ? true : '' );
 697      return param_check_range( $var, $min, $max, $err_msg, $required );
 698  }
 699  
 700  
 701  /**
 702   * Checks if the param is a decimal number (no float, e.g. 3.14) in a given range.
 703   *
 704   * @param string param name
 705   * @param integer min value
 706   * @param integer max value
 707   * @param string error message (gets printf'ed with $min and $max)
 708   * @param boolean Is the param required?
 709   * @return boolean true if OK
 710   */
 711  function param_check_range( $var, $min, $max, $err_msg, $required = true )
 712  {
 713      if( empty( $GLOBALS[$var] ) && ! $required )
 714      { // empty is OK:
 715          return true;
 716      }
 717  
 718      if( ! preg_match( '~^[-+]?\d+$~', $GLOBALS[$var] ) || $GLOBALS[$var] < $min || $GLOBALS[$var] > $max )
 719      {
 720          param_error( $var, sprintf( $err_msg, $min, $max ) );
 721          return false;
 722      }
 723      return true;
 724  }
 725  
 726  
 727  /**
 728   * @param string param name
 729   * @return boolean true if OK
 730   */
 731  function param_check_email( $var, $required = false )
 732  {
 733      return param_validate( $var, 'check_is_email', $required, NULL );
 734  }
 735  
 736  
 737  /**
 738   * Check that email address looks valid.
 739   *
 740   * @param string email address to check
 741   * @return string error message if address is not valid
 742   */
 743  function check_is_email( $email )
 744  {
 745      if( !is_email( $email ) )
 746      {
 747          return T_( 'The email address is invalid.' );
 748      }
 749  }
 750  
 751  
 752  /**
 753   * Check if the value is a valid login (in terms of allowed chars)
 754   *
 755   * @param string param name
 756   * @return boolean true if OK
 757   */
 758  function param_check_valid_login( $var )
 759  {
 760      global $Settings;
 761  
 762      if( empty( $GLOBALS[$var] ) )
 763      { // empty variable is OK
 764          return T_('Please choose a username.' );
 765      }
 766  
 767      $check = is_valid_login($GLOBALS[$var]);
 768  
 769      if( ! $check || $check === 'usr' )
 770      {
 771          if( $check === 'usr' )
 772          {    // Special case, the login is valid however we forbid it's usage.
 773              $msg = T_('Logins cannot start with "usr_", this prefix is reserved for system use.');
 774          }
 775          elseif( $Settings->get('strict_logins') )
 776          {
 777              $msg = T_('Logins can only contain letters, digits and the following characters: _ .');
 778          }
 779          else
 780          {
 781              $msg = sprintf( T_('Logins cannot contain whitespace and the following characters: %s'), '\', ", >, <, @' );
 782          }
 783          param_error( $var, $msg );
 784          return false;
 785      }
 786      return true;
 787  }
 788  
 789  
 790  /**
 791   * @param string param name
 792   * @return boolean true if OK
 793   */
 794  function param_check_login( $var, $required = false )
 795  {
 796      return param_validate( $var, 'check_is_login', $required, NULL );
 797  }
 798  
 799  
 800  /**
 801   * Check that login is valid.
 802   *
 803   * @param string login to check
 804   * @return string error message if login is not valid
 805   */
 806  function check_is_login( $login )
 807  {
 808      if( !user_exists( $login ) )
 809      {
 810           return sprintf( T_( 'There is no user with username &laquo;%s&raquo;.' ), $login );
 811      }
 812  }
 813  
 814  
 815  /**
 816   * @param string param name
 817   * @param string
 818   * @return boolean true if OK
 819   */
 820  function param_check_url( $var, $context, $field_err_msg = NULL )
 821  {
 822    /**
 823       * @var User
 824       */
 825      global $current_User;
 826  
 827      $Group = $current_User->get_Group();
 828  
 829      if( strpos( $var, '[' ) !== false )
 830      {    // Variable is array, for example 'input_name[group_name][123][]'
 831          // We should get a value from $GLOBALS[input_name][group_name][123][0]
 832          $var_array = explode( '[', $var );
 833          $url_value = $GLOBALS;
 834          foreach( $var_array as $var_item )
 835          {
 836              $var_item = str_replace( ']', '', $var_item);
 837              if( empty( $var_item ) )
 838              {    // Case for []
 839                  $var_item = '0';
 840              }
 841              $url_value = $url_value[$var_item];
 842          }
 843      }
 844      else
 845      {    // Variable with simple name
 846          $url_value = $GLOBALS[$var];
 847      }
 848  
 849      if( $error_detail = validate_url( $url_value, $context, ! $Group->perm_bypass_antispam ) )
 850      {
 851          param_error( $var, /* TRANS: %s contains error details */ sprintf( T_('Supplied URL is invalid. (%s)'), $error_detail ), $field_err_msg );
 852          return false;
 853      }
 854      return true;
 855  }
 856  
 857  
 858  /**
 859   * Checks if the url is valid
 860   *
 861   * @param string url to check
 862   * @return string error message if url is not valid
 863   */
 864  function check_is_url( $url )
 865  {
 866      if( !is_url( $url ) )
 867      {
 868          return sprintf( T_('Please enter a valid URL, like for example: %s.'), 'http://www.b2evolution.net/' );
 869      }
 870  }
 871  
 872  
 873  /**
 874   * Check if the value is a file name
 875   *
 876   * @param string param name
 877   * @param string error message
 878   * @return boolean true if OK
 879   */
 880  function param_check_filename( $var, $err_msg )
 881  {
 882      if( $error_filename = validate_filename( $GLOBALS[$var] ) )
 883      {
 884          param_error( $var, $error_filename );
 885          return false;
 886      }
 887      return true;
 888  }
 889  
 890  
 891  /**
 892   * Check if the value of a param is a regular expression (syntax).
 893   *
 894   * @param string param name
 895   * @param string error message
 896   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
 897   * @return boolean true if OK
 898   */
 899  function param_check_isregexp( $var, $err_msg, $field_err_msg = NULL )
 900  {
 901      if( ! is_regexp( $GLOBALS[$var] ) )
 902      {
 903          param_error( $var, $err_msg, $field_err_msg );
 904          return false;
 905      }
 906      return true;
 907  }
 908  
 909  
 910  /**
 911   * Check if the value of a param MATCHES a regular expression (syntax).
 912   *
 913   * @param string param name
 914   * @param string regexp
 915   * @param string error message
 916   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
 917   * @return boolean true if OK
 918   */
 919  function param_check_regexp( $var, $regexp, $err_msg, $field_err_msg = NULL, $required = true )
 920  {
 921      if( empty( $GLOBALS[$var] ) && ! $required )
 922      { // empty variable is OK
 923          return true;
 924      }
 925  
 926      if( ! preg_match( $regexp, $GLOBALS[$var] ) )
 927      {
 928          param_error( $var, $err_msg, $field_err_msg );
 929          return false;
 930      }
 931      return true;
 932  }
 933  
 934  
 935  /**
 936   * Sets a date parameter by converting locale date (if valid) to ISO date.
 937   *
 938   * If the date is not valid, it is set to the param unchanged (unconverted).
 939   *
 940   * @param string param name
 941   * @param string error message
 942   * @param boolean Is a non-empty date required?
 943   * @param string Default (in the format of $date_format)
 944   * @param string date format (php format), defaults to {@link locale_datefmt()}
 945   */
 946  function param_date( $var, $err_msg, $required, $default = '', $date_format = NULL )
 947  {
 948      param( $var, 'string', $default );
 949  
 950      $iso_date = param_check_date( $var, $err_msg, $required, $date_format );
 951  
 952      if( $iso_date )
 953      {
 954          set_param( $var, $iso_date );
 955      }
 956  
 957      return $iso_date;
 958  }
 959  
 960  
 961  /**
 962   * Check if param is an ISO date.
 963   *
 964   * NOTE: for tokens like e.g. "D" (abbr. weekday), T_() gets used and it uses the current locale!
 965   *
 966   * @param string param name
 967   * @param string error message
 968   * @param boolean Is a non-empty date required?
 969   * @param string date format (php format)
 970   * @return boolean|string false if not OK, ISO date if OK
 971   */
 972  function param_check_date( $var, $err_msg, $required = false, $date_format = NULL )
 973  {
 974      if( empty( $GLOBALS[$var] ) )
 975      { // empty is OK if not required:
 976          if( $required )
 977          {
 978              param_error( $var, $err_msg );
 979              return false;
 980          }
 981          return '';
 982      }
 983  
 984      if( empty( $date_format ) )
 985      {    // Use locale date format:
 986          $date_format = locale_datefmt();
 987      }
 988  
 989      // Convert PHP date format to regexp pattern:
 990      $date_regexp = '~^'.preg_replace_callback( '~(\\\)?(\w)~', create_function( '$m', '
 991          if( $m[1] == "\\\" ) return $m[2]; // escaped
 992          switch( $m[2] )
 993          {
 994              case "d": return "([0-3]\\d)"; // day, 01-31
 995              case "j": return "([1-3]?\\d)"; // day, 1-31
 996              case "l": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday"])))).")";
 997              case "D": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday_abbrev"])))).")";
 998              case "e": // b2evo extension!
 999                  return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday_letter"])))).")";
1000              case "S": return "(st|nd|rd|th)"; // english suffix for day
1001  
1002              case "m": return "([0-1]\\d)"; // month, 01-12
1003              case "n": return "(1?\\d)"; // month, 1-12
1004              case "F": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["month"])))).")"; //  A full textual representation of a month, such as January or March
1005              case "M": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["month_abbrev"])))).")";
1006  
1007              case "y": return "(\\d\\d)"; // year, 00-99
1008              case "Y": return "(\\d{4})"; // year, XXXX
1009              default:
1010                  return $m[0];
1011          }' ), $date_format ).'$~i'; // case-insensitive?
1012      // Allow additional spaces, e.g. "03  May 2007" when format is "d F Y":
1013      $date_regexp = preg_replace( '~ +~', '\s+', $date_regexp );
1014      // echo $date_format.'...'.$date_regexp;
1015  
1016      // Check that the numbers match the date pattern:
1017      if( preg_match( $date_regexp, $GLOBALS[$var], $numbers ) )
1018      {    // Date does match pattern:
1019          //pre_dump( $numbers );
1020  
1021          // Get all date pattern parts. We should get 3 parts!:
1022          preg_match_all( '/(?<!\\\\)[A-Za-z]/', $date_format, $parts ); // "(?<!\\\\)" means that the letter is not escaped with "\"
1023          //pre_dump( $parts );
1024          $day = null;
1025          $month = null;
1026          $year = null;
1027  
1028          foreach( $parts[0] as $position => $part )
1029          {
1030              switch( $part )
1031              {
1032                  case 'd':
1033                  case 'j':
1034                      $day = $numbers[$position+1];
1035                      break;
1036  
1037                  case 'm':
1038                  case 'n':
1039                      $month = $numbers[$position+1];
1040                      break;
1041                  case 'F': // full month name
1042                      $month = array_search( strtolower($numbers[$position+1]), array_map('strtolower', array_map('trim', array_map('T_', $GLOBALS['month']))) );
1043                      break;
1044                  case 'M':
1045                      $month = array_search( strtolower($numbers[$position+1]), array_map('strtolower', array_map('trim', array_map('T_', $GLOBALS['month_abbrev']))) );
1046                      break;
1047  
1048                  case 'y':
1049                  case 'Y':
1050                      $year = $numbers[$position+1];
1051                      if( $year < 50 )
1052                      {
1053                          $year = 2000 + $year;
1054                      }
1055                      elseif( $year < 100 )
1056                      {
1057                          $year = 1900 + $year;
1058                      }
1059                      break;
1060              }
1061          }
1062  
1063          if( checkdate( $month, $day, $year ) )
1064          { // all clean! :)
1065  
1066              // We convert the value to ISO:
1067              $iso_date = substr( '0'.$year, -4 ).'-'.substr( '0'.$month, -2 ).'-'.substr( '0'.$day, -2 );
1068  
1069              return $iso_date;
1070          }
1071      }
1072  
1073      // Date did not pass all tests:
1074  
1075      param_error( $var, $err_msg );
1076  
1077      return false;
1078  }
1079  
1080  
1081  /**
1082   * Sets a date parameter with values from the request or to provided default,
1083   * And check we have a compact date (numbers only) ( used for URL filtering )
1084   *
1085   * @param string Variable to set
1086   * @param mixed Default value or TRUE if user input required
1087   * @param boolean memorize ( see {@link param()} )
1088   * @param string error message
1089   * @param boolean 'required': Is non-empty date required? Default: true.
1090   *
1091   * @return string the compact date value ( yyyymmdd )
1092   */
1093  function param_compact_date( $var, $default = '', $memorize = false, $err_msg, $required = false )
1094  {
1095      global $$var;
1096  
1097      param( $var, 'string', $default, $memorize );
1098  
1099      if( preg_match( '#^[0-9]{4,}$#', $$var ) )
1100      {    // Valid compact date, all good.
1101          return $$var;
1102      }
1103  
1104      // We do not have a compact date, try normal date matching:
1105      $iso_date = param_check_date( $var, $err_msg, $required );
1106  
1107      if( $iso_date )
1108      {
1109          set_param( $var, compact_date( $iso_date ) );
1110          return $$var;
1111      }
1112  
1113      // Nothing valid found....
1114      return '';
1115  }
1116  
1117  
1118  /**
1119   * Sets a time parameter with the value from the request of the var argument
1120   * or of the concat of the var argument_h: var argument_mn: var argument_s ,
1121   * except if param is already set!
1122   *
1123   * @param string Variable to set
1124   * @param mixed Default value or TRUE if user input required
1125   * @param boolean Do we need to memorize this to regenerate the URL for this page?
1126   * @param boolean Override if variable already set
1127   * @param boolean Force setting of variable to default?
1128   * @return mixed Final value of Variable, or false if we don't force setting and did not set
1129   */
1130  function param_time( $var, $default = '', $memorize = false,    $override = false, $forceset = true )
1131  {
1132      global $$var;
1133  
1134      $got_time = false;
1135  
1136      if( param( $var, 'string', $default, $memorize, $override, $forceset ) )
1137      { // Got a time from text field:
1138          if( preg_match( '/^(\d\d):(\d\d)(:(\d\d))?$/', $$var, $matches ) )
1139          {
1140              $time_h = $matches[1];
1141              $time_mn = $matches[2];
1142              $time_s = empty( $matches[4] ) ? 0 : $matches[4];
1143              $got_time = true;
1144          }
1145      }
1146      elseif( ( $time_h = param( $var.'_h', 'integer', -1 ) ) != -1
1147                  && ( $time_mn = param( $var.'_mn', 'integer', -1 ) ) != -1 )
1148      {    // Got a time from selects:
1149          $time_s = param( $var.'_s', 'integer', 0 );
1150          $$var = substr('0'.$time_h,-2).':'.substr('0'.$time_mn,-2).':'.substr('0'.$time_s,-2);
1151          $got_time = true;
1152      }
1153  
1154      if( $got_time )
1155      { // We got a time...
1156          // Check if ranges are correct:
1157          if( $time_h >= 0 && $time_h <= 23
1158              && $time_mn >= 0 && $time_mn <= 59
1159              && $time_s >= 0 && $time_s <= 59 )
1160          {
1161              // Time is correct
1162              return $$var;
1163          }
1164      }
1165  
1166      param_error( $var, T_('Please enter a valid time.') );
1167  
1168      return false;
1169  }
1170  
1171  
1172  /**
1173   * Extend a LIST parameter with an ARRAY param.
1174   *
1175   * Will be used for author/authorsel[], etc.
1176   * Note: cannot be used for catsel[], because catsel is NON-recursive.
1177   * @see param_compile_cat_array()
1178   *
1179   * @param string Variable to extend
1180   * @param string Name of array Variable to use as an extension
1181   * @param boolean Save non numeric prefix?  ( 1 char -- can be used as a modifier, e-g: - + * )
1182   */
1183  function param_extend_list( $var, $var_ext_array, $save_prefix = true )
1184  {
1185      // Make sure original var exists:
1186      if( !isset($GLOBALS[$var]) )
1187      {
1188          debug_die( 'Cannot extend non existing param : '.$var );
1189      }
1190      $original_val = $GLOBALS[$var];
1191  
1192      // Get extension array:
1193      $ext_values_array = param( $var_ext_array, 'array', array(), false );
1194      if( empty($ext_values_array) )
1195      {    // No extension required:
1196          return $original_val;
1197      }
1198  
1199      // Handle prefix:
1200      $prefix = '';
1201      if( $save_prefix )
1202      {    // We might want to save a prefix:
1203          $prefix = substr( $original_val, 0, 1 );
1204          if( is_numeric( $prefix ) )
1205          {    // The prefix is numeric, so it's NOT a prefix
1206              $prefix = '';
1207          }
1208          else
1209          {    // We save the prefix, we must crop if off from the values:
1210              $original_val = substr( $original_val, 1 );
1211          }
1212      }
1213  
1214      // Merge values:
1215      if( empty($original_val) )
1216      {
1217          $original_values_array = array();
1218      }
1219      else
1220      {
1221          $original_values_array = explode( ',', $original_val );
1222      }
1223      $new_values = array_merge( $original_values_array, $ext_values_array );
1224      $new_values = array_unique( $new_values );
1225      $GLOBALS[$var] = $prefix.implode( ',', $new_values );
1226  
1227  
1228      return $GLOBALS[$var];
1229  }
1230  
1231  
1232  /**
1233   * Compiles the cat array from $cat (recursive + optional modifiers) and $catsel[] (non recursive)
1234   * and keeps those values available for future reference (category widget)
1235   */
1236  function param_compile_cat_array( $restrict_to_blog = 0, $cat_default = NULL, $catsel_default = array() )
1237  {
1238      // For now, we'll need those as globals!
1239      // fp> this is used for the categories widget
1240      // fp> we want might to use a $set_globals params to compile_cat_array()
1241      global $cat_array, $cat_modifier;
1242  
1243      $cat = param( 'cat', '/^[*\-\|]?([0-9]+(,[0-9]+)*)?$/', $cat_default, true ); // List of cats to restrict to
1244      $catsel = param( 'catsel', 'array/integer', $catsel_default, true );  // Array of cats to restrict to
1245  
1246      $cat_array = array();
1247      $cat_modifier = '';
1248  
1249      compile_cat_array( $cat, $catsel, /* by ref */ $cat_array, /* by ref */ $cat_modifier, $restrict_to_blog );
1250  }
1251  
1252  
1253  /**
1254   * @param array of param names
1255   * @param string error message
1256   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
1257   * @return boolean true if OK
1258   */
1259  function params_check_at_least_one( $vars, $err_msg, $field_err_msg = NULL )
1260  {
1261      foreach( $vars as $var )
1262      {
1263          if( !empty( $GLOBALS[$var] ) )
1264          { // Okay, we got at least one:
1265              return true;
1266          }
1267      }
1268  
1269      // Error!
1270      param_error_multiple( $vars, $err_msg, $field_err_msg );
1271      return false;
1272  }
1273  
1274  
1275  /**
1276   * Sets a combo parameter with values from the request,
1277   * => the value of the select option and the input text value if new is selected
1278   * Display an error if the new value is selected that the input text has a value
1279   *
1280   * @param string Variable to set
1281   * @param mixed Default value or TRUE if user input required
1282   * @param boolean true: allows to select new without entring a value in the input combo text
1283   * @param string error message
1284   *
1285   * @return string position status ID or 'new' or '' if new is seleted but not input text value
1286   *
1287   */
1288  function param_combo( $var, $default, $allow_none, $err_msg = ''  )
1289  {
1290      param( $var, 'string', $default );
1291  
1292      if( $GLOBALS[$var] == 'new' )
1293      {    // The new option is selected in the combo select, so we need to check if we have a value in the combo input text:
1294          $GLOBALS[$var.'_combo'] = param( $var.'_combo', 'string' );
1295  
1296          if( empty( $GLOBALS[$var.'_combo'] ) )
1297          { // We have no value in the combo input text
1298  
1299              // Set request param to null
1300              $GLOBALS[$var] = NULL;
1301  
1302              if( !$allow_none )
1303              { // it's not allowed, so display error:
1304                  param_error( $var, $err_msg );
1305              }
1306          }
1307      }
1308  
1309      return $GLOBALS[$var];
1310  }
1311  
1312  
1313  /**
1314   * set a parameter with the second part(X2) of the value from request ( X1-X2 )
1315   *
1316   * @param string Variable to set
1317   *
1318   */
1319  function param_child_select_value( $var )
1320  {
1321      global $$var;
1322  
1323      if( $val = param( $var, 'string' ) )
1324      { // keep only the second part of val
1325          preg_match( '/^[0-9]+-([0-9]+)$/', $val, $res );
1326  
1327          if( isset( $res[1] ) )
1328          { //set to the var the second part of val
1329              $$var = $res[1];
1330              return $$var;
1331          }
1332      }
1333      return '';
1334  }
1335  
1336  
1337  /**
1338   * @param string param name
1339   * @return boolean true if OK
1340   */
1341  function param_check_phone( $var, $required = false )
1342  {
1343      global $$var;
1344  
1345      if( empty( $$var ) && ! $required )
1346      { // empty is OK:
1347          return true;
1348      }
1349  
1350      if( ! preg_match( '|^\+?[\-*#/(). 0-9]+$|', $$var ) )
1351      {
1352          param_error( $var, T_('The phone number is invalid.') );
1353          return false;
1354      }
1355      else
1356      { // Keep only 0123456789+ caracters
1357          $$var = preg_replace( '#[^0-9+]#', '', $$var );
1358      }
1359      return true;
1360  }
1361  
1362  
1363  /**
1364   * Checks if the phone number is valid
1365   *
1366   * @param string phone number to check
1367   * @return string error message if phone number is not valid
1368   */
1369  function check_is_phone( $phone )
1370  {
1371      if( !is_phone( $phone ) )
1372      {
1373          return sprintf( T_('Please enter a valid phone number like for example: %s.'), '+1 401-555-1234' );
1374      }
1375  }
1376  
1377  
1378  /**
1379   * @param string param name
1380   * @param string param name
1381   * @param boolean Is a password required? (non-empty)
1382   * @param integer Minimum password length
1383   * @return boolean true if OK
1384   */
1385  function param_check_passwords( $var1, $var2, $required = false, $min_length = 6 )
1386  {
1387      $pass1 = get_param($var1);
1388      $pass2 = get_param($var2);
1389  
1390      if( ! strlen($pass1) && ! strlen($pass2) && ! $required )
1391      { // empty is OK:
1392          return true;
1393      }
1394  
1395      if( ! strlen($pass1) )
1396      {
1397          param_error( $var1, T_('Please enter your new password.') );
1398          param_error( $var2, T_('Please enter your new password twice.') );
1399          return false;
1400      }
1401      if( ! strlen($pass2) )
1402      {
1403          param_error( $var2, T_('Please enter your new password twice.') );
1404          return false;
1405      }
1406  
1407      // checking the password has been typed twice the same:
1408      if( $pass1 != $pass2 )
1409      {
1410          param_error_multiple( array( $var1, $var2), T_('You typed two different passwords.') );
1411          return false;
1412      }
1413  
1414      if( evo_strlen($pass1) < $min_length )
1415      {
1416          param_error_multiple( array( $var1, $var2), sprintf( T_('The minimum password length is %d characters.'), $min_length ) );
1417          return false;
1418      }
1419  
1420      return true;
1421  }
1422  
1423  
1424  /**
1425   * Checks if the word is valid
1426   *
1427   * @param string word to check
1428   * @return string error message if word is not valid
1429   */
1430  function check_is_word( $word )
1431  {
1432      if( !is_word( $word ) )
1433      {
1434          return T_('This field should be a single word; A-Z only.');
1435      }
1436  }
1437  
1438  
1439  /**
1440   * Check if there have been validation errors
1441   *
1442   * We play it safe here and check for all kind of errors, not just those from this particular class.
1443   *
1444   * @return integer
1445   */
1446  function param_errors_detected()
1447  {
1448      global $Messages;
1449  
1450      return $Messages->has_errors();
1451  }
1452  
1453  
1454  /**
1455   * Tell if there is an error on given field.
1456   */
1457  function param_has_error( $var )
1458  {
1459      global $param_input_err_messages;
1460  
1461      return isset( $param_input_err_messages[$var] );
1462  }
1463  
1464  
1465  /**
1466   * Get error message for a param
1467   *
1468   * @return string
1469   */
1470  function param_get_error_msg( $var )
1471  {
1472      global $param_input_err_messages;
1473  
1474      if( empty( $param_input_err_messages[$var] ) )
1475      {
1476          return '';
1477      }
1478  
1479      return $param_input_err_messages[$var];
1480  }
1481  
1482  
1483  /**
1484   * Add an error for a variable, either to the Form's field and/or the global {@link $Messages} object.
1485   *
1486   * @param string param name
1487   * @param string|NULL error message (by using NULL you can only add an error to the field, but not the $Message object)
1488   * @param string|NULL error message for form field ($err_msg gets used if === NULL).
1489   */
1490  function param_error( $var, $err_msg, $field_err_msg = NULL )
1491  {
1492      global $param_input_err_messages;
1493  
1494      if( ! isset( $param_input_err_messages[$var] ) )
1495      { // We haven't already recorded an error for this field:
1496          if( $field_err_msg === NULL )
1497          {
1498              $field_err_msg = $err_msg;
1499          }
1500          $param_input_err_messages[$var] = $field_err_msg;
1501  
1502          if( isset($err_msg) )
1503          {
1504              param_add_message_to_Log( $var, $err_msg, 'error' );
1505          }
1506      }
1507  }
1508  
1509  
1510  /**
1511   * Add an error for multiple variables, either to the Form's field and/or the global {@link $Messages} object.
1512   *
1513   * @param array of param names
1514   * @param string|NULL error message (by using NULL you can only add an error to the field, but not the $Message object)
1515   * @param string|NULL error message for form fields ($err_msg gets used if === NULL).
1516   */
1517  function param_error_multiple( $vars, $err_msg, $field_err_msg = NULL )
1518  {
1519      global $param_input_err_messages;
1520  
1521      if( $field_err_msg === NULL )
1522      {
1523          $field_err_msg = $err_msg;
1524      }
1525  
1526      foreach( $vars as $var )
1527      {
1528          if( ! isset( $param_input_err_messages[$var] ) )
1529          { // We haven't already recorded an error for this field:
1530              $param_input_err_messages[$var] = $field_err_msg;
1531          }
1532      }
1533  
1534      if( isset($err_msg) )
1535      {
1536          param_add_message_to_Log( $var, $err_msg, 'error' );
1537      }
1538  }
1539  
1540  
1541  /**
1542   * This function is used by {@link param_error()} and {@link param_error_multiple()}.
1543   *
1544   * If {@link $link_param_err_messages_to_field_IDs} is true, it will link those parts of the
1545   * error message that are not already links, to the html IDs of the fields with errors.
1546   *
1547   * @param string param name
1548   * @param string error message
1549   */
1550  function param_add_message_to_Log( $var, $err_msg, $log_category = 'error' )
1551  {
1552      global $link_param_err_messages_to_field_IDs;
1553      global $Messages;
1554  
1555      if( !empty($link_param_err_messages_to_field_IDs) )
1556      {
1557          $var_id = Form::get_valid_id($var);
1558          $start_link = '<a href="#'.$var_id.'" onclick="var form_elem = document.getElementById(\''.$var_id.'\'); if( form_elem ) { if(form_elem.select) { form_elem.select(); } else if(form_elem.focus) { form_elem.focus(); } }">'; // "SELECT" does not have .select()
1559  
1560          if( strpos( $err_msg, '<a' ) !== false )
1561          { // there is at least one link in $err_msg, link those parts that are no links
1562              $err_msg = preg_replace( '~(\s*)(<a\s+[^>]+>[^<]*</a>\s*)~i', '</a>$1&raquo;$2'.$start_link, $err_msg );
1563          }
1564  
1565          if( substr($err_msg, 0, 4) == '</a>' )
1566          { // There was a link at the beginning of $err_msg: we do not prepend an emtpy link before it
1567              $Messages->add( substr( $err_msg, 4 ).'</a>', $log_category );
1568          }
1569          else
1570          {
1571              $Messages->add( $start_link.$err_msg.'</a>', $log_category );
1572          }
1573      }
1574      else
1575      {
1576          $Messages->add( $err_msg, $log_category );
1577      }
1578  }
1579  
1580  
1581  
1582  /**
1583   * Set a param (global) & Memorize it for automatic future use in regenerate_url()
1584   *
1585   * @param string Variable to memorize
1586   * @param string Type of the variable
1587   * @param mixed Default value to compare to when regenerating url
1588   * @param mixed Value to set
1589   */
1590  function memorize_param( $var, $type, $default, $value = NULL )
1591  {
1592      global $Debuglog, $global_param_list, $$var;
1593  
1594      if( !isset($global_param_list) )
1595      { // Init list if necessary:
1596          if( isset($Debuglog) ) $Debuglog->add( 'init $global_param_list', 'params' );
1597          $global_param_list = array();
1598      }
1599  
1600      $Debuglog->add( 'memorize_param: '.var_export($var, true).' '.var_export($type, true).' default='.var_export($default, true).
1601                  ( is_null($value) ? '' : ' value='.var_export($value, true) ), 'params' );
1602  
1603      $global_param_list[$var] = array( 'type' => $type, 'default' => (($default===true) ? NULL : $default) );
1604  
1605      if( !is_null( $value ) )
1606      {    // We want to set the variable too.
1607          set_param( $var, $value );
1608      }
1609  }
1610  
1611  
1612  /**
1613   * Forget a param so that is will not get included in subsequent {@link regenerate_url()} calls.
1614   * @param string Param name
1615   */
1616  function forget_param( $var )
1617  {
1618      global $Debuglog, $global_param_list;
1619  
1620      $Debuglog->add( 'forget_param('.$var.')', 'params' );
1621  
1622      unset( $global_param_list[$var] );
1623  }
1624  
1625  
1626  /**
1627   * Has the param already been memorized?
1628   */
1629  function param_ismemorized( $var )
1630  {
1631      global $global_param_list;
1632  
1633      return isset($global_param_list[$var]);
1634  }
1635  
1636  
1637  /**
1638   * Set the value of a param (by force! :P)
1639   *
1640   * Same as setting a global, except you don't need a global declaration in your function.
1641   *
1642   * @param string Param name
1643   * @param mixed Value
1644   * @return mixed Value
1645   */
1646  function set_param( $var, $value )
1647  {
1648      return $GLOBALS[$var] = $value;
1649  }
1650  
1651  
1652  
1653  /**
1654   * Get the value of a param.
1655   *
1656   * @return NULL|mixed The value of the param, if set. NULL otherwise.
1657   */
1658  function get_param( $var )
1659  {
1660      if( ! isset($GLOBALS[$var]) )
1661      {
1662          return NULL;
1663      }
1664  
1665      return $GLOBALS[$var];
1666  }
1667  
1668  
1669  /**
1670   * Construct an array of memorized params which are not in the ignore list
1671   *
1672   * @param mixed string or array of ignore params
1673   */
1674  function get_memorized( $ignore = '' )
1675  {
1676      global $global_param_list;
1677  
1678      $memo = array();
1679  
1680      // Transform ignore params into an array:
1681      if( empty ( $ignore ) )
1682      {
1683          $ignore = array();
1684      }
1685      elseif( !is_array($ignore) )
1686      {
1687          $ignore = explode( ',', $ignore );
1688      }
1689  
1690      // Loop on memorize params
1691      if( isset($global_param_list) )
1692      {
1693          foreach( $global_param_list as $var => $thisparam )
1694          {
1695              if( !in_array( $var, $ignore ) )
1696              {
1697                  global $$var;
1698                  $value = $$var;
1699                  $memo[$var] = $$var;
1700              }
1701          }
1702      }
1703      return $memo;
1704  }
1705  
1706  
1707  /**
1708   * Regenerate current URL from parameters
1709   * This may clean it up
1710   * But it is also useful when generating static pages: you cannot rely on $_REQUEST[]
1711   *
1712   * @param mixed|string (delimited by commas) or array of params to ignore (can be regexps in /.../)
1713   * @param array|string Param(s) to set
1714   * @param mixed|string Alternative URL we want to point to if not the current URL (may be absolute if BASE tag gets used)
1715   * @param string Delimiter to use for multiple params (typically '&amp;' or '&')
1716   */
1717  function regenerate_url( $ignore = '', $set = '', $pagefileurl = '', $glue = '&amp;' )
1718  {
1719      global $Debuglog, $global_param_list, $ReqHost, $ReqPath;
1720      global $base_tag_set;
1721  
1722      // Transform ignore param into an array:
1723      if( empty($ignore) )
1724      {
1725          $ignore = array();
1726      }
1727      elseif( !is_array($ignore) )
1728      {
1729          $ignore = explode( ',', $ignore );
1730      }
1731  
1732      // Construct array of all params that have been memorized:
1733      // (Note: we only include values if they differ from the default and they are not in the ignore list)
1734      $params = array();
1735      if( isset($global_param_list) ) foreach( $global_param_list as $var => $thisparam )
1736      {    // For each saved param...
1737          $type = $thisparam['type'];
1738          $defval = $thisparam['default'];
1739  
1740          // Check if the param should to be ignored:
1741          $skip = false;
1742          foreach( $ignore as $ignore_pattern )
1743          {
1744              if( $ignore_pattern[0] == '/' )
1745              { // regexp:
1746                  if( preg_match( $ignore_pattern, $var ) )
1747                  {    // Skip this param!
1748                      $skip = true;
1749                      break;
1750                  }
1751              }
1752              else
1753              {
1754                  if( $var == $ignore_pattern )
1755                  {    // Skip this param!
1756                      $skip = true;
1757                      break;
1758                  }
1759              }
1760          }
1761          if( $skip )
1762          { // we don't want to include that param
1763              // echo 'regenerate_url(): EXPLICIT IGNORE '.$var;
1764              // $Debuglog->add( 'regenerate_url(): EXPLICIT IGNORE '.$var, 'params' );
1765              continue;
1766          }
1767  
1768          $value = $GLOBALS[$var];
1769          if( $value != $defval )
1770          { // Value is not set to default value:
1771              // Note: sometimes we will want to include an empty value, especially blog=0 ! In that case we set the default for blog to -1.
1772              // echo "adding $var \n";
1773              // $Debuglog->add( "regenerate_url(): Using var=$var, type=$type, defval=[$defval], val=[$value]", 'params' );
1774              // echo "regenerate_url(): Using var=$var, type=$type, defval=[$defval], val=[$value]";
1775  
1776              $params[] = get_param_urlencoded($var, $value, $glue);
1777          }
1778          else // if( $var == 's' )
1779          {
1780              // $Debuglog->add( "regenerate_url(): DEFAULT ignore var=$var, type=$type, defval=[$defval], val=[$value]", 'params' );
1781              // echo "regenerate_url(): DEFAULT ignore var=$var, type=$type, defval=[$defval], val=[$value]";
1782          }
1783      }
1784  
1785      // Merge in the params we want to force to a specific value:
1786      if( !empty( $set ) )
1787      {    // We got some forced params:
1788          // Transform set param into an array:
1789          if( !is_array($set) )
1790          {
1791              if( $set[0] == '?' )
1792              { // Remove leading question mark, e.g. from QUERY_STRING
1793                  $set = substr($set, 1);
1794              }
1795              $set = preg_split( '~&(amp;)?~', $set, NULL, PREG_SPLIT_NO_EMPTY );
1796          }
1797          // Merge them in:
1798          $params = array_merge( $params, $set );
1799      }
1800  
1801      // Construct URL:
1802      if( ! empty($pagefileurl) )
1803      {
1804          $url = $pagefileurl;
1805      }
1806      else
1807      {
1808          if( ! empty($base_tag_set) )
1809          {
1810              if( isset($Debuglog) ) $Debuglog->add( 'regenerate_url(): Using full URL because of $base_tag_set.', 'params' );
1811              $url = $ReqHost.$ReqPath;
1812          }
1813          else
1814          {    // Use just absolute path, because there's no <base> tag used
1815              $url = $ReqPath;
1816          }
1817      }
1818  
1819      if( !empty( $params ) )
1820      {
1821          $url = url_add_param( $url, implode( $glue, $params ), $glue );
1822      }
1823      // if( isset($Debuglog) ) $Debuglog->add( 'regenerate_url(): ['.$url.']', 'params' );
1824      return $url;
1825  }
1826  
1827  
1828  /**
1829   * Get URL param, urlencoded.
1830   * This handles arrays, recursively.
1831   * @return string
1832   */
1833  function get_param_urlencoded($var, $value, $glue = '&amp;')
1834  {
1835      if( is_array($value) )
1836      { // there is a special formatting in case of arrays
1837          $r = array();
1838          $keep_keys = array_diff( array_keys($value), array_keys(array_values($value)) );
1839          foreach( $value as $key => $value )
1840          {
1841              $r[] = get_param_urlencoded($var.'['.($keep_keys ? $key : '').']', $value, $glue);
1842          }
1843          return implode($glue, $r);
1844      }
1845      else
1846      { // not an array : normal formatting
1847          return rawurlencode($var).'='.rawurlencode($value);
1848      }
1849  }
1850  
1851  
1852  /**
1853   * Checks if a given regular expression is valid.
1854   *
1855   * It changes the error_handler and restores it.
1856   *
1857   * @author plenque at hotmail dot com {@link http://php.net/manual/en/function.preg-match.php}
1858   * @param string the regular expression to test
1859   * @param boolean does the regular expression includes delimiters (and optionally modifiers)?
1860   * @return boolean
1861   */
1862  function is_regexp( $reg_exp, $includes_delim = false )
1863  {
1864      $sPREVIOUSHANDLER = set_error_handler( '_trapError' );
1865      if( ! $includes_delim )
1866      {
1867          $reg_exp = '#'.str_replace( '#', '\#', $reg_exp ).'#';
1868      }
1869      preg_match( $reg_exp, '' );
1870      restore_error_handler( $sPREVIOUSHANDLER );
1871  
1872      return !_traperror();
1873  }
1874  
1875  
1876  /**
1877   * Meant to replace error handler temporarily.
1878   *
1879   * @return integer number of errors
1880   */
1881  function _trapError( $reset = 1 )
1882  {
1883      static $iERRORES;
1884  
1885      if( !func_num_args() )
1886      {
1887          $iRETORNO = $iERRORES;
1888          $iERRORES = 0;
1889          return $iRETORNO;
1890      }
1891      else
1892      {
1893          $iERRORES++;
1894      }
1895  }
1896  
1897  
1898  /*
1899   * Clean up the mess PHP has created with its funky quoting everything!
1900   */
1901  if( get_magic_quotes_gpc() )
1902  { // That stupid PHP behaviour consisting of adding slashes everywhere is unfortunately on
1903  
1904      if( in_array( strtolower(ini_get('magic_quotes_sybase')), array('on', '1', 'true', 'yes') ) )
1905      { // overrides "magic_quotes_gpc" and only replaces single quotes with themselves ( "'" => "''" )
1906          /**
1907           * @ignore
1908           */
1909  		function remove_magic_quotes( $mixed )
1910          {
1911              if( is_array( $mixed ) )
1912              {
1913                  foreach($mixed as $k => $v)
1914                  {
1915                      $mixed[$k] = remove_magic_quotes( $v );
1916                  }
1917              }
1918              elseif( is_string($mixed) )
1919              {
1920                  // echo 'Removing slashes ';
1921                  $mixed = str_replace( '\'\'', '\'', $mixed );
1922              }
1923              return $mixed;
1924          }
1925      }
1926      else
1927      {
1928          /**
1929           * Remove quotes from input.
1930           * This handles magic_quotes_gpc and magic_quotes_sybase PHP settings/variants.
1931           *
1932           * NOTE: you should not use it directly, but one of the param-functions!
1933           *
1934           * @param mixed string or array (function is recursive)
1935           * @return mixed Value, with magic quotes removed
1936           */
1937  		function remove_magic_quotes( $mixed )
1938          {
1939              if( is_array( $mixed ) )
1940              {
1941                  foreach($mixed as $k => $v)
1942                  {
1943                      $mixed[$k] = remove_magic_quotes( $v );
1944                  }
1945              }
1946              elseif( is_string($mixed) )
1947              {
1948                  // echo 'Removing slashes ';
1949                  $mixed = stripslashes( $mixed );
1950              }
1951              return $mixed;
1952          }
1953      }
1954  }
1955  else
1956  {
1957      /**
1958       * @ignore
1959       */
1960  	function remove_magic_quotes( $mixed )
1961      {
1962          return $mixed;
1963      }
1964  }
1965  
1966  
1967  
1968  
1969  
1970  /**
1971   * Sets an HTML parameter and checks for sanitized code.
1972   *
1973   * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
1974   * It only checks on it and produces error messages.
1975   * It is NOT (necessarily) safe to use the output.
1976   *
1977   * @todo dh> Not implemented?!
1978   *
1979   * @param string Variable to set
1980   * @param mixed Default value or TRUE if user input required
1981   * @param boolean memorize ( see {@link param()} )
1982   * @param string error message
1983   *
1984   * @return string
1985   */
1986  function param_html( $var, $default = '', $memorize = false, $err_msg )
1987  {
1988  
1989  }
1990  
1991  
1992  /**
1993   * Checks for sanitized code.
1994   *
1995   * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
1996   * It only checks on it and produces error messages.
1997   * It is NOT (necessarily) safe to use the output.
1998   *
1999   * @param string param name
2000   * @param string error message
2001   * @return boolean|string
2002   */
2003  function param_check_html( $var, $err_msg = '#', $field_err_msg = '#' )
2004  {
2005      global $Messages, $evo_charset;
2006  
2007      // The param was already converted to the $evo_charset in the param() function, we need to use that charset!
2008      $altered_html = check_html_sanity( $GLOBALS[$var], 'posting', NULL, $evo_charset );
2009  
2010       if( $altered_html === false )
2011      {    // We have errors, do not keep sanitization attemps:
2012          if( $err_msg == '#' )
2013          {
2014              $err_msg = T_('Invalid XHTML.');
2015          }
2016          if( $field_err_msg == '#' )
2017          {
2018              $field_err_msg = T_('Invalid XHTML.');
2019          }
2020  
2021          param_error( $var, $err_msg, $field_err_msg );
2022          return false;
2023      }
2024  
2025      // Keep the altered HTML (balanced tags, etc.) - NOT necessarily safe if loose checking has been allowed.
2026      $GLOBALS[$var] = $altered_html;
2027  
2028      return $altered_html;
2029  }
2030  
2031  
2032  /**
2033   * @param string param name
2034   * @param boolean required
2035   * @return booelan true if OK
2036   */
2037  function param_check_gender( $var, $required = false )
2038  {
2039      if( empty( $GLOBALS[$var] ) )
2040      {    // empty is OK if not required:
2041          global $current_User;
2042          if( $required )
2043          {
2044              if( $current_User->check_perm( 'users', 'edit' ) )
2045              {    // Display a note message if user can edit all users
2046                  param_add_message_to_Log( $var, T_('Please select a gender.'), 'note' );
2047                  return true;
2048              }
2049              else
2050              {    // Display an error message
2051                  param_error( $var, T_( 'Please select a gender.' ) );
2052                  return false;
2053              }
2054          }
2055          return true;
2056      }
2057  
2058      $gender_value = $GLOBALS[$var];
2059      if( ( $gender_value != 'M' ) && ( $gender_value != 'F' ) )
2060      {
2061          param_error( $var, 'Gender value is invalid' );
2062          return false;
2063      }
2064  
2065      return true;
2066  }
2067  
2068  
2069  /**
2070   * Check html sanity of the not numeric and not object content in a general 'array' param. Handles the array recursivley.
2071   *
2072   * @param array the value of an 'array' type param
2073   * @return array the validated/modified content
2074   */
2075  function param_check_general_array( $param_value )
2076  {
2077      if( is_array( $param_value ) )
2078      { // This param is an array, validate all of the elements
2079          foreach( $param_value as $i => $var_value )
2080          { // Check array content recursively
2081              $param_value[$i] = param_check_general_array( $var_value );
2082          }
2083          return $param_value;
2084      }
2085  
2086      if( ! ( is_numeric( $param_value ) || is_object( $param_value ) ) )
2087      { // This value is not numeric and not an object, we consider it as string
2088          // Check the content html sanity
2089          $param_value = check_html_sanity( $param_value, 'general_array_params' );
2090      }
2091  
2092      return $param_value;
2093  }
2094  
2095  
2096  /**
2097   * DEPRECATED Stub for plugin compatibility:
2098   */
2099  function format_to_post( $content, $is_comment = 0, $encoding = NULL )
2100  {
2101      global $current_User;
2102  
2103      $ret = check_html_sanity( $content, ( $is_comment ? 'commenting' : 'posting' ), $current_User, $encoding );
2104      if( $ret === false )
2105      {    // ERROR
2106          return $content;
2107      }
2108  
2109      // return altered content
2110      return $ret;
2111  }
2112  
2113  
2114  /**
2115   * Check raw HTML input for different levels of sanity including:
2116   * - XHTML validation
2117   * - Javascript injection
2118   * - antispam
2119   *
2120   * Also cleans up the content on some levels:
2121   * - trimming
2122   * - balancing tags
2123   *
2124   * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
2125   * It only checks on it and produces error messages.
2126   * It is NOT (necessarily) safe to use the output.
2127   *
2128   * @param string The content to format
2129   * @param string
2130   * @param User User (used for "posting" and "xmlrpc_posting" context). Default: $current_User
2131   * @param string Encoding (used for XHTML_Validator only!); defaults to $io_charset
2132   * @return boolean|string
2133   */
2134  function check_html_sanity( $content, $context = 'posting', $User = NULL, $encoding = NULL )
2135  {
2136      global $use_balanceTags, $admin_url;
2137      global $io_charset, $use_xhtmlvalidation_for_comments, $comment_allowed_tags, $comments_allow_css_tweaks;
2138      global $Messages;
2139  
2140      if( empty($User) )
2141      {
2142          /**
2143           * @var User
2144           */
2145          global $current_User;
2146          $User = $current_User;
2147      }
2148  
2149      // Add error messages
2150      $verbose = true;
2151  
2152      switch( $context )
2153      {
2154          case 'posting':
2155          case 'xmlrpc_posting':
2156              $Group = $User->get_Group();
2157              if( $context == 'posting' )
2158              {
2159                  $xhtmlvalidation  = ($Group->perm_xhtmlvalidation == 'always');
2160              }
2161              else
2162              {
2163                  $xhtmlvalidation  = ($Group->perm_xhtmlvalidation_xmlrpc == 'always');
2164              }
2165              $allow_css_tweaks = $Group->perm_xhtml_css_tweaks;
2166              $allow_javascript = $Group->perm_xhtml_javascript;
2167              $allow_iframes    = $Group->perm_xhtml_iframes;
2168              $allow_objects    = $Group->perm_xhtml_objects;
2169              $bypass_antispam  = $Group->perm_bypass_antispam;
2170              break;
2171  
2172          case 'commenting':
2173              $xhtmlvalidation  = $use_xhtmlvalidation_for_comments;
2174              $allow_css_tweaks = $comments_allow_css_tweaks;
2175              $allow_javascript = false;
2176              $allow_iframes    = false;
2177              $allow_objects    = false;
2178              // fp> I don't know if it makes sense to bypass antispam in commenting context if the user has that kind of permissions.
2179              // If so, then we also need to bypass in several other places.
2180              $bypass_antispam  = false;
2181              break;
2182  
2183          case 'general_array_params':
2184              $xhtmlvalidation  = false;
2185              $allow_css_tweaks = true;
2186              $allow_javascript = false;
2187              $allow_iframes    = false;
2188              $allow_objects    = false;
2189              $bypass_antispam  = false;
2190              // Do not add error messages in this context
2191              $verbose = false;
2192              break;
2193  
2194          default:
2195              debug_die( 'unknown context: '.$context );
2196      }
2197  
2198      $error = false;
2199  
2200      // Replace any & that is not a character or entity reference with &amp;
2201      $content = preg_replace( '/&(?!#[0-9]+;|#x[0-9a-fA-F]+;|[a-zA-Z_:][a-zA-Z0-9._:-]*;)/', '&amp;', $content );
2202  
2203      // ANTISPAM check:
2204      $error = ( ( ! $bypass_antispam ) && ( $block = antispam_check($content) ) );
2205      if( $error && $verbose )
2206      { // Add error message
2207          if( $context == 'xmlrpc_posting' )
2208          {
2209              $errmsg = ($context == 'commenting')
2210                  ? T_('Illegal content found (spam?)')
2211                  : sprintf( T_('Illegal content found: blacklisted word "%s".'), $block );
2212          }
2213          else
2214          {
2215              $errmsg = ($context == 'commenting')
2216                  ? T_('Illegal content found (spam?).')
2217                  : sprintf( T_('Illegal content found: blacklisted word &laquo;%s&raquo;.'), htmlspecialchars($block) );
2218          }
2219  
2220          $Messages->add(    $errmsg, 'error' );
2221      }
2222  
2223      $content = trim( $content );
2224  
2225      if( $use_balanceTags )
2226      { // Auto close open tags:
2227          $content = balance_tags( $content );
2228      }
2229  
2230      if( $xhtmlvalidation )
2231      { // We want to validate XHTML:
2232          load_class( 'xhtml_validator/_xhtml_validator.class.php', 'XHTML_Validator' );
2233  
2234          $XHTML_Validator = new XHTML_Validator( $context, $allow_css_tweaks, $allow_iframes, $allow_javascript, $allow_objects, $encoding );
2235  
2236          if( ! $XHTML_Validator->check( $content ) ) // TODO: see if we need to use convert_chars( $content, 'html' )
2237          {
2238              $error = true;
2239          }
2240      }
2241      else
2242      {    // We do not WANT to validate XHTML, fall back to basic security checking:
2243          // This is only as strong as its regexps can parse xhtml. This is significantly inferior to the XHTML checker above.
2244          // The only advantage of this checker is that it can check for a little security without requiring VALID XHTML.
2245  
2246          if( $context == 'commenting' )
2247          {    // DEPRECATED but still...
2248              // echo 'allowed tags:',htmlspecialchars($comment_allowed_tags);
2249              $content = strip_tags( $content, $comment_allowed_tags );
2250          }
2251  
2252          // Security checking:
2253          $check = $content;
2254          // Open comments or '<![CDATA[' are dangerous
2255          $check = str_replace('<!', '<', $check);
2256          // # # are delimiters
2257          // i modifier at the end means caseless
2258  
2259          // CHECK Styling restictions:
2260          $css_tweaks_error = ( ( ! $allow_css_tweaks ) && preg_match( '#\s((style|class|id)\s*=)#i', $check, $matches ) );
2261          if( $css_tweaks_error && $verbose )
2262          {
2263              $Messages->add( T_('Illegal CSS markup found: ').htmlspecialchars($matches[1]), 'error' );
2264          }
2265  
2266          // CHECK JAVASCRIPT:
2267          $javascript_error = ( ( ! $allow_javascript )
2268              && ( preg_match( '~( < \s* //? \s* (script|noscript) )~xi', $check, $matches )
2269                  || preg_match( '#\s((on[a-z]+)\s*=)#i', $check, $matches )
2270                  // action=, background=, cite=, classid=, codebase=, data=, href=, longdesc=, profile=, src=, usemap=
2271                  || preg_match( '#=["\'\s]*((javascript|vbscript|about):)#i', $check, $matches ) ) );
2272          if( $javascript_error && $verbose )
2273          {
2274              $Messages->add( T_('Illegal javascript markup found: ').htmlspecialchars($matches[1]), 'error' );
2275          }
2276  
2277          // CHECK IFRAMES:
2278          $iframe_error = ( ( ! $allow_iframes ) && preg_match( '~( < \s* //? \s* (frame|iframe) )~xi', $check, $matches ) );
2279          if( $iframe_error && $verbose )
2280          {
2281              $Messages->add( T_('Illegal frame markup found: ').htmlspecialchars($matches[1]), 'error' );
2282          }
2283  
2284          // CHECK OBJECTS:
2285          $object_error = ( ( ! $allow_objects ) && preg_match( '~( < \s* //? \s* (applet|object|param|embed) )~xi', $check, $matches ) );
2286          if( $object_error && $verbose )
2287          {
2288              $Messages->add( T_('Illegal object markup found: ').htmlspecialchars($matches[1]), 'error' );
2289          }
2290  
2291          // Set the final error value based on all of the results
2292          $error = $error || $css_tweaks_error || $javascript_error || $iframe_error || $object_error;
2293      }
2294  
2295      if( $error )
2296      {
2297          if( $verbose && ( ! empty( $User ) )
2298              && ( ! empty( $Group ) ) // This one will basically prevent this case from happening when commenting
2299              && $User->check_perm( 'users', 'edit', false ) )
2300          {
2301              $Messages->add( sprintf( T_('(Note: To get rid of the above validation warnings, you can deactivate unwanted validation rules in your <a %s>Group settings</a>.)'),
2302                                          'href="'.$admin_url.'?ctrl=groups&amp;grp_ID='.$Group->ID.'"' ), 'error' );
2303          }
2304          return false;
2305      }
2306  
2307      // Return sanitized content
2308      return $content;
2309  }
2310  
2311  
2312  /**
2313   * Balances Tags of string using a modified stack.
2314   *
2315   * @param string HTML to be balanced
2316   * @return string Balanced HTML
2317   */
2318  function balance_tags( $text )
2319  {
2320      $tagstack = array();
2321      $stacksize = 0;
2322      $tagqueue = '';
2323      $newtext = '';
2324  
2325      # b2 bug fix for comments - in case you REALLY meant to type '< !--'
2326      // $text = str_replace('< !--', '<    !--', $text);
2327  
2328      // escape any < that does not look like a tag, i-e: that is not followed by a letter like in <a> or a / like in </a>:
2329      // (also not escaping comments like <!-- )
2330      $text = preg_replace('#<([^a-z/!]{1})#i', '&lt;$1', $text);
2331  
2332      while( preg_match('~<(\s*/?\w+)\s*(.*?)/?>~s', $text, $regex) )
2333      {
2334          $newtext = $newtext . $tagqueue;
2335  
2336          $i = strpos($text,$regex[0]);
2337          $l = strlen($tagqueue) + strlen($regex[0]);
2338  
2339          // clear the shifter
2340          $tagqueue = '';
2341  
2342          // Pop or Push
2343          if( substr($regex[1],0,1) == '/' )
2344          { // End Tag
2345              $tag = strtolower(substr($regex[1],1));
2346  
2347              // if too many closing tags
2348              if($stacksize <= 0)
2349              {
2350                  $tag = '';
2351                  //or close to be safe $tag = '/' . $tag;
2352              }
2353              // if stacktop value = tag close value then pop
2354              else if ($tagstack[$stacksize - 1] == $tag)
2355              { // found closing tag
2356                  $tag = '</'.$tag.'>'; // Close Tag
2357                  // Pop
2358                  array_pop ($tagstack);
2359                  $stacksize--;
2360              } else { // closing tag not at top, search for it
2361                  for ($j=$stacksize-1;$j>=0;$j--) {
2362                      if ($tagstack[$j] == $tag) {
2363                      // add tag to tagqueue
2364                          for ($k=$stacksize-1;$k>=$j;$k--){
2365                              $tagqueue .= '</' . array_pop ($tagstack) . '>';
2366                              $stacksize--;
2367                          }
2368                          break;
2369                      }
2370                  }
2371                  $tag = '';
2372              }
2373          }
2374          else
2375          { // Begin Tag
2376              $tag = strtolower($regex[1]);
2377  
2378              // Tag Cleaning
2379  
2380              // Push if not img, br, hr, param or input
2381              if($tag != 'br' && $tag != 'img' && $tag != 'hr' && $tag != 'param' && $tag != 'input')
2382              {
2383                  $stacksize = array_push ($tagstack, $tag);
2384                  $closing = '>';
2385              }
2386              else
2387              {
2388                  $closing = ' />';
2389              }
2390              // Attributes
2391              // $attributes = $regex[2];
2392              $attributes = $regex[2];
2393              if($attributes)
2394              {
2395                  $attributes = ' '.trim($attributes);
2396              }
2397  
2398              $tag = '<'.$tag.$attributes.$closing;
2399          }
2400  
2401          $newtext .= substr($text,0,$i) . $tag;
2402          $text = substr($text,$i+$l);
2403      }
2404  
2405      // Clear Tag Queue
2406      $newtext = $newtext . $tagqueue;
2407  
2408      // Add Remaining text
2409      $newtext .= $text;
2410  
2411      // Empty Stack
2412      while($x = array_pop($tagstack)) {
2413          $newtext = $newtext . '</' . $x . '>'; // Add remaining tags to close
2414      }
2415  
2416      # b2 fix for the bug with HTML comments
2417      // $newtext = str_replace( '< !--', '<'.'!--', $newtext ); // the concatenation is needed to work around some strange parse error in PHP 4.3.1
2418      // $newtext = str_replace( '<    !--', '< !--', $newtext );
2419  
2420      return $newtext;
2421  }
2422  
2423  
2424  /**
2425   * Check if a parameter is set or not
2426   *
2427   * Used to decide, if a numeric parameter value is NULL because it isn't set, or because it's set to NULL
2428   *
2429   * @param string parameter name
2430   * @return boolean true if parameter is set
2431   */
2432  function isset_param( $var )
2433  {
2434      return isset($_POST[$var]) || isset($_GET[$var]);
2435  }
2436  
2437  ?>

title

Description

title

Description

title

Description

title

title

Body