phpDocumentor pond
[ class tree: pond ] [ index: pond ] [ all elements ]

Source for file _param.funcs.php

Documentation is available at _param.funcs.php

  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 Quam Plures - {@link http://quamplures.net/}
  14.  * See also {@link https://launchpad.net/quam-plures}.
  15.  *
  16.  * @copyright (c) 2009 - 2011 by the Quam Plures developers - {@link http://quamplures.net/}
  17.  * @copyright (c)2003-2009 by Francois PLANQUE - {@link http://fplanque.net/}
  18.  *  Parts of this file are copyright (c)2004-2006 by Daniel HAHLER - {@link http://thequod.de/contact}.
  19.  *  Parts of this file are copyright (c)2005-2006 by PROGIDISTRI - {@link http://progidistri.com/}.
  20.  *
  21.  *  {@internal License choice
  22.  *  - If you have received this file as part of a package, please find the license.txt file in
  23.  *    the same folder or the closest folder above for complete license terms.
  24.  *  - If you have received this file individually (e-g: from http://evocms.cvs.sourceforge.net/)
  25.  *    then you must choose one of the following licenses before using the file:
  26.  *    - GNU General Public License 2 (GPL) - http://www.opensource.org/licenses/gpl-license.php
  27.  *    - Mozilla Public License 1.1 (MPL) - http://www.opensource.org/licenses/mozilla1.1.php
  28.  *  }}}
  29.  *
  30.  *  {@internal Open Source relicensing agreement:
  31.  *  Daniel HAHLER grants Francois PLANQUE the right to license
  32.  *  Daniel HAHLER's contributions to this file and the b2evolution project
  33.  *  under any OSI approved OSS license (http://www.opensource.org/licenses/).
  34.  *  }}}
  35.  *
  36.  * @author cafelog (team)
  37.  * @author blueyed: Daniel HAHLER.
  38.  * @author fplanque: Francois PLANQUE.
  39.  *
  40.  * @package pond
  41.  */
  42. if!defined('QP_MAIN_INIT') ) die'Please, do not access this page directly.' );
  43.  
  44.  
  45. /**
  46.  * Sets a parameter with values from the request or to provided default,
  47.  * except if param is already set!
  48.  *
  49.  * Also removes magic quotes if they are set automatically by PHP.
  50.  * Also forces type.
  51.  * Priority order: POST, GET, COOKIE, DEFAULT.
  52.  *
  53.  * @todo when bad_request_die() gets called, the GLOBAL should not be left set to the invalid value!
  54.  *  fp> Why? if the process dies anyway
  55.  *
  56.  * @param string Variable to set
  57.  * @param string Force value type to one of:
  58.  *  - integer
  59.  *  - float, double
  60.  *  - string (strips (HTML-)Tags, trims whitespace)
  61.  *  - array    (TODO:  array/integer  , array/array/string )
  62.  *  - html (does nothing)
  63.  *  - '' (does nothing)
  64.  *  - '/^...$/' check regexp pattern match (string)
  65.  *  - 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).
  66.  *  Value type will be forced only if resulting value (probably from default then) is !== NULL
  67.  * @param mixed Default value or TRUE if user input required
  68.  * @param boolean Do we need to memorize this to regenerate the URL for this page?
  69.  * @param boolean Override if variable already set
  70.  * @param boolean Force setting of variable to default if no param is sent and var wasn't set before
  71.  * @param mixed true will refuse illegal values,
  72.  *               false will try to convert illegal to legal values,
  73.  *               '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)
  74.  * @return mixed Final value of Variable, or false if we don't force setting and did not set
  75.  */
  76. function param$var$type ''$default ''$memorize false,
  77.                                 $override false$use_default true$strict_typing 'allow_empty' )
  78. {
  79.     global $Debuglog$debug$evo_charset$io_charset;
  80.     // 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", ..)!
  81.  
  82.     /*
  83.      * STEP 1 : Set the variable
  84.      *
  85.      * Check if already set
  86.      * WARNING: when PHP register globals is ON, COOKIES get priority over GET and POST with this!!!
  87.      *   dh> I never understood that comment.. does it refer to "variables_order" php.ini setting?
  88.      *        fp> I guess
  89.      */
  90.     ifisset$GLOBALS[$var|| $override )
  91.     {
  92.         ifisset($_POST[$var]) )
  93.         {
  94.             $GLOBALS[$varremove_magic_quotes$_POST[$var);
  95.             // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by POST', 'params' );
  96.         }
  97.         elseifisset($_GET[$var]) )
  98.         {
  99.             $GLOBALS[$varremove_magic_quotes($_GET[$var]);
  100.             // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by GET', 'params' );
  101.         }
  102.         elseifisset($_COOKIE[$var]))
  103.         {
  104.             $GLOBALS[$varremove_magic_quotes($_COOKIE[$var]);
  105.             // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by COOKIE', 'params' );
  106.         }
  107.         elseif$default === true )
  108.         {
  109.             bad_request_diesprintfT_('Parameter &laquo;%s&raquo; is required!')$var ) );
  110.         }
  111.         elseif$use_default )
  112.         {    // We haven't set any value yet and we really want one: use default:
  113.             if$type == 'array' && $default === '' )
  114.             // Change default '' into array() (otherwise there would be a notice with settype() below)
  115.                 $default array();
  116.             }
  117.             $GLOBALS[$var$default;
  118.             // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.'='.$GLOBALS[$var].' set by default', 'params' );
  119.         }
  120.         else
  121.         // param not found! don't set the variable.
  122.             // Won't be memorized nor type-forced!
  123.             return false;
  124.         }
  125.     }
  126.     else
  127.     // Variable was already set but we need to remove the auto quotes
  128.         $GLOBALS[$varremove_magic_quotes($GLOBALS[$var]);
  129.  
  130.         // if( isset($Debuglog) ) $Debuglog->add( 'param(-): '.$var.' already set to ['.var_export($GLOBALS[$var], true).']!', 'params' );
  131.     }
  132.  
  133.     ifisset($io_charset&& empty($evo_charset) )
  134.     {
  135.         $GLOBALS[$varconvert_charset$GLOBALS[$var]$evo_charset$io_charset );
  136.     }
  137.  
  138.     /*
  139.      * STEP 2: make sure the data fits the expected type
  140.      *
  141.      * type will be forced even if it was set before and not overriden
  142.      */
  143.     if!empty($type&& $GLOBALS[$var!== NULL )
  144.     // Force the type
  145.         switch$type )
  146.         {
  147.             case 'html':
  148.                 // do nothing
  149.                 ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> as RAW Unsecure HTML''params' );
  150.                 break;
  151.  
  152.             case 'string':
  153.                 // strip out any html:
  154.                 ifis_scalar($GLOBALS[$var]) )
  155.                 // This happens if someone uses "foo[]=x" where "foo" is expected as string
  156.                     // TODO: dh> debug_die() instead?
  157.                     $GLOBALS[$var'';
  158.                     $Debuglog->add'param(-): <strong>'.$var.'</strong> is not scalar!''params' );
  159.                 }
  160.                 else
  161.                 {
  162.                     $GLOBALS[$vartrimstrip_tags($GLOBALS[$var]) );
  163.                     // Make sure the string is a single line
  164.                     // TODO: dh> this breaks e.g. multi-line widget params (e.g. "Custom TinyMCE init")
  165.                     //           While this example works without newlines, there are probably places
  166.                     //           where it's more important.
  167.                     //       fp> Someone was hiding text. Can't remember exactly where.
  168.                     //           Make a "multiline-string" where appropriate.
  169.                     $GLOBALS[$varpreg_replace'\r|\n'''$GLOBALS[$var);
  170.                 }
  171.                 $Debuglog->add'param(-): <strong>'.$var.'</strong> as string''params' );
  172.                 break;
  173.  
  174.             default:
  175.                 ifsubstr$type0== '/' )
  176.                 {    // We want to match against a REGEXP:
  177.                     ifpreg_match$type$GLOBALS[$var) )
  178.                     {    // Okay, match
  179.                         ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> matched against '.$type'params' );
  180.                     }
  181.                     elseif$strict_typing == 'allow_empty' && empty($GLOBALS[$var]) )
  182.                     {    // No match but we accept empty value:
  183.                         ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> is empty: ok''params' );
  184.                     }
  185.                     elseif$strict_typing )
  186.                     {    // We cannot accept this MISMATCH:
  187.                         bad_request_diesprintfT_('Illegal value received for parameter &laquo;%s&raquo;!')$var ) );
  188.                     }
  189.                     else
  190.                     // Fall back to default:
  191.                         $GLOBALS[$var$default;
  192.                         ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> DID NOT match '.$type.' set to default value='.$GLOBALS[$var]'params' );
  193.                     }
  194.  
  195.                     // From now on, consider this as a string: (we need this when memorizing)
  196.                     $type 'string';
  197.                 }
  198.                 elseif$GLOBALS[$var=== '' )
  199.                 // Special handling of empty values.
  200.                     if$strict_typing === false && $use_default )
  201.                     {    // ADDED BY FP 2006-07-06
  202.                         // We want to consider empty values as invalid and fall back to the default value:
  203.                         $GLOBALS[$var$default;
  204.                     }
  205.                     else
  206.                     {    // We memorize the empty value as NULL:
  207.                         // fplanque> note: there might be side effects to this, but we need
  208.                         // this to distinguish between 0 and 'no input'
  209.                         // Note: we do this after regexps because we may or may not want to allow empty strings in regexps
  210.                         $GLOBALS[$varNULL;
  211.                         ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> set to NULL''params' );
  212.                     }
  213.                 }
  214.                 elseif$GLOBALS[$var=== array() )
  215.                 {
  216.                     if$strict_typing === false && $use_default )
  217.                     {    // ADDED BY FP 2006-09-07
  218.                         // We want to consider empty values as invalid and fall back to the default value:
  219.                         $GLOBALS[$var$default;
  220.                     }
  221.                 }
  222.                 // 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!)
  223.                 else
  224.                 {
  225.                     if$strict_typing )
  226.                     {    // We want to make sure the value is valid:
  227.                         $regexp '';
  228.                         switch$type )
  229.                         {
  230.                             case 'boolean':
  231.                                 $regexp '/^(0|1|false|true)$/i';
  232.                                 break;
  233.  
  234.                             case 'integer':
  235.                                 $regexp '/^(\+|-)?[0-9]+$/';
  236.                                 break;
  237.  
  238.                             case 'float':
  239.                             case 'double':
  240.                                 $regexp '/^(\+|-)?[0-9]+(.[0-9]+)?$/';
  241.                                 break;
  242.  
  243.                             // Note: other types are not tested here.
  244.                         }
  245.                         if$strict_typing == 'allow_empty' && empty($GLOBALS[$var]) )
  246.                         {    // We have an empty value and we accept it
  247.                             // ok..
  248.                         }
  249.                         elseif!empty$regexp && !is_scalar($GLOBALS[$var]|| !preg_match$regexp$GLOBALS[$var) ) )
  250.                         {    // Value does not match!
  251.                             bad_request_diesprintfT_('Illegal value received for parameter &laquo;%s&raquo;!')$var ) );
  252.                         }
  253.                     }
  254.  
  255.                     // Change the variable type:
  256.                     settype$GLOBALS[$var]$type );
  257.                     ifisset($Debuglog) ) $Debuglog->add'param(-): <strong>'.$var.'</strong> typed to '.$type.', new value='.$GLOBALS[$var]'params' );
  258.                 }
  259.         }
  260.     }
  261.  
  262.  
  263.     /*
  264.      * STEP 3: memorize the value for later url regeneration
  265.      */
  266.     if$memorize )
  267.     // Memorize this parameter
  268.         memorize_param$var$type$default );
  269.     }
  270.  
  271.     return $GLOBALS[$var];
  272. }
  273.  
  274.  
  275. /**
  276.  * Get the param from an array param's first index instead of the value.
  277.  *
  278.  * E.g., for "param[value]" as a submit button you can get the value with
  279.  *       <code>Request::param_arrayindex( 'param' )</code>.
  280.  *
  281.  * @see param_action()
  282.  * @param string Param name
  283.  * @param mixed Default to use
  284.  * @return string 
  285.  */
  286. function param_arrayindex$param_name$default '' )
  287. {
  288.     $array array_keysparam$param_name'array'array() ) );
  289.     $value array_pop$array );
  290.     ifis_string($value) )
  291.     {
  292.         $value substrstrip_tags($value)050 );  // sanitize it
  293.     }
  294.     elseif!empty($value) )
  295.     // this is probably a numeric index from '<input name="array[]" .. />'
  296.         debug_die'Invalid array param!' );
  297.     }
  298.     else
  299.     {
  300.         $value $default;
  301.     }
  302.  
  303.     return $value;
  304. }
  305.  
  306.  
  307. /**
  308.  * Get the action from params.
  309.  *
  310.  * If we got no "action" param, we'll check for an "actionArray" param
  311.  * ( <input type="submit" name="actionArray[real_action]" ...> ).
  312.  * And the real $action will be found in the first key...
  313.  * When there are multiple submit buttons, this is smarter than checking the value which is a translated string.
  314.  * 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).
  315.  *
  316.  * @param mixed Default to use.
  317.  * @return string 
  318.  */
  319. function param_action$default ''$memorize false )
  320. {
  321.     $action param'action''string'NULL$memorize );
  322.  
  323.     ifis_null($action) )
  324.     // Check $actionArray
  325.         $action param_arrayindex'actionArray'$default );
  326.  
  327.         set_param'action'$action )// always set "action"
  328.     }
  329.  
  330.     return $action;
  331. }
  332.  
  333.  
  334. /**
  335.  * Get a param from cookie.
  336.  *
  337.  * {@internal This is just a wrapper around {@link param()} which unsets and
  338.  *  restores GET and POST. IMHO this is less hackish, at least performance
  339.  *  wise then using a $sources param for param()}}}
  340.  *
  341.  * @uses param()
  342.  * @see param()
  343.  */
  344. function param_cookie($var$type ''$default ''$memorize false,
  345.         $override false$use_default true$strict_typing 'allow_empty')
  346. {
  347.     $save_GET $_GET;
  348.     $save_POST $_POST;
  349.  
  350.     unset$_GET$_POST );
  351.  
  352.     $r param$var$type$default$memorize$override$use_default$strict_typing );
  353.  
  354.     $_GET $save_GET;
  355.     $_POST $save_POST;
  356.  
  357.     return $r;
  358. }
  359.  
  360.  
  361. /**
  362.  * @param string param name
  363.  * @param string error message
  364.  * @param string|NULLerror message for form field ($err_msg gets used if === NULL).
  365.  * @return boolean true if OK
  366.  */
  367. function param_string_not_empty$var$err_msg$field_err_msg NULL )
  368. {
  369.     param$var'string'true );
  370.     return param_check_not_empty$var$err_msg$field_err_msg );
  371. }
  372.  
  373.  
  374. /**
  375.  * @param string param name
  376.  * @param string error message
  377.  * @param string|NULLerror message for form field ($err_msg gets used if === NULL).
  378.  * @return boolean true if OK
  379.  */
  380. function param_check_not_empty$var$err_msg$field_err_msg NULL )
  381. {
  382.     ifempty$GLOBALS[$var) )
  383.     {
  384.         param_error$var$err_msg$field_err_msg );
  385.         return false;
  386.     }
  387.     return true;
  388. }
  389.  
  390.  
  391. /**
  392.  * Checks if the param is an integer (no float, e.g. 3.14).
  393.  *
  394.  * @param string param name
  395.  * @param string error message
  396.  * @return boolean true if OK
  397.  */
  398. function param_check_number$var$err_msg$required false )
  399. {
  400.     ifempty$GLOBALS[$var&& $required )
  401.     // empty is OK:
  402.         return true;
  403.     }
  404.  
  405.     ifpreg_match'#^[0-9]+$#'$GLOBALS[$var) )
  406.     {
  407.         param_error$var$err_msg );
  408.         return false;
  409.     }
  410.     return true;
  411. }
  412.  
  413.  
  414. /**
  415.  * Checks if the param is a decimal number
  416.  *
  417.  * @param string param name
  418.  * @param string error message
  419.  * @return boolean true if OK
  420.  */
  421. function param_check_decimal$var$err_msg$required false )
  422. {
  423.     ifempty$GLOBALS[$var&& $required )
  424.     // empty is OK:
  425.         return true;
  426.     }
  427.  
  428.     ifpreg_match'#^[0-9]*(\.[0-9]+)?$#'$GLOBALS[$var) )
  429.     {
  430.         param_error$var$err_msg );
  431.         return false;
  432.     }
  433.     return true;
  434. }
  435.  
  436.  
  437. /**
  438.  * Gets a param and makes sure it's a decimal number (no float, e.g. 3.14) in a given range.
  439.  *
  440.  * @param string param name
  441.  * @param integer min value
  442.  * @param integer max value
  443.  * @param string error message (gets printf'ed with $min and $max)
  444.  * @return boolean true if OK
  445.  */
  446. function param_integer_range$var$min$max$err_msg$required true )
  447. {
  448.     param$var'integer'$required true '' );
  449.     return param_check_range$var$min$max$err_msg$required );
  450. }
  451.  
  452.  
  453. /**
  454.  * Checks if the param is a decimal number (no float, e.g. 3.14) in a given range.
  455.  *
  456.  * @param string param name
  457.  * @param integer min value
  458.  * @param integer max value
  459.  * @param string error message (gets printf'ed with $min and $max)
  460.  * @param boolean Is the param required?
  461.  * @return boolean true if OK
  462.  */
  463. function param_check_range$var$min$max$err_msg$required true )
  464. {
  465.     ifempty$GLOBALS[$var&& $required )
  466.     // empty is OK:
  467.         return true;
  468.     }
  469.  
  470.     ifpreg_match'~^[-+]?\d+$~'$GLOBALS[$var|| $GLOBALS[$var$min || $GLOBALS[$var$max )
  471.     {
  472.         param_error$varsprintf$err_msg$min$max ) );
  473.         return false;
  474.     }
  475.     return true;
  476. }
  477.  
  478.  
  479. /**
  480.  * @param string param name
  481.  * @return boolean true if OK
  482.  */
  483. function param_check_email$var$required false )
  484. {
  485.     ifempty$GLOBALS[$var&& $required )
  486.     // empty is OK:
  487.         return true;
  488.     }
  489.  
  490.     if!is_email$GLOBALS[$var) )
  491.     {
  492.         param_error$varT_('The email address is invalid.') );
  493.         return false;
  494.     }
  495.     return true;
  496. }
  497.  
  498.  
  499. /**
  500.  * @param string param name
  501.  * @param string 
  502.  * @return boolean true if OK
  503.  */
  504. function param_check_url$var$context$field_err_msg NULL )
  505. {
  506.   /**
  507.      * @var User
  508.      */
  509.     global $current_User;
  510.  
  511.     $Group $current_User->get_Group();
  512.  
  513.     if$error_detail validate_url$GLOBALS[$var]$context$Group->perm_bypass_antispam ) )
  514.     {
  515.         param_error$varsprintfT_('Supplied URL is invalid. (%s)')$error_detail )$field_err_msg );
  516.         return false;
  517.     }
  518.     return true;
  519. }
  520.  
  521.  
  522. /**
  523.  * Check if the value is a file name
  524.  *
  525.  * @param string param name
  526.  * @param string error message
  527.  * @return boolean true if OK
  528.  */
  529. function param_check_filename$var$err_msg )
  530. {
  531.     if$error_filename validate_filename$GLOBALS[$var) )
  532.     {
  533.         param_error$var$error_filename );
  534.         return false;
  535.     }
  536.     return true;
  537. }
  538.  
  539.  
  540. /**
  541.  * Check if the value of a param is a regular expression (syntax).
  542.  *
  543.  * @param string param name
  544.  * @param string error message
  545.  * @param string|NULLerror message for form field ($err_msg gets used if === NULL).
  546.  * @return boolean true if OK
  547.  */
  548. function param_check_isregexp$var$err_msg$field_err_msg NULL )
  549. {
  550.     ifis_regexp$GLOBALS[$var) )
  551.     {
  552.         param_error$var$field_err_msg );
  553.         return false;
  554.     }
  555.     return true;
  556. }
  557.  
  558.  
  559. /**
  560.  * Sets a date parameter by converting locale date (if valid) to ISO date.
  561.  *
  562.  * If the date is not valid, it is set to the param unchanged (unconverted).
  563.  *
  564.  * @param string param name
  565.  * @param string error message
  566.  * @param boolean Is a non-empty date required?
  567.  * @param string Default (in the format of $date_format)
  568.  * @param string date format (php format), defaults to {@link locale_datefmt()}
  569.  */
  570. function param_date$var$err_msg$required$default ''$date_format NULL )
  571. {
  572.     param$var'string'$default );
  573.  
  574.     $iso_date param_check_date$var$err_msg$required$date_format );
  575.  
  576.     if$iso_date )
  577.     {
  578.         set_param$var$iso_date );
  579.     }
  580.  
  581.     return $GLOBALS[$var];
  582. }
  583.  
  584.  
  585. /**
  586.  * Check if param is an ISO date.
  587.  *
  588.  * NOTE: for tokens like e.g. "D" (abbr. weekday), T_() gets used and it uses the current locale!
  589.  *
  590.  * @param string param name
  591.  * @param string error message
  592.  * @param boolean Is a non-empty date required?
  593.  * @param string date format (php format)
  594.  * @return boolean|stringfalse if not OK, ISO date if OK
  595.  */
  596. function param_check_date$var$err_msg$required false$date_format NULL )
  597. {
  598.     ifempty$GLOBALS[$var) )
  599.     // empty is OK if not required:
  600.         if$required )
  601.         {
  602.             param_error$var$err_msg );
  603.             return false;
  604.         }
  605.         return '';
  606.     }
  607.  
  608.     ifempty$date_format ) )
  609.     {    // Use locale date format:
  610.         $date_format locale_datefmt();
  611.     }
  612.  
  613.     // Convert PHP date format to regexp pattern:
  614.     $date_regexp '~^'.preg_replace_callback'~(\\\)?(\w)~'create_function'$m''
  615.         if( $m[1] == "\\\" ) return $m[2]; // escaped
  616.         switch( $m[2] )
  617.         {
  618.             case "d": return "([0-3]\\d)"; // day, 01-31
  619.             case "j": return "([1-3]?\\d)"; // day, 1-31
  620.             case "l": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday"])))).")";
  621.             case "D": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday_abbrev"])))).")";
  622.             case "e": // QP extension!
  623.                 return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["weekday_letter"])))).")";
  624.             case "S": return "(st|nd|rd|th)"; // english suffix for day
  625.  
  626.             case "m": return "([0-1]\\d)"; // month, 01-12
  627.             case "n": return "(1?\\d)"; // month, 1-12
  628.             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
  629.             case "M": return "(".str_replace("~", "\~", implode("|", array_map("trim", array_map("T_", $GLOBALS["month_abbrev"])))).")";
  630.  
  631.             case "y": return "(\\d\\d)"; // year, 00-99
  632.             case "Y": return "(\\d{4})"; // year, XXXX
  633.             default:
  634.                 return $m[0];
  635.         }' )$date_format ).'$~i'// case-insensitive?
  636.     // Allow additional spaces, e.g. "03  May 2007" when format is "d F Y":
  637.     $date_regexp preg_replace'~ +~''\s+'$date_regexp );
  638.  
  639.     // Check that the numbers match the date pattern:
  640.     ifpreg_match$date_regexp$GLOBALS[$var]$numbers ) )
  641.     {    // Date does match pattern:
  642.  
  643.         // Get all date pattern parts. We should get 3 parts!:
  644.         preg_match_all'/(?<!\\\\)[A-Za-z]/'$date_format$parts )// "(?<!\\\\)" means that the letter is not escaped with "\"
  645.         $day null;
  646.         $month null;
  647.         $year null;
  648.  
  649.         foreach$parts[0as $position => $part )
  650.         {
  651.             switch$part )
  652.             {
  653.                 case 'd':
  654.                 case 'j':
  655.                     $day $numbers[$position+1];
  656.                     break;
  657.  
  658.                 case 'm':
  659.                 case 'n':
  660.                     $month $numbers[$position+1];
  661.                     break;
  662.                 case 'F'// full month name
  663.                     $month array_searchstrtolower($numbers[$position+1])array_map('strtolower'array_map('trim'array_map('T_'$GLOBALS['month']))) );
  664.                     break;
  665.                 case 'M':
  666.                     $month array_searchstrtolower($numbers[$position+1])array_map('strtolower'array_map('trim'array_map('T_'$GLOBALS['month_abbrev']))) );
  667.                     break;
  668.  
  669.                 case 'y':
  670.                 case 'Y':
  671.                     $year $numbers[$position+1];
  672.                     if$year 50 )
  673.                     {
  674.                         $year 2000 $year;
  675.                     }
  676.                     elseif$year 100 )
  677.                     {
  678.                         $year 1900 $year;
  679.                     }
  680.                     break;
  681.             }
  682.         }
  683.  
  684.         ifcheckdate$month$day$year ) )
  685.         // all clean! :)
  686.  
  687.             // We convert the value to ISO:
  688.             $iso_date substr'0'.$year-).'-'.substr'0'.$month-).'-'.substr'0'.$day-);
  689.  
  690.             return $iso_date;
  691.         }
  692.     }
  693.  
  694.     // Date did not pass all tests:
  695.  
  696.     param_error$var$err_msg );
  697.  
  698.     return false;
  699. }
  700.  
  701.  
  702. /**
  703.  * Sets a date parameter with values from the request or to provided default,
  704.  * And check we have a compact date (numbers only) ( used for URL filtering )
  705.  *
  706.  * @param string Variable to set
  707.  * @param mixed Default value or TRUE if user input required
  708.  * @param boolean memorize ( see {@link param()} )
  709.  * @param string error message
  710.  * @param boolean 'required': Is non-empty date required? Default: true.
  711.  *
  712.  * @return string the compact date value ( yyyymmdd )
  713.  */
  714. function param_compact_date$var$default ''$memorize false$err_msg$required false )
  715. {
  716.     global $$var;
  717.  
  718.     param$var'string'$default$memorize );
  719.  
  720.     ifpreg_match'#^[0-9]{4,}$#'$$var ) )
  721.     {    // Valid compact date, all good.
  722.         return $$var;
  723.     }
  724.  
  725.     // We do not have a compact date, try normal date matching:
  726.     $iso_date param_check_date$var$err_msg$required );
  727.  
  728.     if$iso_date )
  729.     {
  730.         set_param$varcompact_date$iso_date ) );
  731.         return $$var;
  732.     }
  733.  
  734.     // Nothing valid found....
  735.     return '';
  736. }
  737.  
  738.  
  739. /**
  740.  * Sets a time parameter with the value from the request of the var argument
  741.  * or of the concat of the var argument_h: var argument_mn: var argument_s ,
  742.  * except if param is already set!
  743.  *
  744.  * @param string Variable to set
  745.  * @param mixed Default value or TRUE if user input required
  746.  * @param boolean Do we need to memorize this to regenerate the URL for this page?
  747.  * @param boolean Override if variable already set
  748.  * @param boolean Force setting of variable to default?
  749.  * @return mixed Final value of Variable, or false if we don't force setting and did not set
  750.  */
  751. function param_time$var$default ''$memorize false,    $override false$forceset true )
  752. {
  753.     global $$var;
  754.  
  755.     $got_time false;
  756.  
  757.     ifparam$var'string'$default$memorize$override$forceset ) )
  758.     // Got a time from text field:
  759.         ifpreg_match'/^(\d\d):(\d\d)(:(\d\d))?$/'$$var$matches ) )
  760.         {
  761.             $time_h $matches[1];
  762.             $time_mn $matches[2];
  763.             $time_s empty$matches[4$matches[4];
  764.             $got_time true;
  765.         }
  766.     }
  767.     elseif( ( $time_h param$var.'_h''integer'-) ) != -1
  768.                 && $time_mn param$var.'_mn''integer'-) ) != -)
  769.     {    // Got a time from selects:
  770.         $time_s param$var.'_s''integer');
  771.         $$var substr('0'.$time_h,-2).':'.substr('0'.$time_mn,-2).':'.substr('0'.$time_s,-2);
  772.         $got_time true;
  773.     }
  774.  
  775.     if$got_time )
  776.     // We got a time...
  777.         // Check if ranges are correct:
  778.         if$time_h >= && $time_h <= 23
  779.             && $time_mn >= && $time_mn <= 59
  780.             && $time_s >= && $time_s <= 59 )
  781.         {
  782.             // Time is correct
  783.             return $$var;
  784.         }
  785.     }
  786.  
  787.     param_error$varT_('Please enter a valid time.') );
  788.  
  789.     return false;
  790. }
  791.  
  792.  
  793. /**
  794.  * Extend a LIST parameter with an ARRAY param.
  795.  *
  796.  * Will be used for author/authorsel[], etc.
  797.  * Note: cannot be used for catsel[], because catsel is NON-recursive.
  798.  * @see param_compile_cat_array()
  799.  *
  800.  * @param string Variable to extend
  801.  * @param string Name of array Variable to use as an extension
  802.  * @param boolean Save non numeric prefix?  ( 1 char -- can be used as a modifier, e-g: - + * )
  803.  */
  804. function param_extend_list$var$var_ext_array$save_prefix true )
  805. {
  806.     // Make sure original var exists:
  807.     if!isset($GLOBALS[$var]) )
  808.     {
  809.         debug_die'Cannot extend non existing param : '.$var );
  810.     }
  811.     $original_val $GLOBALS[$var];
  812.  
  813.     // Get extension array:
  814.     $ext_values_array param$var_ext_array'array'array()false );
  815.     ifempty($ext_values_array) )
  816.     {    // No extension required:
  817.         return $original_val;
  818.     }
  819.  
  820.     // Handle prefix:
  821.     $prefix '';
  822.     if$save_prefix )
  823.     {    // We might want to save a prefix:
  824.         $prefix substr$original_val0);
  825.         ifis_numeric$prefix ) )
  826.         {    // The prefix is numeric, so it's NOT a prefix
  827.             $prefix '';
  828.         }
  829.         else
  830.         {    // We save the prefix, we must crop if off from the values:
  831.             $original_val substr$original_val);
  832.         }
  833.     }
  834.  
  835.     // Merge values:
  836.     ifempty($original_val) )
  837.     {
  838.         $original_values_array array();
  839.     }
  840.     else
  841.     {
  842.         $original_values_array explode','$original_val );
  843.     }
  844.     $new_values array_merge$original_values_array$ext_values_array );
  845.     $new_values array_unique$new_values );
  846.     $GLOBALS[$var$prefix.implode','$new_values );
  847.  
  848.  
  849.     return $GLOBALS[$var];
  850. }
  851.  
  852.  
  853. /**
  854.  * Compiles the cat array from $cat (recursive + optional modifiers) and $catsel[] (non recursive)
  855.  * and keeps those values available for future reference (category widget)
  856.  */
  857. function param_compile_cat_array$restrict_to_blog 0$cat_default NULL$catsel_default array() )
  858. {
  859.     // For now, we'll need those as globals!
  860.     // fp> this is used for the categories widget
  861.     // fp> we want might to use a $set_globals params to compile_cat_array()
  862.     global $cat_array$cat_modifier;
  863.  
  864.     $cat param'cat''/^[*\-]?([0-9]+(,[0-9]+)*)?$/'$cat_defaulttrue )// List of cats to restrict to
  865.     $catsel param'catsel''array'$catsel_defaulttrue );  // Array of cats to restrict to
  866.  
  867.     $cat_array array();
  868.     $cat_modifier '';
  869.  
  870.     compile_cat_array$cat$catsel/* by ref */ $cat_array/* by ref */ $cat_modifier$restrict_to_blog );
  871. }
  872.  
  873.  
  874. /**
  875.  * @param array of param names
  876.  * @param string error message
  877.  * @param string|NULLerror message for form field ($err_msg gets used if === NULL).
  878.  * @return boolean true if OK
  879.  */
  880. function params_check_at_least_one$vars$err_msg$field_err_msg NULL )
  881. {
  882.     foreach$vars as $var )
  883.     {
  884.         if!empty$GLOBALS[$var) )
  885.         // Okay, we got at least one:
  886.             return true;
  887.         }
  888.     }
  889.  
  890.     // Error!
  891.     param_error_multiple$vars$err_msg$field_err_msg );
  892.     return false;
  893. }
  894.  
  895.  
  896. /**
  897.  * Sets a combo parameter with values from the request,
  898.  * => the value of the select option and the input text value if new is selected
  899.  * Display an error if the new value is selected that the input text has a value
  900.  *
  901.  * @param string Variable to set
  902.  * @param mixed Default value or TRUE if user input required
  903.  * @param boolean true: allows to select new without entring a value in the input combo text
  904.  * @param string error message
  905.  *
  906.  * @return string position status ID or 'new' or '' if new is seleted but not input text value
  907.  *
  908.  */
  909. function param_combo$var$default$allow_none$err_msg ''  )
  910. {
  911.     param$var'string'$default );
  912.  
  913.     if$GLOBALS[$var== 'new' )
  914.     {    // The new option is selected in the combo select, so we need to check if we have a value in the combo input text:
  915.         $GLOBALS[$var.'_combo'param$var.'_combo''string' );
  916.  
  917.         ifempty$GLOBALS[$var.'_combo') )
  918.         // We have no value in the combo input text
  919.  
  920.             // Set request param to null
  921.             $GLOBALS[$varNULL;
  922.  
  923.             if!$allow_none )
  924.             // it's not allowed, so display error:
  925.                 param_error$var$err_msg );
  926.             }
  927.         }
  928.     }
  929.  
  930.     return $GLOBALS[$var];
  931. }
  932.  
  933.  
  934. /**
  935.  * set a parameter with the second part(X2) of the value from request ( X1-X2 )
  936.  *
  937.  * @param string Variable to set
  938.  *
  939.  */
  940. function param_child_select_value$var )
  941. {
  942.     global $$var;
  943.  
  944.     if$val param$var'string' ) )
  945.     // keep only the second part of val
  946.         preg_match'/^[0-9]+-([0-9]+)$/'$val$res );
  947.  
  948.         ifisset$res[1) )
  949.         //set to the var the second part of val
  950.             $$var $res[1];
  951.             return $$var;
  952.         }
  953.     }
  954.     return '';
  955. }
  956.  
  957.  
  958. /**
  959.  * @param string param name
  960.  * @return boolean true if OK
  961.  */
  962. function param_check_phone$var$required false )
  963. {
  964.     global $$var;
  965.  
  966.     ifempty$$var && $required )
  967.     // empty is OK:
  968.         return true;
  969.     }
  970.  
  971.     ifpreg_match'|^\+?[\-*#/(). 0-9]+$|'$$var ) )
  972.     {
  973.         param_error$varT_('The phone number is invalid.') );
  974.         return false;
  975.     }
  976.     else
  977.     // Keep only 0123456789+ caracters
  978.         $$var preg_replace'#[^0-9+]#'''$$var );
  979.     }
  980.     return true;
  981. }
  982.  
  983.  
  984. /**
  985.  * @param string param name
  986.  * @param string param name
  987.  * @param boolean Is a password required? (non-empty)
  988.  * @return boolean true if OK
  989.  */
  990. function param_check_passwords$var1$var2$required false )
  991. {
  992.     global $Settings;
  993.  
  994.     $pass1 $GLOBALS[$var1];
  995.     $pass2 $GLOBALS[$var2];
  996.  
  997.     ifempty($pass1&& empty($pass2&& $required )
  998.     // empty is OK:
  999.         return true;
  1000.     }
  1001.  
  1002.     ifempty($pass1) )
  1003.     {
  1004.         param_error$var1T_('Please enter your password twice.') );
  1005.         return false;
  1006.     }
  1007.     ifempty($pass2) )
  1008.     {
  1009.         param_error$var2T_('Please enter your password twice.') );
  1010.         return false;
  1011.     }
  1012.  
  1013.     // checking the password has been typed twice the same:
  1014.     if$pass1 != $pass2 )
  1015.     {
  1016.         param_error_multiplearray$var1$var2)T_('You typed two different passwords.') );
  1017.         return false;
  1018.     }
  1019.  
  1020.     ifstrlen($pass1$Settings->get('user_minpwdlen') )
  1021.     {
  1022.         param_error_multiplearray$var1$var2)sprintfT_('The minimum password length is %d characters.')$Settings->get('user_minpwdlen') ) );
  1023.         return false;
  1024.     }
  1025.  
  1026.     return true;
  1027. }
  1028.  
  1029.  
  1030. /**
  1031.  * Check if there have been validation errors
  1032.  *
  1033.  * We play it safe here and check for all kind of errors, not just those from this particular class.
  1034.  *
  1035.  * @return integer 
  1036.  */
  1037. {
  1038.     global $Messages;
  1039.  
  1040.     return $Messages->count('error');
  1041. }
  1042.  
  1043.  
  1044. /**
  1045.  * Tell if there is an error on given field.
  1046.  */
  1047. function param_has_error$var )
  1048. {
  1049.     global $param_input_err_messages;
  1050.  
  1051.     return isset$param_input_err_messages[$var);
  1052. }
  1053.  
  1054.  
  1055. /**
  1056.  * Get error message for a param
  1057.  *
  1058.  * @return string 
  1059.  */
  1060. function param_get_error_msg$var )
  1061. {
  1062.     global $param_input_err_messages;
  1063.  
  1064.     ifempty$param_input_err_messages[$var) )
  1065.     {
  1066.         return '';
  1067.     }
  1068.  
  1069.     return $param_input_err_messages[$var];
  1070. }
  1071.  
  1072.  
  1073. /**
  1074.  * Add an error for a variable, either to the Form's field and/or the global {@link $Messages} object.
  1075.  *
  1076.  * @param string param name
  1077.  * @param string|NULLerror message (by using NULL you can only add an error to the field, but not the $Message object)
  1078.  * @param string|NULLerror message for form field ($err_msg gets used if === NULL).
  1079.  */
  1080. function param_error$var$err_msg$field_err_msg NULL )
  1081. {
  1082.     global $param_input_err_messages;
  1083.  
  1084.     ifisset$param_input_err_messages[$var) )
  1085.     // We haven't already recorded an error for this field:
  1086.         if$field_err_msg === NULL )
  1087.         {
  1088.             $field_err_msg $err_msg;
  1089.         }
  1090.         $param_input_err_messages[$var$field_err_msg;
  1091.  
  1092.         ifisset($err_msg) )
  1093.         {
  1094.             param_add_message_to_Log$var$err_msg'error' );
  1095.         }
  1096.     }
  1097. }
  1098.  
  1099.  
  1100. /**
  1101.  * Add an error for multiple variables, either to the Form's field and/or the global {@link $Messages} object.
  1102.  *
  1103.  * @param array of param names
  1104.  * @param string|NULLerror message (by using NULL you can only add an error to the field, but not the $Message object)
  1105.  * @param string|NULLerror message for form fields ($err_msg gets used if === NULL).
  1106.  */
  1107. function param_error_multiple$vars$err_msg$field_err_msg NULL )
  1108. {
  1109.     global $param_input_err_messages;
  1110.  
  1111.     if$field_err_msg === NULL )
  1112.     {
  1113.         $field_err_msg $err_msg;
  1114.     }
  1115.  
  1116.     foreach$vars as $var )
  1117.     {
  1118.         ifisset$param_input_err_messages[$var) )
  1119.         // We haven't already recorded an error for this field:
  1120.             $param_input_err_messages[$var$field_err_msg;
  1121.         }
  1122.     }
  1123.  
  1124.     ifisset($err_msg) )
  1125.     {
  1126.         param_add_message_to_Log$var$err_msg'error' );
  1127.     }
  1128. }
  1129.  
  1130.  
  1131. /**
  1132.  * This function is used by {@link param_error()} and {@link param_error_multiple()}.
  1133.  *
  1134.  * If {@link $link_param_err_messages_to_field_IDs} is true, it will link those parts of the
  1135.  * error message that are not already links, to the html IDs of the fields with errors.
  1136.  *
  1137.  * @param string param name
  1138.  * @param string error message
  1139.  */
  1140. function param_add_message_to_Log$var$err_msg$log_category 'error' )
  1141. {
  1142.     global $link_param_err_messages_to_field_IDs;
  1143.     global $Messages;
  1144.  
  1145.     if!empty($link_param_err_messages_to_field_IDs) )
  1146.     {
  1147.         $var_id Form::get_valid_id($var);
  1148.         $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()
  1149.  
  1150.         ifstrpos$err_msg'<a' !== false )
  1151.         // there is at least one link in $err_msg, link those parts that are no links
  1152.             $err_msg preg_replace'~(\s*)(<a\s+[^>]+>[^<]*</a>\s*)~i''</a>$1&raquo;$2'.$start_link$err_msg );
  1153.         }
  1154.  
  1155.         ifsubstr($err_msg04== '</a>' )
  1156.         // There was a link at the beginning of $err_msg: we do not prepend an emtpy link before it
  1157.             $Messages->addsubstr$err_msg).'</a>'$log_category );
  1158.         }
  1159.         else
  1160.         {
  1161.             $Messages->add$start_link.$err_msg.'</a>'$log_category );
  1162.         }
  1163.     }
  1164.     else
  1165.     {
  1166.         $Messages->add$err_msg$log_category );
  1167.     }
  1168. }
  1169.  
  1170.  
  1171.  
  1172. /**
  1173.  * Set a param (global) & Memorize it for automatic future use in regenerate_url()
  1174.  *
  1175.  * @param string Variable to memorize
  1176.  * @param string Type of the variable
  1177.  * @param mixed Default value to compare to when regenerating url
  1178.  * @param mixed Value to set
  1179.  */
  1180. function memorize_param$var$type$default$value NULL )
  1181. {
  1182.     global $Debuglog$global_param_list$$var;
  1183.  
  1184.     if!isset($global_param_list) )
  1185.     // Init list if necessary:
  1186.         ifisset($Debuglog) ) $Debuglog->add'init $global_param_list''params' );
  1187.         $global_param_list array();
  1188.     }
  1189.  
  1190.     $Debuglog->add"memorize_param: $var $type default=$default".(is_null($value'' " value=$value")'params');
  1191.  
  1192.     $global_param_list[$vararray'type' => $type'default' => (($default===trueNULL $default) );
  1193.  
  1194.     if!is_null$value ) )
  1195.     {    // We want to set the variable too.
  1196.         set_param$var$value );
  1197.     }
  1198. }
  1199.  
  1200.  
  1201. /**
  1202.  * Forget a param so that is will not get included in subsequent {@link regenerate_url()} calls.
  1203.  * @param string Param name
  1204.  */
  1205. function forget_param$var )
  1206. {
  1207.     global $Debuglog$global_param_list;
  1208.  
  1209.     $Debuglog->add'forget_param('.$var.')''params' );
  1210.  
  1211.     unset$global_param_list[$var);
  1212. }
  1213.  
  1214.  
  1215. /**
  1216.  * Has the param already been memorized?
  1217.  */
  1218. function param_ismemorized$var )
  1219. {
  1220.     global $global_param_list;
  1221.  
  1222.     return isset($global_param_list[$var]);
  1223. }
  1224.  
  1225.  
  1226. /**
  1227.  * Set the value of a param (by force! :P)
  1228.  *
  1229.  * Same as setting a global, except you don't need a global declaration in your function.
  1230.  *
  1231.  * @param string Param name
  1232.  * @param mixed Value
  1233.  * @return mixed Value
  1234.  */
  1235. function set_param$var$value )
  1236. {
  1237.     return $GLOBALS[$var$value;
  1238. }
  1239.  
  1240.  
  1241.  
  1242. /**
  1243.  * Get the value of a param.
  1244.  *
  1245.  * @return NULL|mixedThe value of the param, if set. NULL otherwise.
  1246.  */
  1247. function get_param$var )
  1248. {
  1249.     ifisset($GLOBALS[$var]) )
  1250.     {
  1251.         return NULL;
  1252.     }
  1253.  
  1254.     return $GLOBALS[$var];
  1255. }
  1256.  
  1257.  
  1258. /**
  1259.  * Construct an array of memorized params which are not in the ignore list
  1260.  *
  1261.  * @param mixed string or array of ignore params
  1262.  */
  1263. function get_memorized$ignore '' )
  1264. {
  1265.     global $global_param_list;
  1266.  
  1267.     $memo array();
  1268.  
  1269.     // Transform ignore params into an array:
  1270.     ifempty $ignore ) )
  1271.     {
  1272.         $ignore array();
  1273.     }
  1274.     elseif!is_array($ignore) )
  1275.     {
  1276.         $ignore explode','$ignore );
  1277.     }
  1278.  
  1279.     // Loop on memorize params
  1280.     ifisset($global_param_list) )
  1281.     {
  1282.         foreach$global_param_list as $var => $thisparam )
  1283.         {
  1284.             if!in_array$var$ignore ) )
  1285.             {
  1286.                 global $$var;
  1287.                 $value = $$var;
  1288.                 $memo[$var= $$var;
  1289.             }
  1290.         }
  1291.     }
  1292.     return $memo;
  1293. }
  1294.  
  1295.  
  1296. /**
  1297.  * Regenerate current URL from parameters
  1298.  * This may clean it up
  1299.  * But it is also useful when generating static pages: you cannot rely on $_REQUEST[]
  1300.  *
  1301.  * @param mixed|string(delimited by commas) or array of params to ignore (can be regexps in /.../)
  1302.  * @param array|stringParam(s) to set
  1303.  * @param mixed|stringAlternative URL we want to point to if not the current URL (may be absolute if BASE tag gets used)
  1304.  * @param string Delimiter to use for multiple params (typically '&amp;' or '&')
  1305.  */
  1306. function regenerate_url$ignore ''$set ''$pagefileurl ''$glue '&amp;' )
  1307. {
  1308.     global $Debuglog$global_param_list$ReqHost$ReqPath;
  1309.     global $base_tag_set;
  1310.  
  1311.     // Transform ignore param into an array:
  1312.     ifempty($ignore) )
  1313.     {
  1314.         $ignore array();
  1315.     }
  1316.     elseif!is_array($ignore) )
  1317.     {
  1318.         $ignore explode','$ignore );
  1319.     }
  1320.  
  1321.     // Construct array of all params that have been memorized:
  1322.     // (Note: we only include values if they differ from the default and they are not in the ignore list)
  1323.     $params array();
  1324.     ifisset($global_param_list) ) foreach$global_param_list as $var => $thisparam )
  1325.     {    // For each saved param...
  1326.         $type $thisparam['type'];
  1327.         $defval $thisparam['default'];
  1328.  
  1329.         // Check if the param should to be ignored:
  1330.         $skip false;
  1331.         foreach$ignore as $ignore_pattern )
  1332.         {
  1333.             if$ignore_pattern[0== '/' )
  1334.             // regexp:
  1335.                 ifpreg_match$ignore_pattern$var ) )
  1336.                 {    // Skip this param!
  1337.                     $skip true;
  1338.                     break;
  1339.                 }
  1340.             }
  1341.             else
  1342.             {
  1343.                 if$var == $ignore_pattern )
  1344.                 {    // Skip this param!
  1345.                     $skip true;
  1346.                     break;
  1347.                 }
  1348.             }
  1349.         }
  1350.         if$skip )
  1351.         // we don't want to include that param
  1352.             // $Debuglog->add( 'regenerate_url(): EXPLICIT IGNORE '.$var, 'params' );
  1353.             continue;
  1354.         }
  1355.  
  1356.         $value $GLOBALS[$var];
  1357.         if$value != $defval )
  1358.         // Value is not set to default value:
  1359.             // Note: sometimes we will want to include an empty value, especially blog=0 ! In that case we set the default for blog to -1.
  1360.             // $Debuglog->add( "regenerate_url(): Using var=$var, type=$type, defval=[$defval], val=[$value]", 'params' );
  1361.  
  1362.             if$type === 'array' )
  1363.             // there is a special formatting in case of arrays
  1364.                 $url_array array();
  1365.                 foreach$value as $value )
  1366.                 {
  1367.                     $params[$var.'%5B%5D='.rawurlencode($value);
  1368.                 }
  1369.             }
  1370.             else
  1371.             {    // not an array : normal formatting
  1372.                 $params[$var.'='.rawurlencode($value);
  1373.             }
  1374.         }
  1375.         else // if( $var == 's' )
  1376.         {
  1377.             // $Debuglog->add( "regenerate_url(): DEFAULT ignore var=$var, type=$type, defval=[$defval], val=[$value]", 'params' );
  1378.         }
  1379.     }
  1380.  
  1381.     // Merge in the params we want to force to a specific value:
  1382.     if!empty$set ) )
  1383.     {    // We got some forced params:
  1384.         // Transform set param into an array:
  1385.         if!is_array($set) )
  1386.         {
  1387.             $set array$set );
  1388.         }
  1389.         // Merge them in:
  1390.         $params array_merge$params$set );
  1391.     }
  1392.  
  1393.     // Construct URL:
  1394.     ifempty($pagefileurl) )
  1395.     {
  1396.         $url $pagefileurl;
  1397.     }
  1398.     else
  1399.     {
  1400.         ifempty($base_tag_set) )
  1401.         {
  1402.             ifisset($Debuglog) ) $Debuglog->add'regenerate_url(): Using full URL because of $base_tag_set.''params' );
  1403.             $url $ReqHost.$ReqPath;
  1404.         }
  1405.         else
  1406.         {    // Use just absolute path, because there's no <base> tag used
  1407.             $url $ReqPath;
  1408.         }
  1409.     }
  1410.  
  1411.     if!empty$params ) )
  1412.     {
  1413.         $url url_add_param$urlimplode$glue$params )$glue );
  1414.     }
  1415.     // if( isset($Debuglog) ) $Debuglog->add( 'regenerate_url(): ['.$url.']', 'params' );
  1416.     return $url;
  1417. }
  1418.  
  1419.  
  1420. /**
  1421.  * Checks if a given regular expression is valid.
  1422.  *
  1423.  * It changes the error_handler and restores it.
  1424.  *
  1425.  * @author plenque at hotmail dot com {@link http://php.net/manual/en/function.preg-match.php}
  1426.  * @param string the regular expression to test
  1427.  * @param boolean does the regular expression includes delimiters (and optionally modifiers)?
  1428.  * @return boolean 
  1429.  */
  1430. function is_regexp$reg_exp$includes_delim false )
  1431. {
  1432.     $sPREVIOUSHANDLER set_error_handler'_trapError' );
  1433.     if$includes_delim )
  1434.     {
  1435.         $reg_exp '#'.str_replace'#''\#'$reg_exp ).'#';
  1436.     }
  1437.     preg_match$reg_exp'' );
  1438.     restore_error_handler$sPREVIOUSHANDLER );
  1439.  
  1440.     return !_traperror();
  1441. }
  1442.  
  1443.  
  1444. /**
  1445.  * Meant to replace error handler temporarily.
  1446.  *
  1447.  * @return integer number of errors
  1448.  */
  1449. function _trapError$reset )
  1450. {
  1451.     static $iERRORES;
  1452.  
  1453.     if!func_num_args() )
  1454.     {
  1455.         $iRETORNO $iERRORES;
  1456.         $iERRORES 0;
  1457.         return $iRETORNO;
  1458.     }
  1459.     else
  1460.     {
  1461.         $iERRORES++;
  1462.     }
  1463. }
  1464.  
  1465.  
  1466. /*
  1467.  * Clean up the mess PHP has created with its funky quoting everything!
  1468.  */
  1469. // That stupid PHP behaviour consisting of adding slashes everywhere is unfortunately on
  1470.  
  1471.     ifin_arraystrtolower(ini_get('magic_quotes_sybase'))array('on''1''true''yes') ) )
  1472.     // overrides "magic_quotes_gpc" and only replaces single quotes with themselves ( "'" => "''" )
  1473.         /**
  1474.          * @ignore
  1475.          */
  1476.         function remove_magic_quotes$mixed )
  1477.         {
  1478.             ifis_array$mixed ) )
  1479.             {
  1480.                 foreach($mixed as $k => $v)
  1481.                 {
  1482.                     $mixed[$kremove_magic_quotes$v );
  1483.                 }
  1484.             }
  1485.             elseifis_string($mixed) )
  1486.             {
  1487.                 $mixed str_replace'\'\'''\''$mixed );
  1488.             }
  1489.             return $mixed;
  1490.         }
  1491.     }
  1492.     else
  1493.     {
  1494.         /**
  1495.          * Remove quotes from input.
  1496.          * This handles magic_quotes_gpc and magic_quotes_sybase PHP settings/variants.
  1497.          *
  1498.          * NOTE: you should not use it directly, but one of the param-functions!
  1499.          *
  1500.          * @param mixed string or array (function is recursive)
  1501.          * @return mixed Value, with magic quotes removed
  1502.          */
  1503.         function remove_magic_quotes$mixed )
  1504.         {
  1505.             ifis_array$mixed ) )
  1506.             {
  1507.                 foreach($mixed as $k => $v)
  1508.                 {
  1509.                     $mixed[$kremove_magic_quotes$v );
  1510.                 }
  1511.             }
  1512.             elseifis_string($mixed) )
  1513.             {
  1514.                 $mixed stripslashes$mixed );
  1515.             }
  1516.             return $mixed;
  1517.         }
  1518.     }
  1519. }
  1520. else
  1521. {
  1522.     /**
  1523.      * @ignore
  1524.      */
  1525.     function remove_magic_quotes$mixed )
  1526.     {
  1527.         return $mixed;
  1528.     }
  1529. }
  1530.  
  1531.  
  1532.  
  1533.  
  1534.  
  1535. /**
  1536.  * Sets an HTML parameter and checks for sanitized code.
  1537.  *
  1538.  * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
  1539.  * It only checks on it and produces error messages.
  1540.  * It is NOT (necessarily) safe to use the output.
  1541.  *
  1542.  * @todo dh> Not implemented?!
  1543.  *
  1544.  * @param string Variable to set
  1545.  * @param mixed Default value or TRUE if user input required
  1546.  * @param boolean memorize ( see {@link param()} )
  1547.  * @param string error message
  1548.  *
  1549.  * @return string 
  1550.  */
  1551. function param_html$var$default ''$memorize false$err_msg )
  1552. {
  1553.  
  1554. }
  1555.  
  1556.  
  1557. /**
  1558.  * Checks for sanitized code.
  1559.  *
  1560.  * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
  1561.  * It only checks on it and produces error messages.
  1562.  * It is NOT (necessarily) safe to use the output.
  1563.  *
  1564.  * @param string param name
  1565.  * @param string error message
  1566.  * @return boolean|string
  1567.  */
  1568. function param_check_html$var$err_msg '#'$field_err_msg '#'$autobr )
  1569. {
  1570.     global $Messages;
  1571.  
  1572.     $altered_html check_html_sanity$GLOBALS[$var]'posting'$autobr );
  1573.  
  1574.      if$altered_html === false )
  1575.     {    // We have errors, do not keep sanitization attemps:
  1576.         if$err_msg == '#' )
  1577.         {
  1578.             $err_msg T_('Invalid XHTML.');
  1579.         }
  1580.         if$field_err_msg == '#' )
  1581.         {
  1582.             $field_err_msg T_('Invalid XHTML.');
  1583.         }
  1584.  
  1585.         param_error$var$err_msg$field_err_msg );
  1586.         return false;
  1587.     }
  1588.  
  1589.     // Keep the altered HTML (balanced tags, etc.) - NOT necessarily safe if loose checking has been allowed.
  1590.     $GLOBALS[$var$altered_html;
  1591.  
  1592.     return $altered_html;
  1593. }
  1594.  
  1595.  
  1596. /**
  1597.  * DEPRECATED Stub for plugin compatibility:
  1598.  */
  1599. function format_to_post$content$autobr 0$is_comment 0$encoding NULL )
  1600. {
  1601.     $ret check_html_sanity$content$is_comment 'commenting' 'posting' )$autobr$encoding );
  1602.     if$ret === false )
  1603.     {    // ERROR
  1604.         return $content;
  1605.     }
  1606.  
  1607.     // return aletered content
  1608.     return $ret;
  1609. }
  1610.  
  1611.  
  1612. /**
  1613.  * Check raw HTML input for different levels of sanity including:
  1614.  * - XHTML validation
  1615.  * - Javascript injection
  1616.  * - antispam
  1617.  *
  1618.  * Also cleans up the content on some levels:
  1619.  * - trimming
  1620.  * - balancing tags
  1621.  *
  1622.  * WARNING: this does *NOT* (necessarilly) make the HTML code safe.
  1623.  * It only checks on it and produces error messages.
  1624.  * It is NOT (necessarily) safe to use the output.
  1625.  *
  1626.  * @param string The content to format
  1627.  * @param string Context: "posting", "xmlrpc_posting" or "commenting"
  1628.  * @param integer Create automated <br /> tags?
  1629.  * @param string Encoding (used for XHTML_Validator only!); defaults to $io_charset
  1630.  * @param boolean When "posting" or "xmlrpc_posting", should comments be converted to
  1631.  *                 visible HTML? Forced to true when "commenting".
  1632.  * @return boolean|string
  1633.  */
  1634. function check_html_sanity$content$context 'posting'$autobr false,
  1635.                             $encoding NULL$escape_comments false )
  1636. {
  1637.     global $use_balanceTags$admin_url;
  1638.     global $io_charset$use_xhtmlvalidation_for_comments$comment_allowed_tags$comments_allow_css_tweaks;
  1639.     global $Messages;
  1640.  
  1641.     /**
  1642.      * @var User
  1643.      */
  1644.     global $current_User;
  1645.  
  1646.     switch$context )
  1647.     {
  1648.         case 'posting':
  1649.         case 'xmlrpc_posting':
  1650.             $Group $current_User->get_Group();
  1651.             if$context == 'posting' )
  1652.             {
  1653.                 $xhtmlvalidation  ($Group->perm_xhtmlvalidation == 'always');
  1654.             }
  1655.             else
  1656.             {
  1657.                 $xhtmlvalidation  ($Group->perm_xhtmlvalidation_xmlrpc == 'always');
  1658.             }
  1659.             $allow_css_tweaks $Group->perm_xhtml_css_tweaks;
  1660.             $allow_javascript $Group->perm_xhtml_javascript;
  1661.             $allow_iframes    $Group->perm_xhtml_iframes;
  1662.             $allow_objects    $Group->perm_xhtml_objects;
  1663.             $bypass_antispam  $Group->perm_bypass_antispam;
  1664.             break;
  1665.  
  1666.         case 'commenting':
  1667.             $xhtmlvalidation  $use_xhtmlvalidation_for_comments;
  1668.             $allow_css_tweaks $comments_allow_css_tweaks;
  1669.             $allow_javascript false;
  1670.             $allow_iframes    false;
  1671.             $allow_objects    false;
  1672.             // fp> I don't know if it makes sense to bypass antispam in commenting context if the user has that kind of permissions.
  1673.             // If so, then we also need to bypass in several other places.
  1674.             $bypass_antispam  false;
  1675.             break;
  1676.  
  1677.         default:
  1678.             debug_die'unknown context: '.$context );
  1679.     }
  1680.  
  1681.     $error false;
  1682.  
  1683.     // NOTE: These RegExps match NameStartChar and NameChar, but exclude
  1684.     //       higher characters (i. e. UTF-8). That's pure laziness: FIXME.
  1685.     // Replace any & that is not a character or entity reference with &amp;
  1686.     $content preg_replace'/&(?!#[0-9]+;|#x[0-9a-fA-F]+;|[a-zA-Z_:][a-zA-Z0-9._:-]*;)/''&amp;'$content );
  1687.     // Escape any '<'s that are not used in a valid manner:
  1688.     $content preg_replace'#<(?![!?/]|[a-zA-Z_:][a-zA-Z0-9._:-]*)#''&lt;'$content );
  1689.  
  1690.     // ANTISPAM check:
  1691.     if$bypass_antispam
  1692.         && $block antispam_check($content) )
  1693.     {
  1694.         if$context == 'xmlrpc_posting' )
  1695.         {
  1696.             $errmsg ($context == 'commenting')
  1697.                 ? T_('Illegal content found (spam?)')
  1698.                 : sprintfT_('Illegal content found: blacklisted word "%s"')$block );
  1699.         }
  1700.         else
  1701.         {
  1702.             $errmsg ($context == 'commenting')
  1703.                 ? T_('Illegal content found (spam?)')
  1704.                 : sprintfT_('Illegal content found: blacklisted word &laquo;%s&raquo;')htmlspecialchars($block) );
  1705.         }
  1706.  
  1707.         $Messages->add(    $errmsg'error' );
  1708.         $error true;
  1709.     }
  1710.  
  1711.     if$autobr )
  1712.     // Auto <br />:
  1713.         // may put brs in the middle of multiline tags...
  1714.         // TODO: this may create "<br />" tags in "<UL>" (outside of <LI>) and make the HTML invalid! -> use autoP pugin?
  1715.         $content autobrize$content );
  1716.     }
  1717.  
  1718.     $content trim$content );
  1719.  
  1720.     if$use_balanceTags )
  1721.     // Auto close open tags:
  1722.         $content balance_tags$content );
  1723.     }
  1724.  
  1725.     if$context == 'commenting' || $escape_comments )
  1726.     {    // Comments, processing instructions etc. are not allowed when commenting.
  1727.         $content str_replacearray(
  1728.                 // Comments, CDATA sections and various declarations.
  1729.                 // Declarations end with a single '>' which makes it hard
  1730.                 // to correctly detect their closing tags. Thus, we don't
  1731.                 // even try to do that (FIXME).
  1732.                 '<!',
  1733.                 // Closing comments
  1734.                 '-->',
  1735.                 // Closing CDATA sections
  1736.                 ']]>',
  1737.                 // Processing instructions
  1738.                 '<?',
  1739.                 '?>',
  1740.             )array(
  1741.                 '&lt;!',
  1742.                 '--&gt;',
  1743.                 ']]&gt;',
  1744.                 '&lt;?',
  1745.                 '?&gt;',
  1746.             )$content );
  1747.     }
  1748.  
  1749.     if$xhtmlvalidation )
  1750.     // We want to validate XHTML:
  1751.         load_class'xhtml_validator/_xhtml_validator.class.php' );
  1752.  
  1753.         $XHTML_Validator new XHTML_Validator$context$allow_css_tweaks$allow_iframes$allow_javascript$allow_objects$encoding );
  1754.  
  1755.         if$XHTML_Validator->check$content ) ) // TODO: see if we need to use convert_chars( $content, 'html' )
  1756.         {
  1757.             $error true;
  1758.         }
  1759.     }
  1760.     else
  1761.     {    // We do not WANT to validate XHTML, fall back to basic security checking:
  1762.         // This is only as strong as its regexps can parse xhtml. This is significantly inferior to the XHTML checker above.
  1763.         // The only advantage of this checker is that it can check for a little security without requiring VALID XHTML.
  1764.  
  1765.         if$context == 'commenting' )
  1766.         {    // DEPRECATED but still...
  1767.             $content strip_tags$content$comment_allowed_tags );
  1768.         }
  1769.         else if$escape_comments
  1770.                 && preg_match_all'#(?:<!--|<!\[CDATA\[|<\?)#i'$content$matchesPREG_OFFSET_CAPTURE ) )
  1771.         {    // We are posting an item, try an el-cheapo check for unclosed
  1772.             // comments, processing instructions etc.
  1773.             // Declarations end with a single '>' which is hard to detect
  1774.             // correctly, so we don't try to validate them here. In theory,
  1775.             // this may be an issue, but apparently, it's not a big threat.
  1776.             static $search array(
  1777.                 '<!--'      => '-->',
  1778.                 '<![CDATA[' => ']]>',
  1779.                 '<?'        => '?>',
  1780.             );
  1781.  
  1782.             $last_offset 0;
  1783.             foreach$matches[0as $m )
  1784.             {
  1785.                 list$text$this_offset $m;
  1786.                 if$this_offset $last_offset )
  1787.                 {
  1788.                     continue;
  1789.                 }
  1790.  
  1791.                 $last_offset strpos$content$search[$text]$this_offset strlen$text ) );
  1792.                 if$last_offset === false )
  1793.                 {
  1794.                     $Messages->addT_'XML document not well-formed, are there unclosed comments or processing instructions?' )'error' );
  1795.                     $error true;
  1796.                     break;
  1797.                 }
  1798.  
  1799.                 $last_offset += strlen$search[$text);
  1800.             }
  1801.         }
  1802.  
  1803.         if$error )
  1804.         {    // No error yet?
  1805.             // TODO: Remove useless assignment.
  1806.             $check $content;
  1807.  
  1808.             // CHECK Styling restictions:
  1809.             if$allow_css_tweaks
  1810.                 && preg_match'#\s((style|class|id)\s*=)#i'$check$matches) )
  1811.             {
  1812.                 $Messages->addT_('Illegal CSS markup found: ').htmlspecialchars($matches[1])'error' );
  1813.                 $error true;
  1814.             }
  1815.  
  1816.             // CHECK JAVASCRIPT:
  1817.             if$allow_javascript
  1818.                 && preg_match'( < \s* //? \s* (script|noscript) )xi'$check$matches )
  1819.                     || preg_match'#\s((on[a-z]+)\s*=)#i'$check$matches )
  1820.                     // action=, background=, cite=, classid=, codebase=, data=, href=, longdesc=, profile=, src=, usemap=
  1821.                     || preg_match'#=["\'\s]*((javascript|vbscript|about):)#i'$check$matches ) ) )
  1822.             {
  1823.                 $Messages->addT_('Illegal javascript markup found: ').htmlspecialchars($matches[1])'error' );
  1824.                 $error true;
  1825.             }
  1826.  
  1827.             // CHECK IFRAMES:
  1828.             if$allow_iframes
  1829.                 && preg_match'( < \s* //? \s* (frame|iframe) )xi'$check$matches) )
  1830.             {
  1831.                 $Messages->addT_('Illegal frame markup found: ').htmlspecialchars($matches[1])'error' );
  1832.                 $error true;
  1833.             }
  1834.  
  1835.             // CHECK OBJECTS:
  1836.             if$allow_objects
  1837.                 && preg_match'( < \s* //? \s* (applet|object|param|embed) )xi'$check$matches) )
  1838.             {
  1839.                 $Messages->addT_('Illegal object markup found: ').htmlspecialchars($matches[1])'error' );
  1840.                 $error true;
  1841.             }
  1842.         }
  1843.     }
  1844.  
  1845.     if$error )
  1846.     {
  1847.         if!empty($current_User)
  1848.                 && !empty($Group)  // This one will basically prevent this case from happening when commenting
  1849.                 && $current_User->check_perm'users''edit'false ) )
  1850.         {
  1851.             $Messages->addsprintfT_('(Note: To get rid of the above validation warnings, you can deactivate unwanted validation rules in your <a %s>Group settings</a>.)'),
  1852.                                         'href="'.$admin_url.'?ctrl=users&amp;grp_ID='.$Group->ID.'"' )'error' );
  1853.         }
  1854.         return false;
  1855.     }
  1856.  
  1857.     // Return sanitized content
  1858.     return $content;
  1859. }
  1860.  
  1861.  
  1862. /**
  1863.  * Balances Tags of string using a modified stack.
  1864.  *
  1865.  * @param string HTML to be balanced
  1866.  * @return string Balanced HTML
  1867.  */
  1868. function balance_tags$text )
  1869. {
  1870.     $tagstack array();
  1871.     $stacksize 0;
  1872.     $tagqueue '';
  1873.     $newtext '';
  1874.  
  1875.     whilepreg_match('~<(\s*/?\w+)\s*(.*?)/?>~'$text$regex) )
  1876.     {
  1877.         $newtext $newtext $tagqueue;
  1878.  
  1879.         $i strpos($text,$regex[0]);
  1880.         $l strlen($tagqueuestrlen($regex[0]);
  1881.  
  1882.         // clear the shifter
  1883.         $tagqueue '';
  1884.  
  1885.         // Pop or Push
  1886.         ifsubstr($regex[1],0,1== '/' )
  1887.         // End Tag
  1888.             $tag strtolower(substr($regex[1],1));
  1889.  
  1890.             // if too many closing tags
  1891.             if($stacksize <= 0)
  1892.             {
  1893.                 $tag '';
  1894.                 //or close to be safe $tag = '/' . $tag;
  1895.             }
  1896.             // if stacktop value = tag close value then pop
  1897.             else if ($tagstack[$stacksize 1== $tag)
  1898.             // found closing tag
  1899.                 $tag '</'.$tag.'>'// Close Tag
  1900.                 // Pop
  1901.                 array_pop ($tagstack);
  1902.                 $stacksize--;
  1903.             else // closing tag not at top, search for it
  1904.                 for ($j=$stacksize-1;$j>=0;$j--{
  1905.                     if ($tagstack[$j== $tag{
  1906.                     // add tag to tagqueue
  1907.                         for ($k=$stacksize-1;$k>=$j;$k--){
  1908.                             $tagqueue .= '</' array_pop ($tagstack'>';
  1909.                             $stacksize--;
  1910.                         }
  1911.                         break;
  1912.                     }
  1913.                 }
  1914.                 $tag '';
  1915.             }
  1916.         }
  1917.         else
  1918.         // Begin Tag
  1919.             $tag strtolower($regex[1]);
  1920.  
  1921.             // Tag Cleaning
  1922.  
  1923.             // Push if not img, br, hr, param or input
  1924.             if($tag != 'br' && $tag != 'img' && $tag != 'hr' && $tag != 'param' && $tag != 'input')
  1925.             {
  1926.                 $stacksize array_push ($tagstack$tag);
  1927.                 $closing '>';
  1928.             }
  1929.             else
  1930.             {
  1931.                 $closing ' />';
  1932.             }
  1933.             // Attributes
  1934.             // $attributes = $regex[2];
  1935.             $attributes $regex[2];
  1936.             if($attributes)
  1937.             {
  1938.                 $attributes ' '.trim($attributes);
  1939.             }
  1940.  
  1941.             $tag '<'.$tag.$attributes.$closing;
  1942.         }
  1943.  
  1944.         $newtext .= substr($text,0,$i$tag;
  1945.         $text substr($text,$i+$l);
  1946.     }
  1947.  
  1948.     // Clear Tag Queue
  1949.     $newtext $newtext $tagqueue;
  1950.  
  1951.     // Add Remaining text
  1952.     $newtext .= $text;
  1953.  
  1954.     // Empty Stack
  1955.     while($x array_pop($tagstack)) {
  1956.         $newtext $newtext '</' $x '>'// Add remaining tags to close
  1957.     }
  1958.  
  1959.     return $newtext;
  1960. }
  1961.  
  1962.  
  1963. ?>