Guest User

Untitled

a guest
Oct 20th, 2012
43
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 150.20 KB | None | 0 0
  1. <?php
  2. /*======================================================================*\
  3. || #################################################################### ||
  4. || # vBulletin 4.2.0 Patch Level 2 - Licence Number
  5. || # ---------------------------------------------------------------- # ||
  6. || # Copyright ©2000-2012 vBulletin Solutions Inc. All Rights Reserved. ||
  7. || # This file may not be redistributed in whole or significant part. # ||
  8. || # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
  9. || # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
  10. || #################################################################### ||
  11. \*======================================================================*/
  12.  
  13. define('FILE_VERSION', '4.2.0'); // this should match installsteps.php
  14. define('SIMPLE_VERSION', '420'); // see vB_Datastore::check_options()
  15. define('YUI_VERSION', '2.9.0'); // define the YUI version we bundle
  16. define('JQUERY_VERSION', '1.6.4'); // define the jQuery version we use
  17. define('JQUERY_MOBILE_VERSION', '1.0'); // define the jQuery Mobile Style Version
  18.  
  19. /**#@+
  20. * The maximum sizes for the "small" profile avatars
  21. */
  22. define('FIXED_SIZE_AVATAR_WIDTH',  60);
  23. define('FIXED_SIZE_AVATAR_HEIGHT', 80);
  24. /**#@-*/
  25.  
  26. /**#@+
  27. * These make up the bit field to disable specific types of BB codes.
  28. */
  29. define('ALLOW_BBCODE_BASIC',  1);
  30. define('ALLOW_BBCODE_COLOR',  2);
  31. define('ALLOW_BBCODE_SIZE',   4);
  32. define('ALLOW_BBCODE_FONT',   8);
  33. define('ALLOW_BBCODE_ALIGN',  16);
  34. define('ALLOW_BBCODE_LIST',   32);
  35. define('ALLOW_BBCODE_URL',    64);
  36. define('ALLOW_BBCODE_CODE',   128);
  37. define('ALLOW_BBCODE_PHP',    256);
  38. define('ALLOW_BBCODE_HTML',   512);
  39. define('ALLOW_BBCODE_IMG',    1024);
  40. define('ALLOW_BBCODE_QUOTE',  2048);
  41. define('ALLOW_BBCODE_CUSTOM', 4096);
  42. define('ALLOW_BBCODE_VIDEO',  8192);
  43. /**#@-*/
  44.  
  45. /**#@+
  46. * These make up the bit field to control what "special" BB codes are found in the text.
  47. */
  48. define('BBCODE_HAS_IMG',    1);
  49. define('BBCODE_HAS_ATTACH', 2);
  50. define('BBCODE_HAS_SIGPIC', 4);
  51. define('BBCODE_HAS_RELPATH',8);
  52. /**#@-*/
  53.  
  54. /**#@+
  55. * Bitfield values for the inline moderation javascript selector which should be self-explanitory
  56. */
  57. define('POST_FLAG_INVISIBLE', 1);
  58. define('POST_FLAG_DELETED',   2);
  59. define('POST_FLAG_ATTACH',    4);
  60. define('POST_FLAG_GUEST',     8);
  61. /**#@-*/
  62.  
  63. // #############################################################################
  64. // MySQL Database Class
  65.  
  66. /**#@+
  67. * The type of result set to return from the database for a specific row.
  68. */
  69. define('DBARRAY_BOTH',  0);
  70. define('DBARRAY_ASSOC', 1);
  71. define('DBARRAY_NUM',   2);
  72. /**#@-*/
  73.  
  74. /**
  75. * Class to interface with a database
  76. *
  77. * This class also handles data replication between a master and slave(s) servers
  78. *
  79. * @package  vBulletin
  80. * @version  $Revision: 62833 $
  81. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  82. */
  83. class vB_Database
  84. {
  85.     /**
  86.     * Array of function names, mapping a simple name to the RDBMS specific function name
  87.     *
  88.     * @var  array
  89.     */
  90.     var $functions = array(
  91.         'connect'            => 'mysql_connect',
  92.         'pconnect'           => 'mysql_pconnect',
  93.         'select_db'          => 'mysql_select_db',
  94.         'query'              => 'mysql_query',
  95.         'query_unbuffered'   => 'mysql_unbuffered_query',
  96.         'fetch_row'          => 'mysql_fetch_row',
  97.         'fetch_array'        => 'mysql_fetch_array',
  98.         'fetch_field'        => 'mysql_fetch_field',
  99.         'free_result'        => 'mysql_free_result',
  100.         'data_seek'          => 'mysql_data_seek',
  101.         'error'              => 'mysql_error',
  102.         'errno'              => 'mysql_errno',
  103.         'affected_rows'      => 'mysql_affected_rows',
  104.         'num_rows'           => 'mysql_num_rows',
  105.         'num_fields'         => 'mysql_num_fields',
  106.         'field_name'         => 'mysql_field_name',
  107.         'insert_id'          => 'mysql_insert_id',
  108.         'escape_string'      => 'mysql_real_escape_string',
  109.         'real_escape_string' => 'mysql_real_escape_string',
  110.         'close'              => 'mysql_close',
  111.         'client_encoding'    => 'mysql_client_encoding',
  112.         'ping'               => 'mysql_ping',
  113.     );
  114.  
  115.     /**
  116.     * The vBulletin registry object
  117.     *
  118.     * @var  vB_Registry
  119.     */
  120.     var $registry = null;
  121.  
  122.     /**
  123.     * Array of constants for use in fetch_array
  124.     *
  125.     * @var  array
  126.     */
  127.     var $fetchtypes = array(
  128.         DBARRAY_NUM   => MYSQL_NUM,
  129.         DBARRAY_ASSOC => MYSQL_ASSOC,
  130.         DBARRAY_BOTH  => MYSQL_BOTH
  131.     );
  132.  
  133.     /**
  134.     * Full name of the system
  135.     *
  136.     * @var  string
  137.     */
  138.     var $appname = 'vBulletin';
  139.  
  140.     /**
  141.     * Short name of the system
  142.     *
  143.     * @var  string
  144.     */
  145.     var $appshortname = 'vBulletin';
  146.  
  147.     /**
  148.     * Database name
  149.     *
  150.     * @var  string
  151.     */
  152.     var $database = null;
  153.  
  154.     /**
  155.     * Link variable. The connection to the master/write server.
  156.     *
  157.     * @var  string
  158.     */
  159.     var $connection_master = null;
  160.  
  161.     /**
  162.     * Link variable. The connection to the slave/read server(s).
  163.     *
  164.     * @var  string
  165.     */
  166.     var $connection_slave = null;
  167.  
  168.     /**
  169.     * Link variable. The connection last used.
  170.     *
  171.     * @var  string
  172.     */
  173.     var $connection_recent = null;
  174.  
  175.     /**
  176.     * Whether or not we will be using different connections for read and write queries
  177.     *
  178.     * @var  boolean
  179.     */
  180.     var $multiserver = false;
  181.  
  182.     /**
  183.     * Array of queries to be executed when the script shuts down
  184.     *
  185.     * @var  array
  186.     */
  187.     var $shutdownqueries = array();
  188.  
  189.     /**
  190.     * The contents of the most recent SQL query string.
  191.     *
  192.     * @var  string
  193.     */
  194.     var $sql = '';
  195.  
  196.     /**
  197.     * Whether or not to show and halt on database errors
  198.     *
  199.     * @var  boolean
  200.     */
  201.     var $reporterror = true;
  202.  
  203.     /**
  204.     * The text of the most recent database error message
  205.     *
  206.     * @var  string
  207.     */
  208.     var $error = '';
  209.  
  210.     /**
  211.     * The error number of the most recent database error message
  212.     *
  213.     * @var  integer
  214.     */
  215.     var $errno = '';
  216.  
  217.     /**
  218.     * SQL Query String
  219.     *
  220.     * @var  integer The maximum size of query string permitted by the master server
  221.     */
  222.     var $maxpacket = 0;
  223.  
  224.     /**
  225.     * Track lock status of tables. True if a table lock has been issued
  226.     *
  227.     * @var  bool
  228.     */
  229.     var $locked = false;
  230.  
  231.     /**
  232.     * Number of queries executed
  233.     *
  234.     * @var  integer The number of SQL queries run by the system
  235.     */
  236.     var $querycount = 0;
  237.  
  238.     /**
  239.     * Constructor. If x_real_escape_string() is available, switches to use that
  240.     * function over x_escape_string().
  241.     *
  242.     * @param    vB_Registry Registry object
  243.     */
  244.     function vB_Database(&$registry)
  245.     {
  246.         if (is_object($registry))
  247.         {
  248.             $this->registry =& $registry;
  249.         }
  250.         else
  251.         {
  252.             trigger_error("vB_Database::Registry object is not an object", E_USER_ERROR);
  253.         }
  254.     }
  255.  
  256.     /**
  257.     * Connects to the specified database server(s)
  258.     *
  259.     * @param    string  Name of the database that we will be using for select_db()
  260.     * @param    string  Name of the master (write) server - should be either 'localhost' or an IP address
  261.     * @param    integer Port for the master server
  262.     * @param    string  Username to connect to the master server
  263.     * @param    string  Password associated with the username for the master server
  264.     * @param    boolean Whether or not to use persistent connections to the master server
  265.     * @param    string  (Optional) Name of the slave (read) server - should be either left blank or set to 'localhost' or an IP address, but NOT the same as the servername for the master server
  266.     * @param    integer (Optional) Port of the slave server
  267.     * @param    string  (Optional) Username to connect to the slave server
  268.     * @param    string  (Optional) Password associated with the username for the slave server
  269.     * @param    boolean (Optional) Whether or not to use persistent connections to the slave server
  270.     * @param    string  (Optional) Parse given MySQL config file to set options
  271.     * @param    string  (Optional) Connection Charset MySQLi / PHP 5.1.0+ or 5.0.5+ / MySQL 4.1.13+ or MySQL 5.1.10+ Only
  272.     *
  273.     * @return   none
  274.     */
  275.     function connect($database, $w_servername, $w_port, $w_username, $w_password, $w_usepconnect = false, $r_servername = '', $r_port = 3306, $r_username = '', $r_password = '', $r_usepconnect = false, $configfile = '', $charset = '')
  276.     {
  277.         $this->database = $database;
  278.  
  279.         $w_port = $w_port ? $w_port : 3306;
  280.         $r_port = $r_port ? $r_port : 3306;
  281.  
  282.         $this->connection_master = $this->db_connect($w_servername, $w_port, $w_username, $w_password, $w_usepconnect, $configfile, $charset);
  283.         $this->multiserver = false;
  284.         $this->connection_slave =& $this->connection_master;
  285.  
  286.         if ($this->connection_master)
  287.         {
  288.             $this->select_db($this->database);
  289.         }
  290.     }
  291.  
  292.     /**
  293.     * Initialize database connection(s)
  294.     *
  295.     * Connects to the specified master database server, and also to the slave server if it is specified
  296.     *
  297.     * @param    string  Name of the database server - should be either 'localhost' or an IP address
  298.     * @param    integer Port of the database server (usually 3306)
  299.     * @param    string  Username to connect to the database server
  300.     * @param    string  Password associated with the username for the database server
  301.     * @param    boolean Whether or not to use persistent connections to the database server
  302.     * @param    string  Not applicable; config file for MySQLi only
  303.     * @param    string  Force connection character set (to prevent collation errors)
  304.     *
  305.     * @return   boolean
  306.     */
  307.     function db_connect($servername, $port, $username, $password, $usepconnect, $configfile = '', $charset = '')
  308.     {
  309.         if (function_exists('catch_db_error'))
  310.         {
  311.             set_error_handler('catch_db_error');
  312.         }
  313.  
  314.         // catch_db_error will handle exiting, no infinite loop here
  315.         do
  316.         {
  317.             $link = $this->functions[$usepconnect ? 'pconnect' : 'connect']("$servername:$port", $username, $password);
  318.         }
  319.         while ($link == false AND $this->reporterror);
  320.  
  321.         restore_error_handler();
  322.  
  323.         if (!empty($charset))
  324.         {
  325.             if (function_exists('mysql_set_charset'))
  326.             {
  327.                 mysql_set_charset($charset);
  328.             }
  329.             else
  330.             {
  331.                 $this->sql = "SET NAMES $charset";
  332.                 $this->execute_query(true, $link);
  333.             }
  334.         }
  335.  
  336.         return $link;
  337.     }
  338.  
  339.     /**
  340.     * Selects a database to use
  341.     *
  342.     * @param    string  The name of the database located on the database server(s)
  343.     *
  344.     * @return   boolean
  345.     */
  346.     function select_db($database = '')
  347.     {
  348.         if ($database != '')
  349.         {
  350.             $this->database = $database;
  351.         }
  352.  
  353.         if ($check_write = @$this->select_db_wrapper($this->database, $this->connection_master))
  354.         {
  355.             $this->connection_recent =& $this->connection_master;
  356.             return true;
  357.         }
  358.         else
  359.         {
  360.             $this->connection_recent =& $this->connection_master;
  361.             if (!file_exists(DIR . '/install/install.php'))
  362.             {
  363.                 $this->halt('Cannot use database ' . $this->database);
  364.             }
  365.             return false;
  366.         }
  367.     }
  368.  
  369.     /**
  370.     * Simple wrapper for select_db(), to allow argument order changes
  371.     *
  372.     * @param    string  Database name
  373.     * @param    integer Link identifier
  374.     *
  375.     * @return   boolean
  376.     */
  377.     function select_db_wrapper($database = '', $link = null)
  378.     {
  379.         return $this->functions['select_db']($database, $link);
  380.     }
  381.  
  382.     /**
  383.     * Forces the sql_mode varaible to a specific mode. Certain modes may be
  384.     * incompatible with vBulletin. Applies to MySQL 4.1+.
  385.     *
  386.     * @param    string  The mode to set the sql_mode variable to
  387.     */
  388.     function force_sql_mode($mode)
  389.     {
  390.         $reset_errors = $this->reporterror;
  391.         if ($reset_errors)
  392.         {
  393.             $this->hide_errors();
  394.         }
  395.  
  396.         $this->query_write("SET @@sql_mode = '" . $this->escape_string($mode) . "'");
  397.  
  398.         if ($reset_errors)
  399.         {
  400.             $this->show_errors();
  401.         }
  402.     }
  403.  
  404.     /**
  405.     * Executes an SQL query through the specified connection
  406.     *
  407.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  408.     * @param    string  The connection ID to the database server
  409.     *
  410.     * @return   string
  411.     */
  412.     function &execute_query($buffered = true, &$link)
  413.     {
  414.         $this->connection_recent =& $link;
  415.         $this->querycount++;
  416.  
  417.         if ($queryresult = $this->functions[$buffered ? 'query' : 'query_unbuffered']($this->sql, $link))
  418.         {
  419.             // unset $sql to lower memory .. this isn't an error, so it's not needed
  420.             $this->sql = '';
  421.  
  422.             return $queryresult;
  423.         }
  424.         else
  425.         {
  426.             $this->halt();
  427.  
  428.             // unset $sql to lower memory .. error will have already been thrown
  429.             $this->sql = '';
  430.         }
  431.     }
  432.  
  433.     /**
  434.     * Executes a data-writing SQL query through the 'master' database connection
  435.     *
  436.     * @param    string  The text of the SQL query to be executed
  437.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  438.     *
  439.     * @return   string
  440.     */
  441.     function query_write($sql, $buffered = true)
  442.     {
  443.         $this->sql =& $sql;
  444.         return $this->execute_query($buffered, $this->connection_master);
  445.     }
  446.  
  447.     /**
  448.     * Executes a data-reading SQL query through the 'master' database connection
  449.     * we don't know if the 'read' database is up to date so be on the safe side
  450.     *
  451.     * @param    string  The text of the SQL query to be executed
  452.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  453.     *
  454.     * @return   string
  455.     */
  456.     function query_read($sql, $buffered = true)
  457.     {
  458.         $this->sql =& $sql;
  459.         return $this->execute_query($buffered, $this->connection_master);
  460.     }
  461.  
  462.     /**
  463.     * Executes a data-reading SQL query through the 'slave' database connection
  464.     *
  465.     * @param    string  The text of the SQL query to be executed
  466.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  467.     *
  468.     * @return   string
  469.     */
  470.     function query_read_slave($sql, $buffered = true)
  471.     {
  472.         $this->sql =& $sql;
  473.         return $this->execute_query($buffered, $this->connection_master);
  474.     }
  475.  
  476.     /**
  477.     * Executes an SQL query, using either the write connection
  478.     *
  479.     * @deprecated   Deprecated as of 3.6. Use query_(read/write)
  480.     *
  481.     * @param    string  The text of the SQL query to be executed
  482.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  483.     *
  484.     * @return   string
  485.     */
  486.     function query($sql, $buffered = true)
  487.     {
  488.         $this->sql =& $sql;
  489.         return $this->execute_query($buffered, $this->connection_master);
  490.     }
  491.  
  492.     /**
  493.     * Executes a data-reading SQL query, then returns an array of the data from the first row from the result set
  494.     *
  495.     * @param    string  The text of the SQL query to be executed
  496.     * @param    string  One of (NUM, ASSOC, BOTH)
  497.     *
  498.     * @return   array
  499.     */
  500.     function &query_first($sql, $type = DBARRAY_ASSOC)
  501.     {
  502.         $this->sql =& $sql;
  503.         $queryresult = $this->execute_query(true, $this->connection_master);
  504.         $returnarray = $this->fetch_array($queryresult, $type);
  505.         $this->free_result($queryresult);
  506.         return $returnarray;
  507.     }
  508.  
  509.     /**
  510.     * Executes a FOUND_ROWS query to get the results of SQL_CALC_FOUND_ROWS
  511.     *
  512.     * @return   integer
  513.     */
  514.     function found_rows()
  515.     {
  516.         $this->sql = "SELECT FOUND_ROWS()";
  517.         $queryresult = $this->execute_query(true, $this->connection_recent);
  518.         $returnarray = $this->fetch_array($queryresult, DBARRAY_NUM);
  519.         $this->free_result($queryresult);
  520.  
  521.         return intval($returnarray[0]);
  522.     }
  523.  
  524.     /**
  525.     * Executes a data-reading SQL query against the slave server, then returns an array of the data from the first row from the result set
  526.     *
  527.     * @param    string  The text of the SQL query to be executed
  528.     * @param    string  One of (NUM, ASSOC, BOTH)
  529.     *
  530.     * @return   array
  531.     */
  532.     function &query_first_slave($sql, $type = DBARRAY_ASSOC)
  533.     {
  534.         $returnarray = $this->query_first($sql, $type);
  535.         return $returnarray;
  536.     }
  537.  
  538.     /**
  539.     * Executes an INSERT INTO query, using extended inserts if possible
  540.     *
  541.     * @param    string  Name of the table into which data should be inserted
  542.     * @param    string  Comma-separated list of the fields to affect
  543.     * @param    array   Array of SQL values
  544.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  545.     *
  546.     * @return   mixed
  547.     */
  548.     function &query_insert($table, $fields, &$values, $buffered = true)
  549.     {
  550.         return $this->insert_multiple("INSERT INTO $table $fields VALUES", $values, $buffered);
  551.     }
  552.  
  553.     /**
  554.     * Executes a REPLACE INTO query, using extended inserts if possible
  555.     *
  556.     * @param    string  Name of the table into which data should be inserted
  557.     * @param    string  Comma-separated list of the fields to affect
  558.     * @param    array   Array of SQL values
  559.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  560.     *
  561.     * @return   mixed
  562.     */
  563.     function &query_replace($table, $fields, &$values, $buffered = true)
  564.     {
  565.         return $this->insert_multiple("REPLACE INTO $table $fields VALUES", $values, $buffered);
  566.     }
  567.  
  568.     /**
  569.     * Executes an INSERT or REPLACE query with multiple values, splitting large queries into manageable chunks based on $this->maxpacket
  570.     *
  571.     * @param    string  The text of the first part of the SQL query to be executed - example "INSERT INTO table (field1, field2) VALUES"
  572.     * @param    mixed   The values to be inserted. Example: (0 => "('value1', 'value2')", 1 => "('value3', 'value4')")
  573.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  574.     *
  575.     * @return   mixed
  576.     */
  577.     function insert_multiple($sql, &$values, $buffered)
  578.     {
  579.         if ($this->maxpacket == 0)
  580.         {
  581.             // must do a READ query on the WRITE link here!
  582.             $vars = $this->query_write("SHOW VARIABLES LIKE 'max_allowed_packet'");
  583.             $var = $this->fetch_row($vars);
  584.             $this->maxpacket = $var[1];
  585.             $this->free_result($vars);
  586.         }
  587.  
  588.         $i = 0;
  589.         $num_values = sizeof($values);
  590.         $this->sql = $sql;
  591.  
  592.         while ($i < $num_values)
  593.         {
  594.             $sql_length = strlen($this->sql);
  595.             $value_length = strlen("\r\n" . $values["$i"] . ",");
  596.  
  597.             if (($sql_length + $value_length) < $this->maxpacket)
  598.             {
  599.                 $this->sql .= "\r\n" . $values["$i"] . ",";
  600.                 unset($values["$i"]);
  601.                 $i++;
  602.             }
  603.             else
  604.             {
  605.                 $this->sql = (substr($this->sql, -1) == ',') ? substr($this->sql, 0, -1) : $this->sql;
  606.                 $this->execute_query($buffered, $this->connection_master);
  607.                 $this->sql = $sql;
  608.             }
  609.         }
  610.         if ($this->sql != $sql)
  611.         {
  612.             $this->sql = (substr($this->sql, -1) == ',') ? substr($this->sql, 0, -1) : $this->sql;
  613.             $this->execute_query($buffered, $this->connection_master);
  614.         }
  615.  
  616.         if (sizeof($values) == 1)
  617.         {
  618.             return $this->insert_id();
  619.         }
  620.         else
  621.         {
  622.             return true;
  623.         }
  624.     }
  625.  
  626.     /**
  627.     * Registers an SQL query to be executed at shutdown time. If shutdown functions are disabled, the query is run immediately.
  628.     *
  629.     * @param    string  The text of the SQL query to be executed
  630.     * @param    mixed   (Optional) Allows particular shutdown queries to be labelled
  631.     *
  632.     * @return   boolean
  633.     */
  634.     function shutdown_query($sql, $arraykey = -1)
  635.     {
  636.         if ($arraykey === -1)
  637.         {
  638.             $this->shutdownqueries[] = $sql;
  639.             return true;
  640.         }
  641.         else
  642.         {
  643.             $this->shutdownqueries["$arraykey"] = $sql;
  644.             return true;
  645.         }
  646.     }
  647.  
  648.     /**
  649.     * Returns the number of rows contained within a query result set
  650.     *
  651.     * @param    string  The query result ID we are dealing with
  652.     *
  653.     * @return   integer
  654.     */
  655.     function num_rows($queryresult)
  656.     {
  657.         return @$this->functions['num_rows']($queryresult);
  658.     }
  659.  
  660.     /**
  661.     * Returns the number of fields contained within a query result set
  662.     *
  663.     * @param    string  The query result ID we are dealing with
  664.     *
  665.     * @return   integer
  666.     */
  667.     function num_fields($queryresult)
  668.     {
  669.         return @$this->functions['num_fields']($queryresult);
  670.     }
  671.  
  672.     /**
  673.     * Returns the name of a field from within a query result set
  674.     *
  675.     * @param    string  The query result ID we are dealing with
  676.     * @param    integer The index position of the field
  677.     *
  678.     * @return   string
  679.     */
  680.     function field_name($queryresult, $index)
  681.     {
  682.         return @$this->functions['field_name']($queryresult, $index);
  683.     }
  684.  
  685.     /**
  686.     * Returns the ID of the item just inserted into an auto-increment field
  687.     *
  688.     * @return   integer
  689.     */
  690.     function insert_id()
  691.     {
  692.         return @$this->functions['insert_id']($this->connection_master);
  693.     }
  694.  
  695.     /**
  696.     * Returns the name of the character set
  697.     *
  698.     * @return   string
  699.     */
  700.     function client_encoding()
  701.     {
  702.         return @$this->functions['client_encoding']($this->connection_master);
  703.     }
  704.  
  705.     /**
  706.     * Closes the connection to the database server
  707.     *
  708.     * @return   integer
  709.     */
  710.     function close()
  711.     {
  712.         return @$this->functions['close']($this->connection_master);
  713.     }
  714.  
  715.     /**
  716.     * Escapes a string to make it safe to be inserted into an SQL query
  717.     *
  718.     * @param    string  The string to be escaped
  719.     *
  720.     * @return   string
  721.     */
  722.     function escape_string($string)
  723.     {
  724.         return $this->functions['real_escape_string']($string, $this->connection_master);
  725.     }
  726.  
  727.     /**
  728.     * Escapes a string using the appropriate escape character for the RDBMS for use in LIKE conditions
  729.     *
  730.     * @param    string  The string to be escaped
  731.     *
  732.     * @return   string
  733.     */
  734.     function escape_string_like($string)
  735.     {
  736.         return str_replace(array('%', '_') , array('\%' , '\_') , $this->escape_string($string));
  737.     }
  738.  
  739.     /**
  740.     * Takes a piece of data and prepares it to be put into an SQL query by adding quotes etc.
  741.     *
  742.     * @param    mixed   The data to be used
  743.     *
  744.     * @return   mixed   The prepared data
  745.     */
  746.     function sql_prepare($value)
  747.     {
  748.         if (is_string($value))
  749.         {
  750.             return "'" . $this->escape_string($value) . "'";
  751.         }
  752.         else if (is_numeric($value) AND $value + 0 == $value)
  753.         {
  754.             return $value;
  755.         }
  756.         else if (is_bool($value))
  757.         {
  758.             return $value ? 1 : 0;
  759.         }
  760.         else if (is_null($value))
  761.         {
  762.             return "''";
  763.         }
  764.         else if (is_array($value))
  765.         {
  766.             foreach ($value as $key => $item)
  767.             {
  768.                 $value[$key] = $this->sql_prepare($item);
  769.             }
  770.             return $value;
  771.         }
  772.         else
  773.         {
  774.             return "'" . $this->escape_string($value) . "'";
  775.         }
  776.     }
  777.  
  778.     /**
  779.     * Fetches a row from a query result and returns the values from that row as an array
  780.     *
  781.     * The value of $type defines whether the array will have numeric or associative keys, or both
  782.     *
  783.     * @param    string  The query result ID we are dealing with
  784.     * @param    integer One of DBARRAY_ASSOC / DBARRAY_NUM / DBARRAY_BOTH
  785.     *
  786.     * @return   array
  787.     */
  788.     function fetch_array($queryresult, $type = DBARRAY_ASSOC)
  789.     {
  790.         static $hook_code = false;
  791.  
  792.         if ($hook_code === false AND class_exists('vBulletinHook', false))
  793.         {
  794.             $hook_code['pre_fetch'] = vBulletinHook::fetch_hook('database_pre_fetch_array');
  795.             $hook_code['post_fetch'] = vBulletinHook::fetch_hook('database_post_fetch_array');
  796.         }
  797.  
  798.         if ($hook_code['pre_fetch'])
  799.         {
  800.             $result = false;
  801.  
  802.             eval($hook_code['pre_fetch']);
  803.  
  804.             if ($result)
  805.             {
  806.                 return $result;
  807.             }
  808.         }
  809.  
  810.         $result = @$this->functions['fetch_array']($queryresult, $this->fetchtypes["$type"]);
  811.  
  812.         if ($hook_code['post_fetch'])
  813.         {
  814.             eval($hook_code['post_fetch']);
  815.         }
  816.  
  817.         return $result;
  818.     }
  819.  
  820.     /**
  821.     * Fetches a row from a query result and returns the values from that row as an array with numeric keys
  822.     *
  823.     * @param    string  The query result ID we are dealing with
  824.     *
  825.     * @return   array
  826.     */
  827.     function fetch_row($queryresult)
  828.     {
  829.         static $hook_code = false;
  830.  
  831.         if ($hook_code === false AND class_exists('vBulletinHook', false))
  832.         {
  833.             $hook_code['pre_fetch'] = vBulletinHook::fetch_hook('database_pre_fetch_row');
  834.             $hook_code['post_fetch'] = vBulletinHook::fetch_hook('database_post_fetch_row');
  835.         }
  836.  
  837.         if ($hook_code['pre_fetch'])
  838.         {
  839.             $result = false;
  840.  
  841.             eval($hook_code['pre_fetch']);
  842.  
  843.             if ($result)
  844.             {
  845.                 return $result;
  846.             }
  847.         }
  848.  
  849.         $result = @$this->functions['fetch_row']($queryresult);
  850.  
  851.         if ($hook_code['post_fetch'])
  852.         {
  853.             eval($hook_code['post_fetch']);
  854.         }
  855.  
  856.         return $result;
  857.     }
  858.  
  859.     /**
  860.     * Fetches a row information from a query result and returns the values from that row as an array
  861.     *
  862.     * @param    string  The query result ID we are dealing with
  863.     *
  864.     * @return   array
  865.     */
  866.     function fetch_field($queryresult)
  867.     {
  868.         static $hook_code = false;
  869.  
  870.         if ($hook_code === false AND class_exists('vBulletinHook', false))
  871.         {
  872.             $hook_code['pre_fetch'] = vBulletinHook::fetch_hook('database_pre_fetch_field');
  873.             $hook_code['post_fetch'] = vBulletinHook::fetch_hook('database_post_fetch_field');
  874.         }
  875.  
  876.         if ($hook_code['pre_fetch'])
  877.         {
  878.             $result = false;
  879.  
  880.             eval($hook_code['pre_fetch']);
  881.  
  882.             if ($result)
  883.             {
  884.                 return $result;
  885.             }
  886.         }
  887.  
  888.         $result = @$this->functions['fetch_field']($queryresult);
  889.  
  890.         if ($hook_code['post_fetch'])
  891.         {
  892.             eval($hook_code['post_fetch']);
  893.         }
  894.  
  895.         return $result;
  896.     }
  897.  
  898.     /**
  899.     * Moves the internal result pointer within a query result set
  900.     *
  901.     * @param    string  The query result ID we are dealing with
  902.     * @param    integer The position to which to move the pointer (first position is 0)
  903.     *
  904.     * @return   boolean
  905.     */
  906.     function data_seek($queryresult, $index)
  907.     {
  908.         return @$this->functions['data_seek']($queryresult, $index);
  909.     }
  910.  
  911.     /**
  912.     * Frees all memory associated with the specified query result
  913.     *
  914.     * @param    string  The query result ID we are dealing with
  915.     *
  916.     * @return   boolean
  917.     */
  918.     function free_result($queryresult)
  919.     {
  920.         $this->sql = '';
  921.         return @$this->functions['free_result']($queryresult);
  922.     }
  923.  
  924.     /**
  925.     * Retuns the number of rows affected by the most recent insert/replace/update query
  926.     *
  927.     * @return   integer
  928.     */
  929.     function affected_rows()
  930.     {
  931.         $this->rows = $this->functions['affected_rows']($this->connection_recent);
  932.         return $this->rows;
  933.     }
  934.  
  935.     /**
  936.     * Ping connection and reconnect
  937.     * Don't use this in a manner that could cause a loop condition
  938.     *
  939.     */
  940.     function ping()
  941.     {
  942.         if (!@$this->functions['ping']($this->connection_master))
  943.         {
  944.             $this->close();
  945.             // make database connection
  946.             $this->connect(
  947.                 $this->registry->config['Database']['dbname'],
  948.                 $this->registry->config['MasterServer']['servername'],
  949.                 $this->registry->config['MasterServer']['port'],
  950.                 $this->registry->config['MasterServer']['username'],
  951.                 $this->registry->config['MasterServer']['password'],
  952.                 $this->registry->config['MasterServer']['usepconnect'],
  953.                 $this->registry->config['SlaveServer']['servername'],
  954.                 $this->registry->config['SlaveServer']['port'],
  955.                 $this->registry->config['SlaveServer']['username'],
  956.                 $this->registry->config['SlaveServer']['password'],
  957.                 $this->registry->config['SlaveServer']['usepconnect'],
  958.                 $this->registry->config['Mysqli']['ini_file'],
  959.                 (isset($this->registry->config['Mysqli']['charset']) ? $this->registry->config['Mysqli']['charset'] : '')
  960.             );
  961.         }
  962.     }
  963.  
  964.     /**
  965.     * Lock tables
  966.     *
  967.     * @param    mixed   List of tables to lock
  968.     * @param    string  Type of lock to perform
  969.     *
  970.     */
  971.     function lock_tables($tablelist)
  972.     {
  973.         if (!empty($tablelist) AND is_array($tablelist))
  974.         {
  975.             // Don't lock tables if we know we might get stuck with them locked (pconnect = true)
  976.             // mysqli doesn't support pconnect! YAY!
  977.             if (strtolower($this->registry->config['Database']['dbtype']) != 'mysqli' AND $this->registry->config['MasterServer']['usepconnect'])
  978.             {
  979.                 return;
  980.             }
  981.  
  982.             $sql = '';
  983.             foreach($tablelist AS $name => $type)
  984.             {
  985.                 $sql .= (!empty($sql) ? ', ' : '') . TABLE_PREFIX . $name . " " . $type;
  986.             }
  987.  
  988.             $this->query_write("LOCK TABLES $sql");
  989.             $this->locked = true;
  990.  
  991.         }
  992.     }
  993.  
  994.     /**
  995.     * Unlock tables
  996.     *
  997.     */
  998.     function unlock_tables()
  999.     {
  1000.         # must be called from exec_shutdown as tables can get stuck locked if pconnects are enabled
  1001.         # note: the above case never actually happens as we skip the lock if pconnects are enabled (to be safe) =)
  1002.         if ($this->locked)
  1003.         {
  1004.             $this->query_write("UNLOCK TABLES");
  1005.         }
  1006.     }
  1007.  
  1008.     /**
  1009.     * Returns the text of the error message from previous database operation
  1010.     *
  1011.     * @return   string
  1012.     */
  1013.     function error()
  1014.     {
  1015.         if ($this->connection_recent === null)
  1016.         {
  1017.             $this->error = '';
  1018.         }
  1019.         else
  1020.         {
  1021.             $this->error = $this->functions['error']($this->connection_recent);
  1022.         }
  1023.         return $this->error;
  1024.     }
  1025.  
  1026.     /**
  1027.     * Returns the numerical value of the error message from previous database operation
  1028.     *
  1029.     * @return   integer
  1030.     */
  1031.     function errno()
  1032.     {
  1033.         if ($this->connection_recent === null)
  1034.         {
  1035.             $this->errno = 0;
  1036.         }
  1037.         else
  1038.         {
  1039.             $this->errno = $this->functions['errno']($this->connection_recent);
  1040.         }
  1041.         return $this->errno;
  1042.     }
  1043.  
  1044.     /**
  1045.     * Switches database error display ON
  1046.     */
  1047.     function show_errors()
  1048.     {
  1049.         $this->reporterror = true;
  1050.     }
  1051.  
  1052.     /**
  1053.     * Switches database error display OFF
  1054.     */
  1055.     function hide_errors()
  1056.     {
  1057.         $this->reporterror = false;
  1058.     }
  1059.  
  1060.     /**
  1061.     * Halts execution of the entire system and displays an error message
  1062.     *
  1063.     * @param    string  Text of the error message. Leave blank to use $this->sql as error text.
  1064.     *
  1065.     * @return   integer
  1066.     */
  1067.     function halt($errortext = '')
  1068.     {
  1069.         global $vbulletin;
  1070.  
  1071.         if ($this->connection_recent)
  1072.         {
  1073.             $this->error = $this->error($this->connection_recent);
  1074.             $this->errno = $this->errno($this->connection_recent);
  1075.         }
  1076.  
  1077.         if ($this->reporterror)
  1078.         {
  1079.             if ($errortext == '')
  1080.             {
  1081.                 $this->sql = "Invalid SQL:\r\n" . chop($this->sql) . ';';
  1082.                 $errortext =& $this->sql;
  1083.             }
  1084.  
  1085.             // Try and stop e-mail flooding.
  1086.             if (!$vbulletin->options['disableerroremail'])
  1087.             {
  1088.                 if (!$vbulletin->options['safeupload'])
  1089.                 {
  1090.                     $tempdir = ini_get('upload_tmp_dir');
  1091.                 }
  1092.                 else
  1093.                 {
  1094.                     $tempdir = $vbulletin->options['tmppath'] . '/';
  1095.                 }
  1096.  
  1097.                 $unique = md5(COOKIE_SALT);
  1098.                 $tempfile = $tempdir."zdberr$unique.dat";
  1099.  
  1100.                 /* If its less than a minute since the last e-mail
  1101.                 and the error code is the same as last time, disable e-mail */
  1102.                 if ($data = @file_get_contents($tempfile))
  1103.                 {
  1104.                     $errc = intval(substr($data, 10));
  1105.                     $time = intval(substr($data, 0, 10));
  1106.                     if ($time AND (TIMENOW - $time) < 60
  1107.                         AND intval($this->errno) == $errc)
  1108.                     {
  1109.                         $vbulletin->options['disableerroremail'] = true;
  1110.                     }
  1111.                     else
  1112.                     {
  1113.                         $data = TIMENOW.intval($this->errno);
  1114.                         @file_put_contents($tempfile, $data);
  1115.                     }
  1116.                 }
  1117.                 else
  1118.                 {
  1119.                     $data = TIMENOW.intval($this->errno);
  1120.                     @file_put_contents($tempfile, $data);
  1121.                 }
  1122.             }
  1123.  
  1124.             $vboptions      =& $vbulletin->options;
  1125.             $technicalemail =& $vbulletin->config['Database']['technicalemail'];
  1126.             $bbuserinfo     =& $vbulletin->userinfo;
  1127.             $requestdate    = date('l, F jS Y @ h:i:s A', TIMENOW);
  1128.             $date           = date('l, F jS Y @ h:i:s A');
  1129.             $scriptpath     = str_replace('&amp;', '&', $vbulletin->scriptpath);
  1130.             $referer        = REFERRER;
  1131.             $ipaddress      = IPADDRESS;
  1132.             $classname      = get_class($this);
  1133.  
  1134.             if ($this->connection_recent)
  1135.             {
  1136.                 $this->hide_errors();
  1137.                 list($mysqlversion) = $this->query_first("SELECT VERSION() AS version", DBARRAY_NUM);
  1138.                 $this->show_errors();
  1139.             }
  1140.  
  1141.             $display_db_error = (VB_AREA == 'Upgrade' OR VB_AREA == 'Install' OR $vbulletin->userinfo['permissions']['adminpermissions'] & $vbulletin->bf_ugp_adminpermissions['cancontrolpanel']);
  1142.  
  1143.             // Hide the MySQL Version if its going in the source
  1144.             if (!$display_db_error)
  1145.             {
  1146.                 $mysqlversion = '';
  1147.             }
  1148.  
  1149.             eval('$message = "' . str_replace('"', '\"', file_get_contents(DIR . '/includes/database_error_message.html')) . '";');
  1150.  
  1151.             // add a backtrace to the message
  1152.             if ($vbulletin->debug)
  1153.             {
  1154.                 $trace = debug_backtrace();
  1155.                 $trace_output = "\n";
  1156.  
  1157.                 foreach ($trace AS $index => $trace_item)
  1158.                 {
  1159.                     $param = (in_array($trace_item['function'], array('require', 'require_once', 'include', 'include_once')) ? $trace_item['args'][0] : '');
  1160.  
  1161.                     // remove path
  1162.                     $param = str_replace(DIR, '[path]', $param);
  1163.                     $trace_item['file'] = str_replace(DIR, '[path]', $trace_item['file']);
  1164.  
  1165.                     $trace_output .= "#$index $trace_item[class]$trace_item[type]$trace_item[function]($param) called in $trace_item[file] on line $trace_item[line]\n";
  1166.                 }
  1167.  
  1168.                 $message .= "\n\nStack Trace:\n$trace_output\n";
  1169.             }
  1170.  
  1171.             require_once(DIR . '/includes/functions_log_error.php');
  1172.             if (function_exists('log_vbulletin_error'))
  1173.             {
  1174.                 log_vbulletin_error($message, 'database');
  1175.             }
  1176.  
  1177.             if ($technicalemail != '' AND !$vbulletin->options['disableerroremail'] AND verify_email_vbulletin_error($this->errno, 'database'))
  1178.             {
  1179.                 // If vBulletinHook is defined then we know that options are loaded, so we can then use vbmail
  1180.                 if (class_exists('vBulletinHook', false))
  1181.                 {
  1182.                     @vbmail($technicalemail, $this->appshortname . ' Database Error!', $message, true, $technicalemail);
  1183.                 }
  1184.                 else
  1185.                 {
  1186.                     @mail($technicalemail, $this->appshortname . ' Database Error!', preg_replace("#(\r\n|\r|\n)#s", (@ini_get('sendmail_path') === '') ? "\r\n" : "\n", $message), "From: $technicalemail");
  1187.                 }
  1188.             }
  1189.  
  1190.             if (defined('STDIN'))
  1191.             {
  1192.                 echo $message;
  1193.                 exit;
  1194.             }
  1195.  
  1196.             // send ajax reponse after sending error email
  1197.             if ($vbulletin->GPC['ajax'])
  1198.             {
  1199.                 require_once(DIR . '/includes/class_xml.php');
  1200.                 $xml = new vB_AJAX_XML_Builder($vbulletin, 'text/xml');
  1201.  
  1202.                 $error = '<p>Database Error</p>';
  1203.                 if ($vbulletin->debug OR VB_AREA == 'Upgrade')
  1204.                 {
  1205.                     $error .= "\r\n\r\n$errortext";
  1206.                     $error .= "\r\n\r\n{$this->error}";
  1207.                 }
  1208.  
  1209.                 eval('$ajaxmessage = "' . str_replace('"', '\"', file_get_contents(DIR . '/includes/database_error_message_ajax.html')) . '";');
  1210.  
  1211.                 $xml->add_group('errors');
  1212.                     $xml->add_tag('error', $error);
  1213.                     $xml->add_tag('error_html', $ajaxmessage);
  1214.                 $xml->close_group('errors');
  1215.  
  1216.                 $xml->print_xml();
  1217.             }
  1218.  
  1219.             if (!headers_sent())
  1220.             {
  1221.                 if (SAPI_NAME == 'cgi' OR SAPI_NAME == 'cgi-fcgi')
  1222.                 {
  1223.                     header('Status: 503 Service Unavailable');
  1224.                 }
  1225.                 else
  1226.                 {
  1227.                     header($_SERVER['SERVER_PROTOCOL'] . ' 503 Service Unavailable');
  1228.                 }
  1229.             }
  1230.  
  1231.             if ($display_db_error)
  1232.             {
  1233.                 // display error message on screen
  1234.                 $message = '<form><textarea rows="15" cols="70" wrap="off" id="message">' . htmlspecialchars_uni($message) . '</textarea></form>';
  1235.             }
  1236.             else if ($vbulletin->debug)
  1237.             {
  1238.                 // display hidden error message
  1239.                 $message = "\r\n<!--\r\n" . htmlspecialchars_uni($message) . "\r\n-->\r\n";
  1240.             }
  1241.             else
  1242.             {
  1243.                 $message = '';
  1244.             }
  1245.  
  1246.             if ($vbulletin->options['bburl'])
  1247.             {
  1248.                 $imagepath = $vbulletin->options['bburl'];
  1249.             }
  1250.             else
  1251.             {
  1252.                 // this might not work with too many slashes in the archive
  1253.                 $imagepath = (VB_AREA == 'Forum' ? '.' : '..');
  1254.             }
  1255.  
  1256.             eval('$message = "' . str_replace('"', '\"', file_get_contents(DIR . '/includes/database_error_page.html')) . '";');
  1257.  
  1258.             // This is needed so IE doesn't show the pretty error messages
  1259.             $message .= str_repeat(' ', 512);
  1260.             die($message);
  1261.         }
  1262.         else if (!empty($errortext))
  1263.         {
  1264.             $this->error = $errortext;
  1265.         }
  1266.     }
  1267. }
  1268.  
  1269. // #############################################################################
  1270. // MySQLi Database Class
  1271.  
  1272. /**
  1273. * Class to interface with a MySQL 4.1 database
  1274. *
  1275. * This class also handles data replication between a master and slave(s) servers
  1276. *
  1277. * @package  vBulletin
  1278. * @version  $Revision: 62833 $
  1279. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  1280. */
  1281. class vB_Database_MySQLi extends vB_Database
  1282. {
  1283.     /**
  1284.     * Array of function names, mapping a simple name to the RDBMS specific function name
  1285.     *
  1286.     * @var  array
  1287.     */
  1288.     var $functions = array(
  1289.         'connect'            => 'mysqli_real_connect',
  1290.         'pconnect'           => 'mysqli_real_connect', // mysqli doesn't support persistent connections THANK YOU!
  1291.         'select_db'          => 'mysqli_select_db',
  1292.         'query'              => 'mysqli_query',
  1293.         'query_unbuffered'   => 'mysqli_unbuffered_query',
  1294.         'fetch_row'          => 'mysqli_fetch_row',
  1295.         'fetch_array'        => 'mysqli_fetch_array',
  1296.         'fetch_field'        => 'mysqli_fetch_field',
  1297.         'free_result'        => 'mysqli_free_result',
  1298.         'data_seek'          => 'mysqli_data_seek',
  1299.         'error'              => 'mysqli_error',
  1300.         'errno'              => 'mysqli_errno',
  1301.         'affected_rows'      => 'mysqli_affected_rows',
  1302.         'num_rows'           => 'mysqli_num_rows',
  1303.         'num_fields'         => 'mysqli_num_fields',
  1304.         'field_name'         => 'mysqli_field_tell',
  1305.         'insert_id'          => 'mysqli_insert_id',
  1306.         'escape_string'      => 'mysqli_real_escape_string',
  1307.         'real_escape_string' => 'mysqli_real_escape_string',
  1308.         'close'              => 'mysqli_close',
  1309.         'client_encoding'    => 'mysqli_client_encoding',
  1310.         'ping'               => 'mysqli_ping',
  1311.     );
  1312.  
  1313.     /**
  1314.     * Array of constants for use in fetch_array
  1315.     *
  1316.     * @var  array
  1317.     */
  1318.     var $fetchtypes = array(
  1319.         DBARRAY_NUM   => MYSQLI_NUM,
  1320.         DBARRAY_ASSOC => MYSQLI_ASSOC,
  1321.         DBARRAY_BOTH  => MYSQLI_BOTH
  1322.     );
  1323.  
  1324.     /**
  1325.     * Initialize database connection(s)
  1326.     *
  1327.     * Connects to the specified master database server, and also to the slave server if it is specified
  1328.     *
  1329.     * @param    string  Name of the database server - should be either 'localhost' or an IP address
  1330.     * @param    integer Port of the database server - usually 3306
  1331.     * @param    string  Username to connect to the database server
  1332.     * @param    string  Password associated with the username for the database server
  1333.     * @param    string  Persistent Connections - Not supported with MySQLi
  1334.     * @param    string  Configuration file from config.php.ini (my.ini / my.cnf)
  1335.     * @param    string  Mysqli Connection Charset PHP 5.1.0+ or 5.0.5+ / MySQL 4.1.13+ or MySQL 5.1.10+ Only
  1336.     *
  1337.     * @return   object  Mysqli Resource
  1338.     */
  1339.     function db_connect($servername, $port, $username, $password, $usepconnect, $configfile = '', $charset = '')
  1340.     {
  1341.         if (function_exists('catch_db_error'))
  1342.         {
  1343.             set_error_handler('catch_db_error');
  1344.         }
  1345.  
  1346.         $link = mysqli_init();
  1347.         # Set Options Connection Options
  1348.         if (!empty($configfile))
  1349.         {
  1350.             mysqli_options($link, MYSQLI_READ_DEFAULT_FILE, $configfile);
  1351.         }
  1352.  
  1353.         // this will execute at most 5 times, see catch_db_error()
  1354.         do
  1355.         {
  1356.             $connect = $this->functions['connect']($link, $servername, $username, $password, '', $port);
  1357.         }
  1358.         while ($connect == false AND $this->reporterror);
  1359.  
  1360.         restore_error_handler();
  1361.  
  1362.         if (!empty($charset))
  1363.         {
  1364.             if (function_exists('mysqli_set_charset'))
  1365.             {
  1366.                 mysqli_set_charset($link, $charset);
  1367.             }
  1368.             else
  1369.             {
  1370.                 $this->sql = "SET NAMES $charset";
  1371.                 $this->execute_query(true, $link);
  1372.             }
  1373.         }
  1374.  
  1375.         return (!$connect) ? false : $link;
  1376.     }
  1377.  
  1378.     /**
  1379.     * Executes an SQL query through the specified connection
  1380.     *
  1381.     * @param    boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  1382.     * @param    string  The connection ID to the database server
  1383.     *
  1384.     * @return   string
  1385.     */
  1386.     function &execute_query($buffered = true, &$link)
  1387.     {
  1388.         $this->connection_recent =& $link;
  1389.         $this->querycount++;
  1390.  
  1391.         if ($queryresult = mysqli_query($link, $this->sql, ($buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)))
  1392.         {
  1393.             // unset $sql to lower memory .. this isn't an error, so it's not needed
  1394.             $this->sql = '';
  1395.  
  1396.             return $queryresult;
  1397.         }
  1398.         else
  1399.         {
  1400.             $this->halt();
  1401.  
  1402.             // unset $sql to lower memory .. error will have already been thrown
  1403.             $this->sql = '';
  1404.         }
  1405.     }
  1406.  
  1407.     /**
  1408.     * Simple wrapper for select_db(), to allow argument order changes
  1409.     *
  1410.     * @param    string  Database name
  1411.     * @param    integer Link identifier
  1412.     *
  1413.     * @return   boolean
  1414.     */
  1415.     function select_db_wrapper($database = '', $link = null)
  1416.     {
  1417.         return $this->functions['select_db']($link, $database);
  1418.     }
  1419.  
  1420.     /**
  1421.     * Escapes a string to make it safe to be inserted into an SQL query
  1422.     *
  1423.     * @param    string  The string to be escaped
  1424.     *
  1425.     * @return   string
  1426.     */
  1427.     function escape_string($string)
  1428.     {
  1429.         return $this->functions['real_escape_string']($this->connection_master, $string);
  1430.     }
  1431.  
  1432.     /**
  1433.     * Returns the name of a field from within a query result set
  1434.     *
  1435.     * @param    string  The query result ID we are dealing with
  1436.     * @param    integer The index position of the field
  1437.     *
  1438.     * @return   string
  1439.     */
  1440.     function field_name($queryresult, $index)
  1441.     {
  1442.         $field = @$this->functions['fetch_field']($queryresult);
  1443.         return $field->name;
  1444.     }
  1445.  
  1446.     /**
  1447.     * Switches database error display ON
  1448.     */
  1449.     function show_errors()
  1450.     {
  1451.         $this->reporterror = true;
  1452.         mysqli_report(MYSQLI_REPORT_ERROR);
  1453.     }
  1454.  
  1455.     /**
  1456.     * Switches database error display OFF
  1457.     */
  1458.     function hide_errors()
  1459.     {
  1460.         $this->reporterror = false;
  1461.         mysqli_report(MYSQLI_REPORT_OFF);
  1462.     }
  1463.  
  1464. }
  1465.  
  1466. // #############################################################################
  1467. // datastore class
  1468.  
  1469. /**
  1470. * Class for fetching and initializing the vBulletin datastore from the database
  1471. *
  1472. * @package  vBulletin
  1473. * @version  $Revision: 62833 $
  1474. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  1475. */
  1476. class vB_Datastore
  1477. {
  1478.     /**
  1479.     * Default items that are always loaded by fetch();
  1480.     *
  1481.     * @var  array
  1482.     */
  1483.     var $defaultitems = array(
  1484.         'options',
  1485.         'bitfields',
  1486.         'attachmentcache',
  1487.         'forumcache',
  1488.         'usergroupcache',
  1489.         'stylecache',
  1490.         'languagecache',
  1491.         'products',
  1492.         'pluginlist',
  1493.         'cron',
  1494.         'profilefield',
  1495.         'loadcache',
  1496.         'noticecache',
  1497.         'activitystream',
  1498.     );
  1499.  
  1500.     /**
  1501.      * All of the entries that have already been fetched
  1502.      *
  1503.      * @var array string
  1504.      */
  1505.     static $registered = array();
  1506.  
  1507.     /**
  1508.     * This variable should be set to be a reference to the registry object
  1509.     *
  1510.     * @var  vB_Registry
  1511.     */
  1512.     var $registry = null;
  1513.  
  1514.     /**
  1515.     * This variable should be set to be a reference to the database object
  1516.     *
  1517.     * @var  vB_Database
  1518.     */
  1519.     var $dbobject = null;
  1520.  
  1521.     /**
  1522.     * Unique prefix for item's title, required for multiple forums on the same server using the same classes that read/write to memory
  1523.     *
  1524.     * @var  string
  1525.     */
  1526.     var $prefix = '';
  1527.  
  1528.     /**
  1529.      * Whether we have verified that options were loaded correctly.
  1530.      *
  1531.      * @var bool
  1532.      */
  1533.     var $checked_options;
  1534.  
  1535.     /**
  1536.     * Constructor - establishes the database object to use for datastore queries
  1537.     *
  1538.     * @param    vB_Registry The registry object
  1539.     * @param    vB_Database The database object
  1540.     */
  1541.     function vB_Datastore(&$registry, &$dbobject)
  1542.     {
  1543.         $this->registry =& $registry;
  1544.         $this->dbobject =& $dbobject;
  1545.  
  1546.         $this->prefix =& $this->registry->config['Datastore']['prefix'];
  1547.  
  1548.         if (defined('SKIP_DEFAULTDATASTORE'))
  1549.         {
  1550.             $this->defaultitems = array('options', 'bitfields', 'pluginlist');
  1551.         }
  1552.  
  1553.         if (!is_object($registry))
  1554.         {
  1555.             trigger_error('<strong>vB_Datastore</strong>: $this->registry is not an object', E_USER_ERROR);
  1556.         }
  1557.         if (!is_object($dbobject))
  1558.         {
  1559.             trigger_error('<strong>vB_Datastore</strong>: $this->dbobject is not an object!', E_USER_ERROR);
  1560.         }
  1561.     }
  1562.  
  1563.     /**
  1564.     * Sorts the data returned from the cache and places it into appropriate places
  1565.     *
  1566.     * @param    string  The name of the data item to be processed
  1567.     * @param    mixed   The data associated with the title
  1568.     * @param    integer If the data needs to be unserialized, 0 = no, 1 = yes, 2 = auto detect
  1569.     *
  1570.     * @return   boolean
  1571.     */
  1572.     function register($title, $data, $unserialize_detect = 2)
  1573.     {
  1574.         // specifies whether or not $data should be an array
  1575.         $try_unserialize = (($unserialize_detect == 2) AND ($data[0] == 'a' AND $data[1] == ':'));
  1576.  
  1577.         if ($try_unserialize OR $unserialize_detect == 1)
  1578.         {
  1579.             // unserialize returned an error so return false
  1580.             if (($data = unserialize($data)) === false)
  1581.             {
  1582.                 return false;
  1583.             }
  1584.         }
  1585.  
  1586.         if ($title == 'bitfields')
  1587.         {
  1588.             $registry =& $this->registry;
  1589.  
  1590.             foreach (array_keys($data) AS $group)
  1591.             {
  1592.                 $registry->{'bf_' . $group} =& $data["$group"];
  1593.  
  1594.                 $group_prefix = 'bf_' . $group . '_';
  1595.                 $group_info =& $data["$group"];
  1596.  
  1597.                 foreach (array_keys($group_info) AS $subgroup)
  1598.                 {
  1599.                     $registry->{$group_prefix . $subgroup} =& $group_info["$subgroup"];
  1600.                 }
  1601.             }
  1602.         }
  1603.         else if (!empty($title))
  1604.         {
  1605.             $this->registry->$title = $data;
  1606.         }
  1607.  
  1608.         // Ensure items are not refetched
  1609.         self::$registered[] = $title;
  1610.  
  1611.         return true;
  1612.     }
  1613.  
  1614.     /**
  1615.      * Prepares a list of items for fetching.
  1616.      * Items that are already fetched are skipped.
  1617.      *
  1618.      * @param array string $items               - Array of item titles that are required
  1619.      * @return array string                     - An array of items that need to be fetched
  1620.      */
  1621.     function prepare_itemarray($items)
  1622.     {
  1623.         if ($items)
  1624.         {
  1625.             if (is_array($items))
  1626.             {
  1627.                 $itemarray = $items;
  1628.             }
  1629.             else
  1630.             {
  1631.                 $itemarray = explode(',', $items);
  1632.  
  1633.                 foreach($itemarray AS &$title)
  1634.                 {
  1635.                     $title = trim($title);
  1636.                 }
  1637.             }
  1638.             // Include default items
  1639.             $itemarray = array_merge($itemarray, $this->defaultitems);
  1640.         }
  1641.         else
  1642.         {
  1643.             $itemarray = $this->defaultitems;
  1644.         }
  1645.  
  1646.         // Remove anything that is already loaded
  1647.         $itemarray = array_diff($itemarray, vB_DataStore::$registered);
  1648.  
  1649.         return $itemarray;
  1650.     }
  1651.  
  1652.     /**
  1653.      * Prepares an array of items into a list.
  1654.      * The result is a comma delimited, db escaped, quoted list for use in SQL.
  1655.      *
  1656.      * @param array string $items               - An array of item titles
  1657.      * @param bool $prepare_items               - Wether to check the items first
  1658.      *
  1659.      * @return string                           - A sql safe comma delimited list
  1660.      */
  1661.     function prepare_itemlist($items, $prepare_items = false)
  1662.     {
  1663.         if (is_string($items) OR $prepare_items)
  1664.         {
  1665.             $items = $this->prepare_itemarray($items);
  1666.         }
  1667.  
  1668.         if (!sizeof($items))
  1669.         {
  1670.             return false;
  1671.         }
  1672.  
  1673.         foreach ($items AS &$item)
  1674.         {
  1675.             $item = "'" . $this->dbobject->escape_string($item) . "'";
  1676.         }
  1677.  
  1678.         return implode(',', $items);
  1679.     }
  1680.  
  1681.     /**
  1682.     * Fetches the contents of the datastore from the database
  1683.     *
  1684.     * @param    array   Array of items to fetch from the datastore
  1685.     *
  1686.     * @return   boolean
  1687.     */
  1688.     function fetch($items)
  1689.     {
  1690.         if ($items = $this->prepare_itemlist($items, true))
  1691.         {
  1692.             $result = $this->do_db_fetch($items);
  1693.             if (!$result)
  1694.             {
  1695.                 return false;
  1696.             }
  1697.         }
  1698.  
  1699.         $this->check_options();
  1700.         return true;
  1701.     }
  1702.  
  1703.     /**
  1704.     * Performs the actual fetching of the datastore items for the database, child classes may use this
  1705.     *
  1706.     * @param    string  title of the datastore item
  1707.     *
  1708.     * @return   bool    Valid Query?
  1709.     */
  1710.     function do_db_fetch($itemlist)
  1711.     {
  1712.         $db =& $this->dbobject;
  1713.  
  1714.         $db->hide_errors();
  1715.         $dataitems = $db->query_read("
  1716.             SELECT *
  1717.             FROM " . TABLE_PREFIX . "datastore
  1718.             WHERE title IN ($itemlist)
  1719.         ");
  1720.         $db->show_errors();
  1721.         while ($dataitem = $db->fetch_array($dataitems))
  1722.         {
  1723.             $this->register($dataitem['title'], $dataitem['data'], (isset($dataitem['unserialize']) ? $dataitem['unserialize'] : 2));
  1724.         }
  1725.         $db->free_result($dataitems);
  1726.  
  1727.         return (!$db->errno());
  1728.     }
  1729.  
  1730.     /**
  1731.     * Checks that the options item has come out of the datastore correctly
  1732.     * and sets the 'versionnumber' variable
  1733.     */
  1734.     function check_options()
  1735.     {
  1736.         if ($this->checked_options)
  1737.         {
  1738.             return;
  1739.         }
  1740.  
  1741.         if (!isset($this->registry->options['templateversion']))
  1742.         {
  1743.             // fatal error - options not loaded correctly
  1744.             require_once(DIR . '/includes/adminfunctions.php');
  1745.             require_once(DIR . '/includes/functions.php');
  1746.             $this->register('options', build_options(), 0);
  1747.         }
  1748.  
  1749.         // set the short version number
  1750.         $this->registry->options['simpleversion'] = SIMPLE_VERSION . (isset($this->registry->config['Misc']['jsver']) ? $this->registry->config['Misc']['jsver'] : '');
  1751.  
  1752.         // set the version number variable
  1753.         $this->registry->versionnumber =& $this->registry->options['templateversion'];
  1754.  
  1755.         $this->checked_options = true;
  1756.     }
  1757. }
  1758.  
  1759. // #############################################################################
  1760. // input handler class
  1761.  
  1762. /**#@+
  1763. * Ways of cleaning input. Should be mostly self-explanatory.
  1764. */
  1765. define('TYPE_NOCLEAN',      0); // no change
  1766.  
  1767. define('TYPE_BOOL',     1); // force boolean
  1768. define('TYPE_INT',      2); // force integer
  1769. define('TYPE_UINT',     3); // force unsigned integer
  1770. define('TYPE_NUM',      4); // force number
  1771. define('TYPE_UNUM',     5); // force unsigned number
  1772. define('TYPE_UNIXTIME', 6); // force unix datestamp (unsigned integer)
  1773. define('TYPE_STR',      7); // force trimmed string
  1774. define('TYPE_NOTRIM',   8); // force string - no trim
  1775. define('TYPE_NOHTML',   9); // force trimmed string with HTML made safe
  1776. define('TYPE_ARRAY',   10); // force array
  1777. define('TYPE_FILE',    11); // force file
  1778. define('TYPE_BINARY',  12); // force binary string
  1779. define('TYPE_NOHTMLCOND', 13); // force trimmed string with HTML made safe if determined to be unsafe
  1780.  
  1781. define('TYPE_ARRAY_BOOL',     101);
  1782. define('TYPE_ARRAY_INT',      102);
  1783. define('TYPE_ARRAY_UINT',     103);
  1784. define('TYPE_ARRAY_NUM',      104);
  1785. define('TYPE_ARRAY_UNUM',     105);
  1786. define('TYPE_ARRAY_UNIXTIME', 106);
  1787. define('TYPE_ARRAY_STR',      107);
  1788. define('TYPE_ARRAY_NOTRIM',   108);
  1789. define('TYPE_ARRAY_NOHTML',   109);
  1790. define('TYPE_ARRAY_ARRAY',    110);
  1791. define('TYPE_ARRAY_FILE',     11);  // An array of "Files" behaves differently than other <input> arrays. TYPE_FILE handles both types.
  1792. define('TYPE_ARRAY_BINARY',   112);
  1793. define('TYPE_ARRAY_NOHTMLCOND',113);
  1794.  
  1795. define('TYPE_ARRAY_KEYS_INT', 202);
  1796. define('TYPE_ARRAY_KEYS_STR', 207);
  1797.  
  1798. define('TYPE_CONVERT_SINGLE', 100); // value to subtract from array types to convert to single types
  1799. define('TYPE_CONVERT_KEYS',   200); // value to subtract from array => keys types to convert to single types
  1800. /**#@-*/
  1801.  
  1802. // temporary
  1803. define('INT',        TYPE_INT);
  1804. define('STR',        TYPE_STR);
  1805. define('STR_NOHTML', TYPE_NOHTML);
  1806. define('FILE',       TYPE_FILE);
  1807.  
  1808. /**
  1809. * Class to handle and sanitize variables from GET, POST and COOKIE etc
  1810. *
  1811. * @package  vBulletin
  1812. * @version  $Revision: 62833 $
  1813. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  1814. */
  1815. class vB_Input_Cleaner
  1816. {
  1817.     /**
  1818.     * Translation table for short name to long name
  1819.     *
  1820.     * @var    array
  1821.     */
  1822.     var $shortvars = array(
  1823.         'f'     => 'forumid',
  1824.         't'     => 'threadid',
  1825.         'p'     => 'postid',
  1826.         'u'     => 'userid',
  1827.         'a'     => 'announcementid',
  1828.         'c'     => 'calendarid',
  1829.         'e'     => 'eventid',
  1830.         'q'     => 'query',
  1831.         'pp'    => 'perpage',
  1832.         'page'  => 'pagenumber',
  1833.         'sort'  => 'sortfield',
  1834.         'order' => 'sortorder',
  1835.     );
  1836.  
  1837.     /**
  1838.     * Translation table for short superglobal name to long superglobal name
  1839.     *
  1840.     * @var     array
  1841.     */
  1842.     var $superglobal_lookup = array(
  1843.         'g' => '_GET',
  1844.         'p' => '_POST',
  1845.         'r' => '_REQUEST',
  1846.         'c' => '_COOKIE',
  1847.         's' => '_SERVER',
  1848.         'e' => '_ENV',
  1849.         'f' => '_FILES'
  1850.     );
  1851.  
  1852.     /**
  1853.     * System state. The complete URL of the current page, without sessionhash
  1854.     *
  1855.     * @var  string
  1856.     */
  1857.     var $scriptpath = '';
  1858.  
  1859.     /**
  1860.     * Reload URL. Complete URL of the current page including sessionhash
  1861.     *
  1862.     * @var  string
  1863.     */
  1864.     var $reloadurl = '';
  1865.  
  1866.     /**
  1867.     * System state. The complete URL of the page for Who's Online purposes
  1868.     *
  1869.     * @var  string
  1870.     */
  1871.     var $wolpath = '';
  1872.  
  1873.     /**
  1874.     * System state. The complete URL of the referring page
  1875.     *
  1876.     * @var  string
  1877.     */
  1878.     var $url = '';
  1879.  
  1880.     /**
  1881.     * System state. The IP address of the current visitor
  1882.     *
  1883.     * @var  string
  1884.     */
  1885.     var $ipaddress = '';
  1886.  
  1887.     /**
  1888.     * System state. An attempt to find a second IP for the current visitor (proxy etc)
  1889.     *
  1890.     * @var  string
  1891.     */
  1892.     var $alt_ip = '';
  1893.  
  1894.     /**
  1895.     * A reference to the main registry object
  1896.     *
  1897.     * @var  vB_Registry
  1898.     */
  1899.     var $registry = null;
  1900.  
  1901.     /**
  1902.     * Keep track of variables that have already been cleaned
  1903.     *
  1904.     * @var  array
  1905.     */
  1906.     var $cleaned_vars = array();
  1907.  
  1908.     /**
  1909.     * Constructor
  1910.     *
  1911.     * First, reverses the effects of magic quotes on GPC
  1912.     * Second, translates short variable names to long (u --> userid)
  1913.     * Third, deals with $_COOKIE[userid] conflicts
  1914.     *
  1915.     * @param    vB_Registry The instance of the vB_Registry object
  1916.     */
  1917.     function vB_Input_Cleaner(&$registry)
  1918.     {
  1919.         $this->registry =& $registry;
  1920.  
  1921.         if (!is_array($GLOBALS))
  1922.         {
  1923.             die('<strong>Fatal Error:</strong> Invalid URL.');
  1924.         }
  1925.  
  1926.         // resolve the request URL
  1927.         $this->resolve_request_url($registry);
  1928.  
  1929.         // store a relative path that includes the sessionhash for reloadurl
  1930.         $registry->reloadurl = $this->xss_clean($this->add_query(VB_URL_PATH, VB_URL_QUERY_RAW));
  1931.         // store the current script
  1932.         $registry->script = SCRIPT;
  1933.  
  1934.         // store the scriptpath
  1935.         $registry->scriptpath = $this->xss_clean($this->add_query(VB_URL_PATH, VB_URL_QUERY));
  1936.  
  1937.         // overwrite GET[x] and REQUEST[x] with POST[x] if it exists (overrides server's GPC order preference)
  1938.         if ($_SERVER['REQUEST_METHOD'] == 'POST')
  1939.         {
  1940.             foreach (array_keys($_POST) AS $key)
  1941.             {
  1942.                 if (isset($_GET["$key"]))
  1943.                 {
  1944.                     $_GET["$key"] = $_REQUEST["$key"] = $_POST["$key"];
  1945.                 }
  1946.             }
  1947.         }
  1948.  
  1949.         // deal with session bypass situation
  1950.         if (!defined('SESSION_BYPASS'))
  1951.         {
  1952.             define('SESSION_BYPASS', !empty($_REQUEST['bypass']));
  1953.         }
  1954.  
  1955.         // reverse the effects of magic quotes if necessary
  1956.         if (function_exists('get_magic_quotes_gpc') AND get_magic_quotes_gpc())
  1957.         {
  1958.             $this->stripslashes_deep($_REQUEST); // needed for some reason (at least on php5 - not tested on php4)
  1959.             $this->stripslashes_deep($_GET);
  1960.             $this->stripslashes_deep($_POST);
  1961.             $this->stripslashes_deep($_COOKIE);
  1962.  
  1963.             if (is_array($_FILES))
  1964.             {
  1965.                 foreach ($_FILES AS $key => $val)
  1966.                 {
  1967.                     $_FILES["$key"]['tmp_name'] = str_replace('\\', '\\\\', $val['tmp_name']);
  1968.                 }
  1969.                 $this->stripslashes_deep($_FILES);
  1970.             }
  1971.         }
  1972.  
  1973.         //28997 -- set_magic_quotes_runtime is throws deprecation warnings in 5.3.
  1974.         //We still need it (somebody could still turn it on, wj, so supress the warning (the only thing
  1975.         //we can do).  Add check for function so that 6.0 (or whichever) doesn't mysteriously
  1976.         //break because the "function doesn't exist" error gets supressed.
  1977.         if (function_exists('set_magic_quotes_runtime'))
  1978.         {
  1979.             @set_magic_quotes_runtime(0);
  1980.             @ini_set('magic_quotes_sybase', 0);
  1981.         }
  1982.  
  1983.         foreach (array('_GET', '_POST') AS $arrayname)
  1984.         {
  1985.             if (isset($GLOBALS["$arrayname"]['do']))
  1986.             {
  1987.                 $GLOBALS["$arrayname"]['do'] = trim($GLOBALS["$arrayname"]['do']);
  1988.             }
  1989.  
  1990.             $this->convert_shortvars($GLOBALS["$arrayname"]);
  1991.         }
  1992.  
  1993.         // set the AJAX flag if we have got an AJAX submission
  1994.         // unless the request explictly doesn't want us to.  The problem with this is that it hits any XMLHttpRequest
  1995.         // even if we have a request for which the ajax handling is not appropriate (for example JQUERY mobile which
  1996.         // uses XMLHttpRequest for everything and expects html to come back).  Ideally we'd use a less blunt force
  1997.         // approach to handling AJAX behavior in the first place, but this allows specific requests to avoid it.
  1998.         if ($_SERVER['REQUEST_METHOD'] == 'POST' AND $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' AND
  1999.             !(isset($_REQUEST['forcenoajax']) AND $_REQUEST['forcenoajax']))
  2000.         {
  2001.             $_POST['ajax'] = $_REQUEST['ajax'] = 1;
  2002.         }
  2003.  
  2004.         // reverse the effects of register_globals if necessary
  2005.         if (@ini_get('register_globals') OR !@ini_get('gpc_order'))
  2006.         {
  2007.             foreach ($this->superglobal_lookup AS $arrayname)
  2008.             {
  2009.                 $registry->superglobal_size["$arrayname"] = sizeof($GLOBALS["$arrayname"]);
  2010.  
  2011.                 foreach (array_keys($GLOBALS["$arrayname"]) AS $varname)
  2012.                 {
  2013.                     // make sure we dont unset any global arrays like _SERVER
  2014.                     if (!in_array($varname, $this->superglobal_lookup))
  2015.                     {
  2016.                         unset($GLOBALS["$varname"]);
  2017.                     }
  2018.                 }
  2019.             }
  2020.         }
  2021.         else
  2022.         {
  2023.             foreach ($this->superglobal_lookup AS $arrayname)
  2024.             {
  2025.                 $registry->superglobal_size["$arrayname"] = sizeof($GLOBALS["$arrayname"]);
  2026.             }
  2027.         }
  2028.  
  2029.         // deal with cookies that may conflict with _GET and _POST data, and create our own _REQUEST with no _COOKIE input
  2030.         foreach (array_keys($_COOKIE) AS $varname)
  2031.         {
  2032.             unset($_REQUEST["$varname"]);
  2033.             if (isset($_POST["$varname"]))
  2034.             {
  2035.                 $_REQUEST["$varname"] =& $_POST["$varname"];
  2036.             }
  2037.             else if (isset($_GET["$varname"]))
  2038.             {
  2039.                 $_REQUEST["$varname"] =& $_GET["$varname"];
  2040.             }
  2041.         }
  2042.  
  2043.         // fetch client IP address
  2044.         $registry->ipaddress = $this->fetch_ip();
  2045.         $registry->alt_ip = $this->fetch_alt_ip();
  2046.  
  2047.         // fetch url of current page for Who's Online
  2048.         if (!defined('SKIP_WOLPATH') OR !SKIP_WOLPATH)
  2049.         {
  2050.             $registry->wolpath = $this->fetch_wolpath();
  2051.             define('WOLPATH', $registry->wolpath);
  2052.         }
  2053.  
  2054.         // define some useful contants related to environment
  2055.         define('USER_AGENT',     $_SERVER['HTTP_USER_AGENT']);
  2056.         define('REFERRER',       $_SERVER['HTTP_REFERER']);
  2057.  
  2058.         // All requests passed from API client should be in UTF-8 encoding and we need to convert it back to vB's current encoding.
  2059.         // We also need to do this this for the ajax requests.
  2060.         if ($_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' OR (defined('VB_API') AND VB_API === true))
  2061.         {
  2062.             define('NEED_DECODE', true);
  2063.         }
  2064.  
  2065.     }
  2066.  
  2067.     /**
  2068.      * Resolves information about the request URL.
  2069.      */
  2070.     function resolve_request_url($registry)
  2071.     {
  2072.         // Get server port
  2073.         $port = intval($_SERVER['SERVER_PORT']);
  2074.         $port = in_array($port, array(80, 443)) ? '' : ':' . $port;
  2075.  
  2076.         // resolve the request scheme
  2077.         $scheme = ((':443' == $port) OR (isset($_SERVER['HTTPS']) AND $_SERVER['HTTPS'] AND ($_SERVER['HTTPS'] != 'off'))) ? 'https://' : 'http://';
  2078.  
  2079.         if ($scheme == 'http://' AND $_SERVER['SERVER_PORT'] == 443)
  2080.         {
  2081.             $port = ':443';
  2082.         }
  2083.  
  2084.         $host = $this->fetch_server_value('HTTP_HOST');
  2085.         $name = $this->fetch_server_value('SERVER_NAME');
  2086.  
  2087.         // If host exists use it, otherwise fallback to servername.
  2088.         $host = ( !empty($host) ? $host : $name );
  2089.  
  2090.         // resolve the query
  2091.         $query = ($query = $this->fetch_server_value('QUERY_STRING')) ? '?' . $query : '';
  2092.  
  2093.         // resolve the path and query
  2094.         if (!($scriptpath = $this->fetch_server_value('REQUEST_URI')))
  2095.         {
  2096.             if (!($scriptpath = $this->fetch_server_value('UNENCODED_URL')))
  2097.             {
  2098.                 $scriptpath = $this->fetch_server_value('HTTP_X_REWRITE_URL');
  2099.             }
  2100.         }
  2101.  
  2102.         if ($scriptpath)
  2103.         {
  2104.             // already have the query
  2105.             if ($scriptpath)
  2106.             {
  2107.                 $query = '';
  2108.             }
  2109.         }
  2110.         else
  2111.         {
  2112.             // server hasn't provided a URI, try to resolve one
  2113.             if (!$scriptpath = $this->fetch_server_value('PATH_INFO'))
  2114.             {
  2115.                 if (!$scriptpath = $this->fetch_server_value('REDIRECT_URL'))
  2116.                 {
  2117.                     if (!($scriptpath = $this->fetch_server_value('URL')))
  2118.                     {
  2119.                         if (!($scriptpath = $this->fetch_server_value('PHP_SELF')))
  2120.                         {
  2121.                             $scriptpath = $this->fetch_server_value('SCRIPT_NAME');
  2122.                         }
  2123.                     }
  2124.                 }
  2125.             }
  2126.         }
  2127.  
  2128.         // build the URL
  2129.         $url = $scheme . $host . '/' . ltrim($scriptpath, '/\\') . $query;
  2130.  
  2131.         // store a literal version
  2132.         define('VB_URL', $url);
  2133.  
  2134.         // check relative path
  2135.         if (defined('VB_RELATIVE_PATH'))
  2136.         {
  2137.             define('VB_URL_RELATIVE_PATH', trim(VB_RELATIVE_PATH, '/') . '/');
  2138.         }
  2139.         else
  2140.         {
  2141.             define('VB_URL_RELATIVE_PATH', '');
  2142.         }
  2143.  
  2144.         // Set URL info
  2145.         $url_info = @parse_url(VB_URL);
  2146.         $url_info['path'] = '/' . ltrim($url_info['path'], '/\\');
  2147.         $url_info['query_raw'] = (isset($url_info['query']) ? $url_info['query'] : '');
  2148.         $url_info['query'] = $this->strip_sessionhash($url_info['query']);
  2149.         $url_info['query'] = trim($url_info['query'], '?&') ? $url_info['query'] : '';
  2150.  
  2151.         /*
  2152.             values seen in the wild:
  2153.  
  2154.             CGI+suexec:
  2155.             SCRIPT_NAME: /vb4/admincp/index.php
  2156.             ORIG_SCRIPT_NAME: /cgi-sys/php53-fcgi-starter.fcgi
  2157.  
  2158.             CGI #1:
  2159.             SCRIPT_NAME: /index.php
  2160.             ORIG_SCRIPT_NAME: /search/foo
  2161.  
  2162.             CGI #2:
  2163.             SCRIPT_NAME: /index.php/search/foo
  2164.             ORIG_SCRIPT_NAME: /index.php
  2165.  
  2166.         */
  2167.  
  2168.         if (substr(PHP_SAPI, -3) == 'cgi' AND (isset($_SERVER['ORIG_SCRIPT_NAME']) AND !empty($_SERVER['ORIG_SCRIPT_NAME'])))
  2169.         {
  2170.             if (substr($_SERVER['SCRIPT_NAME'], 0, strlen($_SERVER['ORIG_SCRIPT_NAME'])) == $_SERVER['ORIG_SCRIPT_NAME'])
  2171.             {
  2172.                 // cgi #2 above
  2173.                 $url_info['script'] = $_SERVER['ORIG_SCRIPT_NAME'];
  2174.             }
  2175.             else
  2176.             {
  2177.                 // cgi #1 and CGI+suexec above
  2178.                 $url_info['script'] = $_SERVER['SCRIPT_NAME'];
  2179.             }
  2180.         }
  2181.         else
  2182.         {
  2183.             $url_info['script'] = (isset($_SERVER['ORIG_SCRIPT_NAME']) AND !empty($_SERVER['ORIG_SCRIPT_NAME'])) ? $_SERVER['ORIG_SCRIPT_NAME'] : $_SERVER['SCRIPT_NAME'];
  2184.         }
  2185.         $url_info['script'] = '/' . ltrim($url_info['script'], '/\\');
  2186.  
  2187.         // define constants
  2188.         define('VB_URL_SCHEME',      $url_info['scheme']);
  2189.         define('VB_URL_HOST',        $url_info['host']);
  2190.         define('VB_URL_PORT',        $port);
  2191.         define('VB_URL_SCRIPT_PATH', rtrim(dirname($url_info['script']), '/\\') . '/');
  2192.         define('VB_URL_SCRIPT',      basename($url_info['script']));
  2193.         define('VB_URL_PATH',        urldecode($url_info['path']));
  2194.         define('VB_URL_PATH_RAW',    $url_info['path']);
  2195.         define('VB_URL_QUERY',       $url_info['query'] ? $url_info['query'] : '');
  2196.         define('VB_URL_QUERY_RAW',   $url_info['query_raw']);
  2197.         define('VB_URL_CLEAN',       $this->xss_clean($this->strip_sessionhash(VB_URL)));
  2198.         define('VB_URL_WEBROOT',     $this->xss_clean(VB_URL_SCHEME . '://' . VB_URL_HOST . VB_URL_PORT));
  2199.         define('VB_URL_BASE_PATH',   $this->xss_clean(VB_URL_SCHEME . '://' . VB_URL_HOST . VB_URL_PORT . VB_URL_SCRIPT_PATH . VB_URL_RELATIVE_PATH));
  2200.  
  2201.         // legacy constants
  2202.         define('SCRIPT',       $_SERVER['SCRIPT_NAME']);
  2203.         define('SCRIPTPATH',   $this->xss_clean($this->add_query(VB_URL_PATH)));
  2204.         define('REQ_PROTOCOL', $url_info['scheme']);
  2205.         define('VB_HTTP_HOST', $url_info['host']);
  2206.     }
  2207.  
  2208.     /**
  2209.      * Fetches a value from $_SERVER or $_ENV
  2210.      *
  2211.      * @param string $name
  2212.      * @return string
  2213.      */
  2214.     function fetch_server_value($name)
  2215.     {
  2216.         if (isset($_SERVER[$name]) AND $_SERVER[$name])
  2217.         {
  2218.             return $_SERVER[$name];
  2219.         }
  2220.  
  2221.         if (isset($_ENV[$name]) AND $_ENV[$name])
  2222.         {
  2223.             return $_ENV[$name];
  2224.         }
  2225.  
  2226.         return false;
  2227.     }
  2228.  
  2229.  
  2230.     /**
  2231.      * Adds a query string to a path, fixing the query characters.
  2232.      *
  2233.      * @param   string      The path to add the query to
  2234.      * @param   string      The query string to add to the path
  2235.      *
  2236.      * @return  string      The resulting string
  2237.      */
  2238.     function add_query($path, $query = false)
  2239.     {
  2240.         if (false === $query)
  2241.         {
  2242.             $query = VB_URL_QUERY;
  2243.         }
  2244.  
  2245.         if (!$query OR !($query = trim($query, '?&')))
  2246.         {
  2247.             return $path;
  2248.         }
  2249.  
  2250.         return $path . '?' . $query;
  2251.     }
  2252.  
  2253.     /**
  2254.      * Adds a fragment to a path
  2255.      *
  2256.      * @param   string      The path to add the fragment to
  2257.      * @param   string      The fragment to add to the path
  2258.      *
  2259.      * @return  string      The resulting string
  2260.      */
  2261.     function add_fragment($path, $fragment = false)
  2262.     {
  2263.         if (!$fragment)
  2264.         {
  2265.             return $path;
  2266.         }
  2267.  
  2268.         return $path . '#' . $fragment;
  2269.     }
  2270.  
  2271.  
  2272.     /**
  2273.     * Makes data in an array safe to use
  2274.     *
  2275.     * @param    array   The source array containing the data to be cleaned
  2276.     * @param    array   Array of variable names and types we want to extract from the source array
  2277.     *
  2278.     * @return   array
  2279.     */
  2280.     function &clean_array(&$source, $variables)
  2281.     {
  2282.         $return = array();
  2283.  
  2284.         foreach ($variables AS $varname => $vartype)
  2285.         {
  2286.             $return["$varname"] =& $this->clean($source["$varname"], $vartype, isset($source["$varname"]));
  2287.         }
  2288.  
  2289.         return $return;
  2290.     }
  2291.  
  2292.     /**
  2293.     * Makes GPC variables safe to use
  2294.     *
  2295.     * @param    string  Either, g, p, c, r or f (corresponding to get, post, cookie, request and files)
  2296.     * @param    array   Array of variable names and types we want to extract from the source array
  2297.     *
  2298.     * @return   array
  2299.     */
  2300.     function clean_array_gpc($source, $variables)
  2301.     {
  2302.         $sg =& $GLOBALS[$this->superglobal_lookup["$source"]];
  2303.  
  2304.         foreach ($variables AS $varname => $vartype)
  2305.         {
  2306.             // clean a variable only once unless its a different type
  2307.             if (!isset($this->cleaned_vars["$varname"]) OR $this->cleaned_vars["$varname"] != $vartype)
  2308.             {
  2309.                 $this->registry->GPC_exists["$varname"] = isset($sg["$varname"]);
  2310.                 $this->registry->GPC["$varname"] =& $this->clean(
  2311.                     $sg["$varname"],
  2312.                     $vartype,
  2313.                     isset($sg["$varname"])
  2314.                 );
  2315.                 if ((defined('NEED_DECODE') AND NEED_DECODE === true))
  2316.                 {
  2317.                     switch ($vartype) {
  2318.                         case TYPE_STR:
  2319.                         case TYPE_NOTRIM:
  2320.                         case TYPE_NOHTML:
  2321.                         case TYPE_NOHTMLCOND:
  2322.                             if (!($charset = vB_Template_Runtime::fetchStyleVar('charset')))
  2323.                             {
  2324.                                 $charset = $this->registry->userinfo['lang_charset'];
  2325.                             }
  2326.  
  2327.                             $lower_charset = strtolower($charset);
  2328.                             if ($lower_charset != 'utf-8')
  2329.                             {
  2330.                                 if ($lower_charset == 'iso-8859-1')
  2331.                                 {
  2332.                                     $this->registry->GPC["$varname"] = to_charset(ncrencode($this->registry->GPC["$varname"], true, true), 'utf-8');
  2333.                                 }
  2334.                                 else
  2335.                                 {
  2336.                                     $this->registry->GPC["$varname"] = to_charset($this->registry->GPC["$varname"], 'utf-8');
  2337.                                 }
  2338.                             }
  2339.                     }
  2340.                 }
  2341.                 $this->cleaned_vars["$varname"] = $vartype;
  2342.             }
  2343.         }
  2344.     }
  2345.  
  2346.     /**
  2347.     * Makes a single GPC variable safe to use and returns it
  2348.     *
  2349.     * @param    array   The source array containing the data to be cleaned
  2350.     * @param    string  The name of the variable in which we are interested
  2351.     * @param    integer The type of the variable in which we are interested
  2352.     *
  2353.     * @return   mixed
  2354.     */
  2355.     function &clean_gpc($source, $varname, $vartype = TYPE_NOCLEAN)
  2356.     {
  2357.         // clean a variable only once unless its a different type
  2358.         if (!isset($this->cleaned_vars["$varname"]) OR $this->cleaned_vars["$varname"] != $vartype)
  2359.         {
  2360.             $sg =& $GLOBALS[$this->superglobal_lookup["$source"]];
  2361.  
  2362.             $this->registry->GPC_exists["$varname"] = isset($sg["$varname"]);
  2363.             $this->registry->GPC["$varname"] =& $this->clean(
  2364.                 $sg["$varname"],
  2365.                 $vartype,
  2366.                 isset($sg["$varname"])
  2367.             );
  2368.             $this->cleaned_vars["$varname"] = $vartype;
  2369.         }
  2370.  
  2371.         return $this->registry->GPC["$varname"];
  2372.     }
  2373.  
  2374.     /**
  2375.     * Makes a single variable safe to use and returns it
  2376.     *
  2377.     * @param    mixed   The variable to be cleaned
  2378.     * @param    integer The type of the variable in which we are interested
  2379.     * @param    boolean Whether or not the variable to be cleaned actually is set
  2380.     *
  2381.     * @return   mixed   The cleaned value
  2382.     */
  2383.     function &clean(&$var, $vartype = TYPE_NOCLEAN, $exists = true)
  2384.     {
  2385.         if ($exists)
  2386.         {
  2387.             if ($vartype < TYPE_CONVERT_SINGLE)
  2388.             {
  2389.                 $this->do_clean($var, $vartype);
  2390.             }
  2391.             else if (is_array($var))
  2392.             {
  2393.                 if ($vartype >= TYPE_CONVERT_KEYS)
  2394.                 {
  2395.                     $var = array_keys($var);
  2396.                     $vartype -=  TYPE_CONVERT_KEYS;
  2397.                 }
  2398.                 else
  2399.                 {
  2400.                     $vartype -= TYPE_CONVERT_SINGLE;
  2401.                 }
  2402.  
  2403.                 foreach (array_keys($var) AS $key)
  2404.                 {
  2405.                     $this->do_clean($var["$key"], $vartype);
  2406.                 }
  2407.             }
  2408.             else
  2409.             {
  2410.                 $var = array();
  2411.             }
  2412.             return $var;
  2413.         }
  2414.         else
  2415.         {
  2416.             // We use $newvar here to prevent overwrite superglobals. See bug #28898.
  2417.             if ($vartype < TYPE_CONVERT_SINGLE)
  2418.             {
  2419.                 switch ($vartype)
  2420.                 {
  2421.                     case TYPE_INT:
  2422.                     case TYPE_UINT:
  2423.                     case TYPE_NUM:
  2424.                     case TYPE_UNUM:
  2425.                     case TYPE_UNIXTIME:
  2426.                     {
  2427.                         $newvar = 0;
  2428.                         break;
  2429.                     }
  2430.                     case TYPE_STR:
  2431.                     case TYPE_NOHTML:
  2432.                     case TYPE_NOTRIM:
  2433.                     case TYPE_NOHTMLCOND:
  2434.                     {
  2435.                         $newvar = '';
  2436.                         break;
  2437.                     }
  2438.                     case TYPE_BOOL:
  2439.                     {
  2440.                         $newvar = 0;
  2441.                         break;
  2442.                     }
  2443.                     case TYPE_ARRAY:
  2444.                     case TYPE_FILE:
  2445.                     {
  2446.                         $newvar = array();
  2447.                         break;
  2448.                     }
  2449.                     case TYPE_NOCLEAN:
  2450.                     {
  2451.                         $newvar = null;
  2452.                         break;
  2453.                     }
  2454.                     default:
  2455.                     {
  2456.                         $newvar = null;
  2457.                     }
  2458.                 }
  2459.             }
  2460.             else
  2461.             {
  2462.                 $newvar = array();
  2463.             }
  2464.  
  2465.             return $newvar;
  2466.         }
  2467.     }
  2468.  
  2469.     /**
  2470.     * Does the actual work to make a variable safe
  2471.     *
  2472.     * @param    mixed   The data we want to make safe
  2473.     * @param    integer The type of the data
  2474.     *
  2475.     * @return   mixed
  2476.     */
  2477.     function &do_clean(&$data, $type)
  2478.     {
  2479.         static $booltypes = array('1', 'yes', 'y', 'true', 'on');
  2480.  
  2481.         switch ($type)
  2482.         {
  2483.             case TYPE_NUM:
  2484.             case TYPE_UNUM:
  2485.                 // Account for language specific separators
  2486.                 if (isset($this->registry->userinfo['lang_decimalsep']) AND $this->registry->userinfo['lang_decimalsep'] != '')
  2487.                 {
  2488.                     $data = strtr($data, array($this->registry->userinfo['lang_decimalsep'] => '.', $this->registry->userinfo['lang_thousandsep'] => ''));
  2489.                 }
  2490.         }
  2491.  
  2492.         switch ($type)
  2493.         {
  2494.             case TYPE_INT:    $data = intval($data);                                   break;
  2495.             case TYPE_UINT:   $data = ($data = intval($data)) < 0 ? 0 : $data;         break;
  2496.             case TYPE_NUM:    $data = strval($data) + 0;                               break;
  2497.             case TYPE_UNUM:   $data = strval($data) + 0;
  2498.                               $data = ($data < 0) ? 0 : $data;                         break;
  2499.             case TYPE_BINARY: $data = strval($data);                                   break;
  2500.             case TYPE_STR:    $data = trim(strval($data));                             break;
  2501.             case TYPE_NOTRIM: $data = strval($data);                                   break;
  2502.             case TYPE_NOHTML: $data = htmlspecialchars_uni(trim(strval($data)));       break;
  2503.             case TYPE_BOOL:   $data = in_array(strtolower($data), $booltypes) ? 1 : 0; break;
  2504.             case TYPE_ARRAY:  $data = (is_array($data)) ? $data : array();             break;
  2505.             case TYPE_NOHTMLCOND:
  2506.             {
  2507.                 $data = trim(strval($data));
  2508.                 if (strcspn($data, '<>"') < strlen($data) OR (strpos($data, '&') !== false AND !preg_match('/&(#[0-9]+|amp|lt|gt|quot);/si', $data)))
  2509.                 {
  2510.                     // data is not htmlspecialchars because it still has characters or entities it shouldn't
  2511.                     $data = htmlspecialchars_uni($data);
  2512.                 }
  2513.                 break;
  2514.             }
  2515.             case TYPE_FILE:
  2516.             {
  2517.                 // perhaps redundant :p
  2518.                 if (is_array($data))
  2519.                 {
  2520.                     if (is_array($data['name']))
  2521.                     {
  2522.                         $files = count($data['name']);
  2523.                         for ($index = 0; $index < $files; $index++)
  2524.                         {
  2525.                             $data['name']["$index"] = trim(strval($data['name']["$index"]));
  2526.                             $data['type']["$index"] = trim(strval($data['type']["$index"]));
  2527.                             $data['tmp_name']["$index"] = trim(strval($data['tmp_name']["$index"]));
  2528.                             $data['error']["$index"] = intval($data['error']["$index"]);
  2529.                             $data['size']["$index"] = intval($data['size']["$index"]);
  2530.                         }
  2531.                     }
  2532.                     else
  2533.                     {
  2534.                         $data['name'] = trim(strval($data['name']));
  2535.                         $data['type'] = trim(strval($data['type']));
  2536.                         $data['tmp_name'] = trim(strval($data['tmp_name']));
  2537.                         $data['error'] = intval($data['error']);
  2538.                         $data['size'] = intval($data['size']);
  2539.                     }
  2540.                 }
  2541.                 else
  2542.                 {
  2543.                     $data = array(
  2544.                         'name'     => '',
  2545.                         'type'     => '',
  2546.                         'tmp_name' => '',
  2547.                         'error'    => 0,
  2548.                         'size'     => 4, // UPLOAD_ERR_NO_FILE
  2549.                     );
  2550.                 }
  2551.                 break;
  2552.             }
  2553.             case TYPE_UNIXTIME:
  2554.             {
  2555.                 if (is_array($data))
  2556.                 {
  2557.                     $data = $this->clean($data, TYPE_ARRAY_UINT);
  2558.                     if ($data['month'] AND $data['day'] AND $data['year'])
  2559.                     {
  2560.                         require_once(DIR . '/includes/functions_misc.php');
  2561.                         $data = vbmktime($data['hour'], $data['minute'], $data['second'], $data['month'], $data['day'], $data['year']);
  2562.                     }
  2563.                     else
  2564.                     {
  2565.                         $data = 0;
  2566.                     }
  2567.                 }
  2568.                 else
  2569.                 {
  2570.                     $data = ($data = intval($data)) < 0 ? 0 : $data;
  2571.                 }
  2572.                 break;
  2573.             }
  2574.             // null actions should be deifned here so we can still catch typos below
  2575.             case TYPE_NOCLEAN:
  2576.             {
  2577.                 break;
  2578.             }
  2579.  
  2580.             default:
  2581.             {
  2582.                 if ($this->registry->debug)
  2583.                 {
  2584.                     trigger_error('vB_Input_Cleaner::do_clean() Invalid data type specified', E_USER_WARNING);
  2585.                 }
  2586.             }
  2587.         }
  2588.  
  2589.         // strip out characters that really have no business being in non-binary data
  2590.         switch ($type)
  2591.         {
  2592.             case TYPE_STR:
  2593.             case TYPE_NOTRIM:
  2594.             case TYPE_NOHTML:
  2595.             case TYPE_NOHTMLCOND:
  2596.                 $data = str_replace(chr(0), '', $data);
  2597.         }
  2598.  
  2599.         return $data;
  2600.     }
  2601.  
  2602.     /**
  2603.     * Removes HTML characters and potentially unsafe scripting words from a string
  2604.     *
  2605.     * @param    string  The variable we want to make safe
  2606.     *
  2607.     * @return   string
  2608.     */
  2609.     function xss_clean($var)
  2610.     {
  2611.         static
  2612.             $preg_find    = array('#^javascript#i', '#^vbscript#i'),
  2613.             $preg_replace = array('java script',   'vb script');
  2614.  
  2615.         return preg_replace($preg_find, $preg_replace, htmlspecialchars(trim($var)));
  2616.     }
  2617.  
  2618.     /**
  2619.      * Removes HTML characters and potentially unsafe scripting words from a URL
  2620.      * Note: The query string is preserved.
  2621.      *
  2622.      * @param   string  The url to clean
  2623.      * @return  string
  2624.      */
  2625.     function xss_clean_url($url)
  2626.     {
  2627.         $query = parse_url($url, PHP_URL_QUERY);
  2628.         $fragment = parse_url($url, PHP_URL_FRAGMENT);
  2629.         $clean_url = false;
  2630.  
  2631.         if ($query)
  2632.         {
  2633.             $url = substr($url, 0, strpos($url, '?'));
  2634.             $url = $this->xss_clean($url);
  2635.             $clean_url = true;
  2636.         }
  2637.  
  2638.         if ($fragment AND !$clean_url)
  2639.         {
  2640.             $url = substr($url, 0, strpos($url, '#'));
  2641.             $url = $this->xss_clean($url);
  2642.             $clean_url = true;
  2643.         }
  2644.  
  2645.         if (!$clean_url)
  2646.         {
  2647.             $url = $this->xss_clean($url);
  2648.         }
  2649.  
  2650.         $query = ($query) ? '?' . $query : '';
  2651.         $fragment = ($fragment) ? '#' . $fragment : '';
  2652.         $url = $url . $query . $fragment;
  2653.  
  2654.         return $url;
  2655.     }
  2656.  
  2657.  
  2658.     /**
  2659.      * Cleans a query string.
  2660.      * Unicode is decoded, url entities are kept encoded, and slashes are preserved.
  2661.      *
  2662.      * @param string $path
  2663.      * @return string
  2664.      */
  2665.     function utf8_clean_path($path, $reencode = true)
  2666.     {
  2667.         $path = explode('/', $path);
  2668.         $path = array_map('urldecode', $path);
  2669.  
  2670.         if ($reencode)
  2671.         {
  2672.             $path = array_map('urlencode_uni', $path);
  2673.         }
  2674.  
  2675.         $path = implode('/', $path);
  2676.  
  2677.         return $path;
  2678.     }
  2679.  
  2680.  
  2681.     /**
  2682.     * Reverses the effects of magic_quotes on an entire array of variables
  2683.     *
  2684.     * @param    array   The array on which we want to work
  2685.     */
  2686.     function stripslashes_deep(&$value, $depth = 0)
  2687.     {
  2688.         if (is_array($value))
  2689.         {
  2690.             foreach ($value AS $key => $val)
  2691.             {
  2692.                 if (is_string($val))
  2693.                 {
  2694.                     $value["$key"] = stripslashes($val);
  2695.                 }
  2696.                 else if (is_array($val) AND $depth < 10)
  2697.                 {
  2698.                     $this->stripslashes_deep($value["$key"], $depth + 1);
  2699.                 }
  2700.             }
  2701.         }
  2702.     }
  2703.  
  2704.     /**
  2705.     * Turns $_POST['t'] into $_POST['threadid'] etc.
  2706.     *
  2707.     * @param    array   The name of the array
  2708.     */
  2709.     function convert_shortvars(&$array, $setglobals = true)
  2710.     {
  2711.         // extract long variable names from short variable names
  2712.         foreach ($this->shortvars AS $shortname => $longname)
  2713.         {
  2714.             if (isset($array["$shortname"]) AND !isset($array["$longname"]))
  2715.             {
  2716.                 $array["$longname"] =& $array["$shortname"];
  2717.                 if ($setglobals)
  2718.                 {
  2719.                     $GLOBALS['_REQUEST']["$longname"] =& $array["$shortname"];
  2720.                 }
  2721.             }
  2722.         }
  2723.     }
  2724.  
  2725.     /**
  2726.     * Strips out the s=gobbledygook& rubbish from URLs
  2727.     *
  2728.     * @param    string  The URL string from which to remove the session stuff
  2729.     *
  2730.     * @return   string
  2731.     */
  2732.     function strip_sessionhash($string)
  2733.     {
  2734.         $string = preg_replace('/(s|sessionhash)=[a-z0-9]{32}?&?/', '', $string);
  2735.         return $string;
  2736.     }
  2737.  
  2738.     /**
  2739.      * Fetches the 'basepath' variable that can be used as <base>.
  2740.      *
  2741.      * @return string
  2742.      */
  2743.     function fetch_basepath($rel_modifier = false)
  2744.     {
  2745.         if ($this->registry->basepath != '')
  2746.         {
  2747.             return $this->registry->basepath;
  2748.         }
  2749.  
  2750.         if ($this->registry->options['bburl_basepath'])
  2751.         {
  2752.             $basepath = trim($this->registry->options['bburl'], '/\\') . '/';
  2753.         }
  2754.         else
  2755.         {
  2756.             $basepath = VB_URL_BASE_PATH;
  2757.         }
  2758.  
  2759.         return $basepath = $basepath . ($rel_modifier ? $this->xss_clean($rel_modifier) : '');
  2760.     }
  2761.  
  2762.     /**
  2763.      * Fetches the path for the current request relative to the basepath.
  2764.      * This is useful for local anchors (<a href="{vb:raw relpath}#post">).
  2765.      *
  2766.      * Substracts any overlap between basepath and path with the following results:
  2767.      *
  2768.      *      base:       http://www.example.com/forums/
  2769.      *      path:       /forums/content.php
  2770.      *      result:     content.php
  2771.      *
  2772.      *      base:       http://www.example.com/forums/admincp
  2773.      *      path:       /forums/content/1-Article
  2774.      *      result:     ../content/1-Article
  2775.      *
  2776.      * @return string
  2777.      */
  2778.     function fetch_relpath($path = false)
  2779.     {
  2780.         if (!$path AND ($this->registry->relpath != ''))
  2781.         {
  2782.             return $this->registry->relpath;
  2783.         }
  2784.  
  2785.         // if no path specified, use the request path
  2786.         if (!$path)
  2787.         {
  2788.             if ($_SERVER['REQUEST_METHOD'] == 'POST' AND $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' AND $_POST['relpath'])
  2789.             {
  2790.                 $relpath = unhtmlspecialchars($_POST['relpath']);
  2791.                 $query = '';
  2792.             }
  2793.             else
  2794.             {
  2795.             $relpath = VB_URL_PATH;
  2796.             $query = VB_URL_QUERY;
  2797.         }
  2798.         }
  2799.         else
  2800.         {
  2801.             // if the path is already absolute there's nothing to do
  2802.             if (strpos($path, '://'))
  2803.             {
  2804.                 return $path;
  2805.             }
  2806.  
  2807.             if (!$path)
  2808.             {
  2809.                 return $path;
  2810.             }
  2811.  
  2812.             $relpath = parse_url($path, PHP_URL_PATH);
  2813.             $query = parse_url($path, PHP_URL_QUERY);
  2814.             $fragment = parse_url($path, PHP_URL_FRAGMENT);
  2815.         }
  2816.  
  2817.         $relpath = ltrim($relpath, '/');
  2818.         $basepath = @parse_url($this->fetch_basepath(), PHP_URL_PATH);
  2819.         $basepath = trim($basepath, '/');
  2820.  
  2821.         // get path segments for comparison
  2822.         $relpath = explode('/', $relpath);
  2823.         $basepath = explode('/', $basepath);
  2824.  
  2825.         // remove segments that basepath and relpath share
  2826.         foreach ($basepath AS $segment)
  2827.         {
  2828.             if ($segment == current($relpath))
  2829.             {
  2830.                 array_shift($basepath);
  2831.                 array_shift($relpath);
  2832.             }
  2833.             else
  2834.             {
  2835.                 break;
  2836.             }
  2837.         }
  2838.  
  2839.         // rebuild the relpath
  2840.         $relpath = implode('/', $relpath);
  2841.  
  2842.         /*
  2843.         // if basepath is in another dir, back out of it
  2844.         if ($diff = sizeof($basepath))
  2845.         {
  2846.             $relpath = str_repeat('../', $diff) . $relpath;
  2847.         }
  2848.         */
  2849.  
  2850.         // add the query string if the current path is being used
  2851.         if ($query)
  2852.         {
  2853.             $relpath = $this->add_query($relpath, $query);
  2854.         }
  2855.  
  2856.         // add the fragment back
  2857.         if ($fragment)
  2858.         {
  2859.             $relpath = $this->add_fragment($relpath, $fragment);
  2860.         }
  2861.  
  2862.         return $relpath;
  2863.     }
  2864.  
  2865.  
  2866.     /**
  2867.     * Fetches the 'wolpath' variable - ie: the same as 'scriptpath' but with a handler for the POST request method
  2868.     *
  2869.     * @return   string
  2870.     */
  2871.     function fetch_wolpath()
  2872.     {
  2873.         $wolpath = SCRIPTPATH;
  2874.  
  2875.         if ($_SERVER['REQUEST_METHOD'] == 'POST')
  2876.         {
  2877.             // Tag the variables back on to the filename if we are coming from POST so that WOL can access them.
  2878.             $tackon = '';
  2879.  
  2880.             if (is_array($_POST))
  2881.             {
  2882.                 foreach ($_POST AS $varname => $value)
  2883.                 {
  2884.                     switch ($varname)
  2885.                     {
  2886.                         case 'forumid':
  2887.                         case 'threadid':
  2888.                         case 'postid':
  2889.                         case 'userid':
  2890.                         case 'eventid':
  2891.                         case 'calendarid':
  2892.                         case 'do':
  2893.                         case 'method': // postings.php
  2894.                         case 'dowhat': // private.php
  2895.                         {
  2896.                             $tackon .= ($tackon == '' ? '' : '&amp;') . $varname . '=' . $value;
  2897.                             break;
  2898.                         }
  2899.                     }
  2900.                 }
  2901.             }
  2902.             if ($tackon != '')
  2903.             {
  2904.                 $wolpath .= (strpos($wolpath, '?') !== false ? '&amp;' : '?') . "$tackon";
  2905.             }
  2906.         }
  2907.  
  2908.         return $wolpath;
  2909.     }
  2910.  
  2911.     /**
  2912.     * Fetches the 'url' variable - usually the URL of the previous page in the history
  2913.     *
  2914.     * @return   string
  2915.     */
  2916.     function fetch_url()
  2917.     {
  2918.         $scriptpath = SCRIPTPATH;
  2919.  
  2920.         //note regarding the default url if not set or inappropriate.
  2921.         //started out as index.php then moved to options['forumhome'] . '.php' when that option was added.
  2922.         //now we've changed to to the forumhome url since there is now quite a bit of logic around that.
  2923.         //Its not clear, however, with the expansion of vb if that's the most appropriate generic landing
  2924.         //place (perhaps it *should* be index.php).
  2925.         //In any case there are several places in the code that check for the default page url and change it
  2926.         //to something more appropriate.  If the default url changes, so do those checks.
  2927.         //The solution is, most likely, to make some note when vbulletin->url is the default so it can be overridden
  2928.         //without worrying about what the exact text is.
  2929.         if (empty($_REQUEST['url']))
  2930.         {
  2931.             $url = (!empty($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
  2932.         }
  2933.         else
  2934.         {
  2935.             $temp_url = $_REQUEST['url'];
  2936.             if (!empty($_SERVER['HTTP_REFERER']) AND $temp_url == $_SERVER['HTTP_REFERER'])
  2937.             {
  2938.                 //$url = 'index.php'
  2939.                 // I am unsure why we redirect to forumhome if we have $url defined and it matches HTTP_REFERRER
  2940.                 // Must be a security check that has been here since at least 2003
  2941.                 // So to keep from breaking something we will check if the url is something we know
  2942.                 $found = false;
  2943.                 $pathinfo = @parse_url($temp_url);
  2944.                 $options = array(
  2945.                         $this->registry->options['vbforum_url'],
  2946.                         $this->registry->options['vbblog_url'],
  2947.                         $this->registry->options['vbcms_url'],
  2948.                         $this->registry->options['bburl']
  2949.                 );
  2950.                 foreach($options AS $value)
  2951.                 {
  2952.                     if ($value AND $info = @parse_url($value))
  2953.                     {
  2954.                         if ("{$pathinfo['scheme']}://{$pathinfo['host']}" == "{$info['scheme']}://{$info['host']}")
  2955.                         {
  2956.                             $found = true;
  2957.                             $url = $temp_url;
  2958.                             break;
  2959.                         }
  2960.                     }
  2961.                 }
  2962.  
  2963.                 if (!$found)
  2964.                 {
  2965.                     $url = fetch_seo_url('forumhome|nosession', array());
  2966.                 }
  2967.             }
  2968.             else
  2969.             {
  2970.                 $url = $temp_url;
  2971.             }
  2972.         }
  2973.  
  2974.         if ($url == $scriptpath OR empty($url))
  2975.         {
  2976.             //$url = 'index.php';
  2977.             $url = fetch_seo_url('forumhome|nosession', array());
  2978.         }
  2979.  
  2980.         //not a lot of point in doing this as a seperate step.
  2981.         // if $url is set to forum home page, check it against options
  2982.         //if ($url == 'index.php' AND $this->registry->options['forumhome'] != 'index')
  2983.         //{
  2984.         //  $url = $this->registry->options['forumhome'] . '.php';
  2985.         //}
  2986.  
  2987.         $url = $this->xss_clean($url);
  2988.         return $url;
  2989.     }
  2990.  
  2991.     /**
  2992.     * Fetches the IP address of the current visitor
  2993.     *
  2994.     * @return   string
  2995.     */
  2996.     function fetch_ip()
  2997.     {
  2998.         return $_SERVER['REMOTE_ADDR'];
  2999.     }
  3000.  
  3001.     /**
  3002.     * Fetches an alternate IP address of the current visitor, attempting to detect proxies etc.
  3003.     *
  3004.     * @return   string
  3005.     */
  3006.     function fetch_alt_ip()
  3007.     {
  3008.         $alt_ip = $_SERVER['REMOTE_ADDR'];
  3009.  
  3010.         if (isset($_SERVER['HTTP_X_FORWARDED_FOR']))
  3011.         {
  3012.             $altip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  3013.         }
  3014.         else if (isset($_SERVER['HTTP_CLIENT_IP']))
  3015.         {
  3016.             $altip = $_SERVER['HTTP_CLIENT_IP'];
  3017.         }
  3018.         else if (isset($_SERVER['HTTP_FROM']))
  3019.         {
  3020.             $altip = $_SERVER['HTTP_FROM'];
  3021.         }
  3022.         else
  3023.         {
  3024.             $altip = false;
  3025.         }
  3026.  
  3027.         if ($altip AND $this->filter_ip($altip))
  3028.         {
  3029.             $alt_ip = $altip;
  3030.         }
  3031.  
  3032.         return $alt_ip;
  3033.     }
  3034.  
  3035.     /**
  3036.     * Validate the IP address (both ipv4 & ipv6)
  3037.     *
  3038.     * @return   string
  3039.     */
  3040.     function filter_ip($ip)
  3041.     {
  3042.         return filter_var($ip, FILTER_VALIDATE_IP);
  3043.     }
  3044. }
  3045.  
  3046. // #############################################################################
  3047. // data registry class
  3048.  
  3049. /**
  3050. * Class to store commonly-used variables
  3051. *
  3052. * @package  vBulletin
  3053. * @version  $Revision: 62833 $
  3054. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  3055. */
  3056. class vB_Registry
  3057. {
  3058.     // general objects
  3059.     /**
  3060.     * Datastore object.
  3061.     *
  3062.     * @var  vB_Datastore
  3063.     */
  3064.     var $datastore;
  3065.  
  3066.     /**
  3067.     * Input cleaner object.
  3068.     *
  3069.     * @var  vB_Input_Cleaner
  3070.     */
  3071.     var $input;
  3072.  
  3073.     /**
  3074.     * Database object.
  3075.     *
  3076.     * @var  vB_Database
  3077.     */
  3078.     var $db;
  3079.  
  3080.     // user/session related
  3081.     /**
  3082.     * Array of info about the current browsing user. In the case of a registered
  3083.     * user, this will be results of fetch_userinfo(). A guest will have slightly
  3084.     * different entries.
  3085.     *
  3086.     * @var  array
  3087.     */
  3088.     var $userinfo;
  3089.  
  3090.     /**
  3091.     * Session object.
  3092.     *
  3093.     * @var vB_Session
  3094.     */
  3095.     var $session;
  3096.  
  3097.     /**
  3098.     * Array of do actions that are exempt from checks
  3099.     *
  3100.     * @var array
  3101.     */
  3102.     var $csrf_skip_list = array();
  3103.  
  3104.     // configuration
  3105.     /**
  3106.     * Array of data from config.php.
  3107.     *
  3108.     * @var  array
  3109.     */
  3110.     var $config;
  3111.  
  3112.     // GPC input
  3113.     /**
  3114.     * Array of data that has been cleaned by the input cleaner.
  3115.     *
  3116.     * @var  array
  3117.     */
  3118.     var $GPC = array();
  3119.  
  3120.     /**
  3121.     * Array of booleans. When cleaning a variable, you often lose the ability
  3122.     * to determine if it was specified in the user's input. Entries in this
  3123.     * array are true if the variable existed before cleaning.
  3124.     *
  3125.     * @var  array
  3126.     */
  3127.     var $GPC_exists = array();
  3128.  
  3129.     /**
  3130.     * The size of the super global arrays.
  3131.     *
  3132.     * @var  array
  3133.     */
  3134.     var $superglobal_size = array();
  3135.  
  3136.     // single variables
  3137.     /**
  3138.     * IP Address of the current browsing user.
  3139.     *
  3140.     * @var  string
  3141.     */
  3142.     var $ipaddress;
  3143.  
  3144.     /**
  3145.     * Alternate IP for the browsing user. This attempts to use various HTTP headers
  3146.     * to find the real IP of a user that may be behind a proxy.
  3147.     *
  3148.     * @var  string
  3149.     */
  3150.     var $alt_ip;
  3151.  
  3152.     /**
  3153.     * The URL of the currently browsed page.
  3154.     *
  3155.     * @var  string
  3156.     */
  3157.     var $scriptpath;
  3158.  
  3159.     /**
  3160.      * The request basepath.
  3161.      * Use for <base>
  3162.      *
  3163.      * @var string
  3164.      */
  3165.     var $basepath;
  3166.  
  3167.     /**
  3168.     * Similar to the URL of the current page, but expands some items and includes
  3169.     * data submitted via POST. Used for Who's Online purposes.
  3170.     *
  3171.     * @var  string
  3172.     */
  3173.     var $wolpath;
  3174.  
  3175.     /**
  3176.     * The URL of the current page, without anything after the '?'.
  3177.     *
  3178.     * @var  string
  3179.     */
  3180.     var $script;
  3181.  
  3182.     /**
  3183.     * Generally the URL of the referring page if there is one, though it is often
  3184.     * set in various places of the code. Used to determine the page to redirect
  3185.     * to, if necessary.
  3186.     *
  3187.     * @var  string
  3188.     */
  3189.     var $url;
  3190.  
  3191.     // usergroup permission bitfields
  3192.     /**#@+
  3193.     * Bitfield arrays for usergroup permissions.
  3194.     *
  3195.     * @var  array
  3196.     */
  3197.     var $bf_ugp;
  3198.     // $bf_ugp_x is a reference to $bf_ugp['x']
  3199.     var $bf_ugp_adminpermissions;
  3200.     var $bf_ugp_calendarpermissions;
  3201.     var $bf_ugp_forumpermissions;
  3202.     var $bf_ugp_genericoptions;
  3203.     var $bf_ugp_genericpermissions;
  3204.     var $bf_ugp_pmpermissions;
  3205.     var $bf_ugp_wolpermissions;
  3206.     var $bf_ugp_visitormessagepermissions;
  3207.     /**#@-*/
  3208.  
  3209.     // misc bitfield arrays
  3210.     /**#@+
  3211.     * Bitfield arrays for miscellaneous permissions and options.
  3212.     *
  3213.     * @var  array
  3214.     */
  3215.     var $bf_misc;
  3216.     // $bf_misc_x is a reference to $bf_misc['x']
  3217.     var $bf_misc_calmoderatorpermissions;
  3218.     var $bf_misc_forumoptions;
  3219.     var $bf_misc_intperms;
  3220.     var $bf_misc_languageoptions;
  3221.     var $bf_misc_moderatorpermissions;
  3222.     var $bf_misc_useroptions;
  3223.     var $bf_misc_hvcheck;
  3224.     /**#@-*/
  3225.  
  3226.     /**#@+
  3227.     * Results for specific entries in the datastore.
  3228.     *
  3229.     * @var  mixed   Mixed, though mostly arrays.
  3230.     */
  3231.     var $options = null;
  3232.     var $attachmentcache = null;
  3233.     var $avatarcache = null;
  3234.     var $birthdaycache = null;
  3235.     var $eventcache = null;
  3236.     var $forumcache = null;
  3237.     var $iconcache = null;
  3238.     var $markupcache = null;
  3239.     var $stylecache = null;
  3240.     var $languagecache = null;
  3241.     var $smiliecache = null;
  3242.     var $usergroupcache = null;
  3243.     var $bbcodecache = null;
  3244.     var $socialsitecache = null;
  3245.     var $cron = null;
  3246.     var $mailqueue = null;
  3247.     var $banemail = null;
  3248.     var $maxloggedin = null;
  3249.     var $pluginlist = null;
  3250.     var $products = null;
  3251.     var $ranks = null;
  3252.     var $statement = null;
  3253.     var $userstats = null;
  3254.     var $wol_spiders = null;
  3255.     var $loadcache = null;
  3256.     var $noticecache = null;
  3257.     var $prefixcache = null;
  3258.     /**#@-*/
  3259.  
  3260.     /**#@+
  3261.     * Miscellaneous variables
  3262.     *
  3263.     * @var  mixed
  3264.     */
  3265.     var $bbcode_style = array('code' => -1, 'html' => -1, 'php' => -1, 'quote' => -1);
  3266.     var $templatecache = array();
  3267.     var $iforumcache = array();
  3268.     var $versionnumber;
  3269.     var $nozip;
  3270.     var $debug;
  3271.     var $noheader;
  3272.     public $stylevars;
  3273.  
  3274.     /**
  3275.      * Shutdown handler
  3276.      *
  3277.      * @var vB_Shutdown
  3278.      */
  3279.     var $shutdown;
  3280.     /**#@-*/
  3281.  
  3282.     /**
  3283.     * For storing global information specific to the CMS
  3284.     *
  3285.     * @var  array
  3286.     */
  3287.     var $vbcms = array();
  3288.  
  3289.  
  3290.     /**
  3291.     * For storing information of the API Client
  3292.     *
  3293.     * @var  array
  3294.     */
  3295.     var $apiclient = array();
  3296.  
  3297.     /**
  3298.     * Constructor - initializes the nozip system,
  3299.     * and calls and instance of the vB_Input_Cleaner class
  3300.     */
  3301.     function vB_Registry()
  3302.     {
  3303.         // variable to allow bypassing of gzip compression
  3304.         $this->nozip = defined('NOZIP') ? true : (@ini_get('zlib.output_compression') ? true : false);
  3305.         // variable that controls HTTP header output
  3306.         $this->noheader = defined('NOHEADER') ? true : false;
  3307.  
  3308.         @ini_set('zend.ze1_compatibility_mode', 0);
  3309.  
  3310.         // initialize the input handler
  3311.         $this->input = new vB_Input_Cleaner($this);
  3312.  
  3313.         // initialize the shutdown handler
  3314.         $this->shutdown = vB_Shutdown::instance();
  3315.  
  3316.         $this->csrf_skip_list = (defined('CSRF_SKIP_LIST') ? explode(',', CSRF_SKIP_LIST) : array());
  3317.     }
  3318.  
  3319.     /**
  3320.     * Fetches database/system configuration
  3321.     */
  3322.     function fetch_config()
  3323.     {
  3324.         // parse the config file
  3325.         $config = array();
  3326.         include(CWD . '/includes/config.php');
  3327.  
  3328.         if (sizeof($config) == 0)
  3329.         {
  3330.             if (file_exists(CWD. '/includes/config.php'))
  3331.             {
  3332.                 // config.php exists, but does not define $config
  3333.                 die('<br /><br /><strong>Configuration</strong>: includes/config.php exists, but is not in the 3.6+ format. Please convert your config file via the new config.php.new.');
  3334.             }
  3335.             else
  3336.             {
  3337.                 die('<br /><br /><strong>Configuration</strong>: includes/config.php does not exist. Please fill out the data in config.php.new and rename it to config.php');
  3338.             }
  3339.         }
  3340.  
  3341.         $this->config =& $config;
  3342.         // if a configuration exists for this exact HTTP host, use it
  3343.         if (isset($this->config["$_SERVER[HTTP_HOST]"]))
  3344.         {
  3345.             $this->config['MasterServer'] = $this->config["$_SERVER[HTTP_HOST]"];
  3346.         }
  3347.  
  3348.         // define table and cookie prefix constants
  3349.         define('TABLE_PREFIX', trim($this->config['Database']['tableprefix']));
  3350.         define('COOKIE_PREFIX', (empty($this->config['Misc']['cookieprefix']) ? 'bb' : $this->config['Misc']['cookieprefix']) . '_');
  3351.  
  3352.         // set debug mode
  3353.         $this->debug = !empty($this->config['Misc']['debug']);
  3354.         define('DEBUG', $this->debug);
  3355.  
  3356.         $proxy = false;
  3357.         if (isset($this->config['Misc']['proxyiplist']))
  3358.         {
  3359.             $proxylist = array_map('trim', explode(',', $this->config['Misc']['proxyiplist']));
  3360.  
  3361.             if (in_array($this->ipaddress, $proxylist))
  3362.             {
  3363.                 $proxy = true;
  3364.                 if (isset($this->config['Misc']['proxyipheader'])
  3365.                 AND isset($_SERVER[$this->config['Misc']['proxyipheader']]))
  3366.                 {
  3367.                     $altip = $_SERVER[$this->config['Misc']['proxyipheader']];
  3368.                     if ($this->input->filter_ip($altip))
  3369.                     {
  3370.                         $this->alt_ip = $altip;
  3371.                     }
  3372.                 }
  3373.             }
  3374.         }
  3375.  
  3376.         if ($proxy)
  3377.         {
  3378.             define('ALT_IP', $this->ipaddress);
  3379.             define('IPADDRESS', $this->alt_ip);
  3380.         }
  3381.         else
  3382.         {
  3383.             define('IPADDRESS', $this->ipaddress);
  3384.             define('ALT_IP', $this->alt_ip);
  3385.         }
  3386.        
  3387.         define('SESSION_HOST',   substr(IPADDRESS, 0, 15));
  3388.     }
  3389.  
  3390.     /**
  3391.     * Takes the contents of an array and recursively uses each title/data
  3392.     * pair to create a new defined constant.
  3393.     */
  3394.     function array_define($array)
  3395.     {
  3396.         foreach ($array AS $title => $data)
  3397.         {
  3398.             if (is_array($data))
  3399.             {
  3400.                 vB_Registry::array_define($data);
  3401.             }
  3402.             else
  3403.             {
  3404.                 define(strtoupper($title), $data);
  3405.             }
  3406.         }
  3407.     }
  3408.  
  3409.     /**
  3410.     *   Check if a user has a specific permission
  3411.     *
  3412.     *   This is intended to replace direct acces to the userinfo['permissions'] array.
  3413.     *
  3414.     * For example:
  3415.     * $vbulletin->check_user_permission('genericpermissions', 'cancreatetag')
  3416.     *
  3417.     * which replaces
  3418.   * ($vbulletin->userinfo['permissions']['genericpermissions'] &
  3419.     *  $vbulletin->bf_ugp_genericpermissions['cancreatetag'])
  3420.     *
  3421.     *   @param string $group the permission group to check
  3422.     * @param string $permission the permission to check within the group
  3423.     * @return bool If the user has the requested permission
  3424.     */
  3425.     public function check_user_permission($group, $permission)
  3426.     {
  3427.         return (bool) ($this->userinfo['permissions'][$group] &
  3428.             $this->{'bf_ugp_' . $group}[$permission]);
  3429.     }
  3430. }
  3431.  
  3432. // #############################################################################
  3433. // session management class
  3434.  
  3435. /**
  3436. * Class to handle sessions
  3437. *
  3438. * Creates, updates, and validates sessions; retrieves user info of browsing user
  3439. *
  3440. * @package  vBulletin
  3441. * @version  $Revision: 62833 $
  3442. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  3443. */
  3444. class vB_Session
  3445. {
  3446.     /**
  3447.     * The individual session variables. Equivalent to $session from the past.
  3448.     *
  3449.     * @var  array
  3450.     */
  3451.     var $vars = array();
  3452.  
  3453.     /**
  3454.     * A list of variables in the $vars member that are in the database. Includes their types.
  3455.     *
  3456.     * @var  array
  3457.     */
  3458.     var $db_fields = array(
  3459.         'sessionhash'   => TYPE_STR,
  3460.         'userid'        => TYPE_INT,
  3461.         'host'          => TYPE_STR,
  3462.         'idhash'        => TYPE_STR,
  3463.         'lastactivity'  => TYPE_INT,
  3464.         'location'      => TYPE_STR,
  3465.         'styleid'       => TYPE_INT,
  3466.         'languageid'    => TYPE_INT,
  3467.         'loggedin'      => TYPE_INT,
  3468.         'inforum'       => TYPE_INT,
  3469.         'inthread'      => TYPE_INT,
  3470.         'incalendar'    => TYPE_INT,
  3471.         'badlocation'   => TYPE_INT,
  3472.         'useragent'     => TYPE_STR,
  3473.         'bypass'        => TYPE_INT,
  3474.         'profileupdate' => TYPE_INT,
  3475.         'apiclientid'   => TYPE_INT,
  3476.         'apiaccesstoken'=> TYPE_STR,
  3477.     );
  3478.  
  3479.     /**
  3480.     * An array of changes. Used to prevent superfluous updates from being made.
  3481.     *
  3482.     * @var  array
  3483.     */
  3484.     var $changes = array();
  3485.  
  3486.     /**
  3487.     * Whether the session was created or existed previously
  3488.     *
  3489.     * @var  bool
  3490.     */
  3491.     var $created = false;
  3492.  
  3493.     /**
  3494.     * Reference to a vB_Registry object that keeps various data we need.
  3495.     *
  3496.     * @var  vB_Registry
  3497.     */
  3498.     var $registry = null;
  3499.  
  3500.     /**
  3501.     * Information about the user that this session belongs to.
  3502.     *
  3503.     * @var  array
  3504.     */
  3505.     var $userinfo = null;
  3506.  
  3507.     /**
  3508.     * Is the sessionhash to be passed through URLs?
  3509.     *
  3510.     * @var  boolean
  3511.     */
  3512.     var $visible = true;
  3513.  
  3514.     /**
  3515.     * Constructor. Attempts to grab a session that matches parameters, but will create one if it can't.
  3516.     *
  3517.     * @param    vB_Registry Reference to a registry object
  3518.     * @param    string      Previously specified sessionhash
  3519.     * @param    integer     User ID (passed in through a cookie)
  3520.     * @param    string      Password, must arrive in cookie format: md5(md5(md5(password) . salt) . 'abcd1234')
  3521.     * @param    integer     Style ID for this session
  3522.     * @param    integer     Language ID for this session
  3523.     */
  3524.     function vB_Session(&$registry, $sessionhash = '', $userid = 0, $password = '', $styleid = 0, $languageid = 0)
  3525.     {
  3526.         $userid = intval($userid);
  3527.         $styleid = intval($styleid);
  3528.         $languageid = intval($languageid);
  3529.  
  3530.         $this->registry =& $registry;
  3531.         $db =& $this->registry->db;
  3532.         $gotsession = false;
  3533.  
  3534.         $this->registry->input->clean_gpc('r', 'api');
  3535.         if (!defined('SESSION_IDHASH'))
  3536.         {
  3537.             if (!VB_API AND !$this->registry->GPC['api'])
  3538.             {
  3539.                 define('SESSION_IDHASH', md5($_SERVER['HTTP_USER_AGENT'] . $this->fetch_substr_ip($this->getIp()))); // this should *never* change during a session
  3540.             }
  3541.             else
  3542.             {
  3543.                 define('SESSION_IDHASH', md5($this->fetch_substr_ip($this->getIp()))); // API session idhash won't have User Agent compiled.
  3544.             }
  3545.         }
  3546.  
  3547.         if (!defined('SKIP_SESSIONCREATE'))
  3548.         {
  3549.             $test = false;
  3550.  
  3551.             if (defined('UNIT_TESTING') AND UNIT_TESTING === true)
  3552.             {
  3553.                 $test = ($session = $db->query_first_slave("
  3554.                     SELECT *
  3555.                     FROM " . TABLE_PREFIX . "session
  3556.                     WHERE sessionhash = '" . $db->escape_string($sessionhash) . "'
  3557.                         AND lastactivity > " . (TIMENOW - $registry->options['cookietimeout']) . "
  3558.                 "));
  3559.             }
  3560.             // apiaccesstoken specified, so see if it already exists
  3561.             elseif (defined('VB_API') AND VB_API === true AND $this->registry->apiclient['apiaccesstoken'])
  3562.             {
  3563.                 $test = ($session = $db->query_first_slave("
  3564.                     SELECT *
  3565.                     FROM " . TABLE_PREFIX . "session
  3566.                     WHERE apiaccesstoken = '" . $db->escape_string($this->registry->apiclient['apiaccesstoken']) . "'
  3567.                         AND lastactivity > " . (TIMENOW - $registry->options['cookietimeout']) . "
  3568.                         AND idhash = '" . $this->registry->db->escape_string(SESSION_IDHASH) . "'
  3569.                 ") AND $this->fetch_substr_ip($session['host']) == $this->fetch_substr_ip($this->getIp()));
  3570.             }
  3571.             // sessionhash specified, so see if it already exists
  3572.             elseif ($sessionhash)
  3573.             {
  3574.                 $test = ($session = $db->query_first_slave("
  3575.                     SELECT *
  3576.                     FROM " . TABLE_PREFIX . "session
  3577.                     WHERE sessionhash = '" . $db->escape_string($sessionhash) . "'
  3578.                         AND lastactivity > " . (TIMENOW - $registry->options['cookietimeout']) . "
  3579.                         AND idhash = '" . $this->registry->db->escape_string(SESSION_IDHASH) . "'
  3580.                 ") AND $this->fetch_substr_ip($session['host']) == $this->fetch_substr_ip($this->getIp()));
  3581.             }
  3582.  
  3583.             if ($test)
  3584.             {
  3585.                 $gotsession = true;
  3586.                 $this->vars =& $session;
  3587.                 $this->created = false;
  3588.  
  3589.                 // found a session - get the userinfo
  3590.                 if ($session['userid'] != 0)
  3591.                 {
  3592.                     $useroptions = (defined('IN_CONTROL_PANEL') ? 16 : 0) + (defined('AVATAR_ON_NAVBAR') ? 2 : 0);
  3593.                     $userinfo = fetch_userinfo($session['userid'], $useroptions, (!empty($languageid) ? $languageid : $session['languageid']));
  3594.                     $this->userinfo =& $userinfo;
  3595.                 }
  3596.             }
  3597.         }
  3598.  
  3599.         // API 'Remember Me'. UserID is stored in apiclient table.
  3600.         if (($gotsession == false OR empty($session['userid'])) AND defined('VB_API') AND VB_API === true AND $this->registry->apiclient['userid'] AND !defined('SKIP_SESSIONCREATE'))
  3601.         {
  3602.             $useroptions = (defined('IN_CONTROL_PANEL') ? FETCH_USERINFO_ADMIN : 0) + (defined('AVATAR_ON_NAVBAR') ? FETCH_USERINFO_AVATAR : 0);
  3603.             $userinfo = fetch_userinfo($this->registry->apiclient['userid'], $useroptions, $languageid);
  3604.  
  3605.             $gotsession = true;
  3606.  
  3607.             // combination is valid
  3608.             if (!empty($session['sessionhash']))
  3609.             {
  3610.                 // old session still exists; kill it
  3611.                 $db->shutdown_query("
  3612.                     DELETE FROM " . TABLE_PREFIX . "session
  3613.                     WHERE sessionhash = '" . $this->registry->db->escape_string($session['sessionhash']). "'
  3614.                 ");
  3615.             }
  3616.  
  3617.             $this->vars = $this->fetch_session($userinfo['userid']);
  3618.             $this->created = true;
  3619.  
  3620.             $this->userinfo =& $userinfo;
  3621.         }
  3622.  
  3623.         // or maybe we can use a cookie..
  3624.         if (($gotsession == false OR empty($session['userid'])) AND $userid AND $password AND !defined('SKIP_SESSIONCREATE') AND !VB_API)
  3625.         {
  3626.             $useroptions = (defined('IN_CONTROL_PANEL') ? FETCH_USERINFO_ADMIN : 0) + (defined('AVATAR_ON_NAVBAR') ? FETCH_USERINFO_AVATAR : 0);
  3627.             $userinfo = fetch_userinfo($userid, $useroptions, $languageid);
  3628.  
  3629.             if (md5($userinfo['password'] . COOKIE_SALT) == $password)
  3630.             {
  3631.                 $gotsession = true;
  3632.  
  3633.                 // combination is valid
  3634.                 if (!empty($session['sessionhash']))
  3635.                 {
  3636.                     // old session still exists; kill it
  3637.                     $db->shutdown_query("
  3638.                         DELETE FROM " . TABLE_PREFIX . "session
  3639.                         WHERE sessionhash = '" . $this->registry->db->escape_string($session['sessionhash']). "'
  3640.                     ");
  3641.                 }
  3642.  
  3643.                 $this->vars = $this->fetch_session($userinfo['userid']);
  3644.                 $this->created = true;
  3645.  
  3646.                 $this->userinfo =& $userinfo;
  3647.             }
  3648.         }
  3649.  
  3650.         // at this point, we're a guest, so lets try to *find* a session
  3651.         // you can prevent this check from being run by passing in a userid with no password
  3652.         if ($gotsession == false AND $userid == 0 AND !defined('SKIP_SESSIONCREATE'))
  3653.         {
  3654.             if ($session = $db->query_first_slave("
  3655.                 SELECT *
  3656.                 FROM " . TABLE_PREFIX . "session
  3657.                 WHERE userid = 0
  3658.                     AND host = '" . $this->registry->db->escape_string($this->getIp()) . "'
  3659.                     AND idhash = '" . $this->registry->db->escape_string(SESSION_IDHASH) . "'
  3660.                 LIMIT 1
  3661.             "))
  3662.             {
  3663.                 $gotsession = true;
  3664.  
  3665.                 $this->vars =& $session;
  3666.                 $this->created = false;
  3667.             }
  3668.         }
  3669.  
  3670.         // well, nothing worked, time to create a new session
  3671.         if ($gotsession == false)
  3672.         {
  3673.             $gotsession = true;
  3674.  
  3675.             $this->vars = $this->fetch_session(0);
  3676.             $this->created = true;
  3677.         }
  3678.  
  3679.         $this->vars['dbsessionhash'] = $this->vars['sessionhash'];
  3680.  
  3681.         $this->set('styleid', $styleid);
  3682.         $this->set('languageid', $languageid);
  3683.         if ($this->created == false)
  3684.         {
  3685.             $this->set('useragent', USER_AGENT);
  3686.             $this->set('lastactivity', TIMENOW);
  3687.             if (!defined('LOCATION_BYPASS'))
  3688.             {
  3689.                 $this->set('location', WOLPATH);
  3690.             }
  3691.             $this->set('bypass', SESSION_BYPASS);
  3692.         }
  3693.     }
  3694.  
  3695.     /**
  3696.     * Saves the session into the database by inserting it or updating an existing one.
  3697.     */
  3698.     function save()
  3699.     {
  3700.         if (defined('SKIP_SESSIONCREATE'))
  3701.         {
  3702.             return;
  3703.         }
  3704.  
  3705.         $cleaned = $this->build_query_array();
  3706.  
  3707.         // since the sessionhash can be blanked out, lets make sure we pull from "dbsessionhash"
  3708.         $cleaned['sessionhash'] = "'" . $this->registry->db->escape_string($this->vars['dbsessionhash']) . "'";
  3709.  
  3710.         if ($this->created == true)
  3711.         {
  3712.             if($this->registry->options['enablespiders'])//VBIV-5766
  3713.             {
  3714.                 require_once(DIR . '/includes/class_xml.php');
  3715.                 $xmlobj = new vB_XML_Parser(false, DIR . '/includes/xml/spiders_vbulletin.xml');
  3716.                 $spiderdata = $xmlobj->parse();
  3717.                 $spiders = "";
  3718.  
  3719.                 if (is_array($spiderdata['spider']))
  3720.                 {
  3721.                     foreach ($spiderdata['spider'] AS $spiderling)
  3722.                     {
  3723.                         $spiders .= ($spiders ? '|' : '') . preg_quote($spiderling['ident'], '#');
  3724.                     }
  3725.                 }
  3726.  
  3727.                 unset($spiderdata, $xmlobj);
  3728.  
  3729.                 //isbot to distinguish between bots and guests in session table VBIV-5766
  3730.                 if (preg_match('#(' . $spiders . ')#si', $cleaned['useragent']))
  3731.                 {
  3732.                     $cleaned['isbot'] = true;
  3733.                 }
  3734.             }// end VBIV-5766
  3735.  
  3736.             /*insert query*/
  3737.             $this->registry->db->query_write("
  3738.                 INSERT IGNORE INTO " . TABLE_PREFIX . "session
  3739.                     (" . implode(', ', array_keys($cleaned)) . ")
  3740.                 VALUES
  3741.                     (" . implode(', ', $cleaned) . ")
  3742.             ");
  3743.         }
  3744.         else
  3745.         {
  3746.             // update query
  3747.  
  3748.             unset($this->changes['sessionhash']); // the sessionhash is not updateable
  3749.             $update = array();
  3750.             foreach ($cleaned AS $key => $value)
  3751.             {
  3752.                 if (!empty($this->changes["$key"]))
  3753.                 {
  3754.                     $update[] = "$key = $value";
  3755.                 }
  3756.             }
  3757.  
  3758.             if (sizeof($update) > 0)
  3759.             {
  3760.                 // note that $cleaned['sessionhash'] has been escaped as necessary above!
  3761.                 $this->registry->db->query_write("
  3762.                     UPDATE " . TABLE_PREFIX . "session
  3763.                     SET " . implode(', ', $update) . "
  3764.                     WHERE sessionhash = $cleaned[sessionhash]
  3765.                 ");
  3766.             }
  3767.         }
  3768.  
  3769.         $this->changes = array();
  3770.     }
  3771.  
  3772.     /**
  3773.     * Builds an array that can be used to build a query to insert/update the session
  3774.     *
  3775.     * @return   array   Array of column name => prepared value
  3776.     */
  3777.     function build_query_array()
  3778.     {
  3779.         $return = array();
  3780.         foreach ($this->db_fields AS $fieldname => $cleantype)
  3781.         {
  3782.             switch ($cleantype)
  3783.             {
  3784.                 case TYPE_INT:
  3785.                     $cleaned = intval($this->vars["$fieldname"]);
  3786.                     break;
  3787.                 case TYPE_STR:
  3788.                 default:
  3789.                     $cleaned = "'" . $this->registry->db->escape_string($this->vars["$fieldname"]) . "'";
  3790.             }
  3791.             $return["$fieldname"] = $cleaned;
  3792.         }
  3793.  
  3794.         return $return;
  3795.     }
  3796.  
  3797.     /**
  3798.     * Sets a session variable and updates the change list.
  3799.     *
  3800.     * @param    string  Name of session variable to update
  3801.     * @param    string  Value to update it with
  3802.     */
  3803.     function set($key, $value)
  3804.     {
  3805.         if (!isset($this->vars["$key"]) OR $this->vars["$key"] != $value)
  3806.         {
  3807.             $this->vars["$key"] = $value;
  3808.             $this->changes["$key"] = true;
  3809.         }
  3810.     }
  3811.  
  3812.     /**
  3813.     * Sets the session visibility (whether session info shows up in a URL). Updates are put in the $vars member.
  3814.     *
  3815.     * @param    bool    Whether the session elements should be visible.
  3816.     */
  3817.     function set_session_visibility($invisible)
  3818.     {
  3819.         $this->visible = !$invisible;
  3820.  
  3821.         if ($invisible)
  3822.         {
  3823.             $this->vars['sessionhash'] = '';
  3824.             $this->vars['sessionurl'] = '';
  3825.             $this->vars['sessionurl_q'] = '';
  3826.             $this->vars['sessionurl_js'] = '';
  3827.         }
  3828.         else
  3829.         {
  3830.             if (!VB_API)
  3831.             {
  3832.                 $this->vars['sessionurl'] = 's=' . $this->vars['dbsessionhash'] . '&amp;';
  3833.                 $this->vars['sessionurl_q'] = '?s=' . $this->vars['dbsessionhash'];
  3834.                 $this->vars['sessionurl_js'] = 's=' . $this->vars['dbsessionhash'] . '&';
  3835.             }
  3836.             else
  3837.             {
  3838.                 $this->vars['sessionurl'] = 's=' . $this->vars['dbsessionhash'] . '&amp;api=1&amp;';
  3839.                 $this->vars['sessionurl_q'] = '?s=' . $this->vars['dbsessionhash'] . '&amp;api=1';
  3840.                 $this->vars['sessionurl_js'] = 's=' . $this->vars['dbsessionhash'] . '&api=1&';
  3841.             }
  3842.         }
  3843.     }
  3844.  
  3845.     /**
  3846.     * Fetches a valid sessionhash value, not necessarily the one tied to this session.
  3847.     *
  3848.     * @return   string  32-character sessionhash
  3849.     */
  3850.     function fetch_sessionhash()
  3851.     {
  3852.         return md5(uniqid(microtime(), true));
  3853.     }
  3854.  
  3855.     /**
  3856.     * Returns the IP address with the specified number of octets removed
  3857.     *
  3858.     * @param    string  IP address
  3859.     *
  3860.     * @return   string  truncated IP address
  3861.     */
  3862.     function fetch_substr_ip($ip, $length = null)
  3863.     {
  3864.         if ($length === null OR $length > 3)
  3865.         {
  3866.             $length = $this->registry->options['ipcheck'];
  3867.         }
  3868.         return implode('.', array_slice(explode('.', $ip), 0, 4 - $length));
  3869.     }
  3870.  
  3871.     /**
  3872.     * Fetches a default session. Used when creating a new session.
  3873.     *
  3874.     * @param    integer User ID the session should be for
  3875.     *
  3876.     * @return   array   Array of session variables
  3877.     */
  3878.     function fetch_session($userid = 0)
  3879.     {
  3880.         $sessionhash = $this->fetch_sessionhash();
  3881.         if (!defined('SKIP_SESSIONCREATE'))
  3882.         {
  3883.             vbsetcookie('sessionhash', $sessionhash, false, false, true);
  3884.         }
  3885.  
  3886.         $session = array(
  3887.             'sessionhash'   => $sessionhash,
  3888.             'dbsessionhash' => $sessionhash,
  3889.             'userid'        => intval($userid),
  3890.             'host'          => $this->getIp(),
  3891.             'idhash'        => SESSION_IDHASH,
  3892.             'lastactivity'  => TIMENOW,
  3893.             'location'      => defined('LOCATION_BYPASS') ? '' : WOLPATH,
  3894.             'styleid'       => 0,
  3895.             'languageid'    => 0,
  3896.             'loggedin'      => intval($userid) ? 1 : 0,
  3897.             'inforum'       => 0,
  3898.             'inthread'      => 0,
  3899.             'incalendar'    => 0,
  3900.             'badlocation'   => 0,
  3901.             'profileupdate' => 0,
  3902.             'useragent'     => USER_AGENT,
  3903.             'bypass'        => SESSION_BYPASS
  3904.         );
  3905.  
  3906.         if (defined('VB_API') AND VB_API === true)
  3907.         {
  3908.             if ($this->registry->apiclient['apiaccesstoken'])
  3909.             {
  3910.                 // Access Token is valid here because it's validated in init.php
  3911.                 $accesstoken = $this->registry->apiclient['apiaccesstoken'];
  3912.             }
  3913.             else
  3914.             {
  3915.                 // Generate an accesstoken
  3916.                 $accesstoken = fetch_random_string();
  3917.  
  3918.                 $this->registry->apiclient['apiaccesstoken'] = $accesstoken;
  3919.             }
  3920.  
  3921.             $session['apiaccesstoken'] = $accesstoken;
  3922.  
  3923.             if ($this->registry->apiclient['apiclientid'])
  3924.             {
  3925.                 $session['apiclientid'] = intval($this->registry->apiclient['apiclientid']);
  3926.                 // Save accesstoken to apiclient table
  3927.                 $this->registry->db->query_write("UPDATE " . TABLE_PREFIX . "apiclient SET
  3928.                     apiaccesstoken = '" . $this->registry->db->escape_string($accesstoken) . "',
  3929.                     lastactivity = " . TIMENOW . "
  3930.                     WHERE apiclientid = $session[apiclientid]");
  3931.             }
  3932.         }
  3933.  
  3934.         ($hook = vBulletinHook::fetch_hook('fetch_session_complete')) ? eval($hook) : false;
  3935.  
  3936.         return $session;
  3937.  
  3938.     }
  3939.  
  3940.     /**
  3941.     * Returns ip for this session
  3942.     *
  3943.     * @return   string  ip
  3944.     */
  3945.     private function getIp()
  3946.     {
  3947.         if(isset($this->registry->options['facebookapp_ip'])) {
  3948.             $ips = explode(',', $this->registry->options['facebookapp_ip']);
  3949.             foreach($ips as $ip) {
  3950.                 if(trim($ip) == $this->registry->ipaddress) {
  3951.                     return $this->registry->alt_ip;
  3952.                 }
  3953.             }
  3954.         }
  3955.         return SESSION_HOST;
  3956.     }
  3957.  
  3958.     /**
  3959.     * Returns appropriate user info for the owner of this session.
  3960.     *
  3961.     * @return   array   Array of user information.
  3962.     */
  3963.     function &fetch_userinfo()
  3964.     {
  3965.         if ($this->userinfo)
  3966.         {
  3967.             // we already calculated this
  3968.             return $this->userinfo;
  3969.         }
  3970.         else if ($this->vars['userid'] AND !defined('SKIP_USERINFO'))
  3971.         {
  3972.             // user is logged in
  3973.             $useroptions = (defined('IN_CONTROL_PANEL') ? FETCH_USERINFO_ADMIN : 0) + (defined('AVATAR_ON_NAVBAR') ? FETCH_USERINFO_AVATAR : 0);
  3974.             $this->userinfo = fetch_userinfo($this->vars['userid'], $useroptions, $this->vars['languageid']);
  3975.             return $this->userinfo;
  3976.         }
  3977.         else
  3978.         {
  3979.             // guest setup
  3980.             $this->userinfo = array(
  3981.                 'userid'         => 0,
  3982.                 'usergroupid'    => 1,
  3983.                 'username'       => (!empty($_REQUEST['username']) ? htmlspecialchars_uni($_REQUEST['username']) : ''),
  3984.                 'password'       => '',
  3985.                 'email'          => '',
  3986.                 'styleid'        => $this->vars['styleid'],
  3987.                 'languageid'     => $this->vars['languageid'],
  3988.                 'lastactivity'   => $this->vars['lastactivity'],
  3989.                 'daysprune'      => 0,
  3990.                 'timezoneoffset' => $this->registry->options['timeoffset'],
  3991.                 'dstonoff'       => $this->registry->options['dstonoff'],
  3992.                 'showsignatures' => 1,
  3993.                 'showavatars'    => 1,
  3994.                 'showimages'     => 1,
  3995.                 'showusercss'    => 1,
  3996.                 'dstauto'        => 0,
  3997.                 'maxposts'       => -1,
  3998.                 'startofweek'    => 1,
  3999.                 'threadedmode'   => $this->registry->options['threadedmode'],
  4000.                 'securitytoken'  => 'guest',
  4001.                 'securitytoken_raw'  => 'guest',
  4002.                 'realstyleid'    => $this->registry->options['styleid'],
  4003.             );
  4004.  
  4005.             $this->userinfo['options'] =
  4006.                 $this->registry->bf_misc_useroptions['showsignatures'] | $this->registry->bf_misc_useroptions['showavatars'] |
  4007.                 $this->registry->bf_misc_useroptions['showimages'] | $this->registry->bf_misc_useroptions['dstauto'] |
  4008.                 $this->registry->bf_misc_useroptions['showusercss'];
  4009.  
  4010.             if (!defined('SKIP_USERINFO'))
  4011.             {
  4012.                 // get default language
  4013.                 $phraseinfo = $this->registry->db->query_first_slave("
  4014.                     SELECT languageid" . fetch_language_fields_sql(0) . "
  4015.                     FROM " . TABLE_PREFIX . "language
  4016.                     WHERE languageid = " . (!empty($this->vars['languageid']) ? $this->vars['languageid'] : intval($this->registry->options['languageid'])) . "
  4017.                 ");
  4018.                 if (empty($phraseinfo))
  4019.                 { // can't phrase this since we can't find the language
  4020.                     trigger_error('The requested language does not exist, reset via tools.php.', E_USER_ERROR);
  4021.                 }
  4022.                 foreach($phraseinfo AS $_arrykey => $_arryval)
  4023.                 {
  4024.                     $this->userinfo["$_arrykey"] = $_arryval;
  4025.                 }
  4026.                 unset($phraseinfo);
  4027.             }
  4028.  
  4029.             return $this->userinfo;
  4030.         }
  4031.     }
  4032.  
  4033.     /**
  4034.     * Updates the last visit and last activity times for guests and registered users (differently).
  4035.     * Last visit is set to the last activity time (before it's updated) only when a certain
  4036.     * time has lapsed. Last activity is always set to the specified time.
  4037.     *
  4038.     * @param    integer Time stamp for last visit time (guest only)
  4039.     * @param    integer Time stamp for last activity time (guest only)
  4040.     */
  4041.     function do_lastvisit_update($lastvisit = 0, $lastactivity = 0)
  4042.     {
  4043.         // update last visit/activity stuff
  4044.         if ($this->vars['userid'] == 0)
  4045.         {
  4046.             // guest -- emulate last visit/activity for registered users by cookies
  4047.             if ($lastvisit)
  4048.             {
  4049.                 // we've been here before
  4050.                 $this->userinfo['lastvisit'] = intval($lastvisit);
  4051.                 $this->userinfo['lastactivity'] = ($lastvisit ? intval($lastvisit) : TIMENOW);
  4052.  
  4053.                 // here's the emulation
  4054.                 if (TIMENOW - $this->userinfo['lastactivity'] > $this->registry->options['cookietimeout'])
  4055.                 {
  4056.                     $this->userinfo['lastvisit'] = $this->userinfo['lastactivity'];
  4057.  
  4058.                     vbsetcookie('lastvisit', $this->userinfo['lastactivity']);
  4059.                 }
  4060.             }
  4061.             else
  4062.             {
  4063.                 // first visit!
  4064.                 $this->userinfo['lastactivity'] = TIMENOW;
  4065.                 $this->userinfo['lastvisit'] = TIMENOW;
  4066.  
  4067.                 vbsetcookie('lastvisit', TIMENOW);
  4068.             }
  4069.             vbsetcookie('lastactivity', $lastactivity);
  4070.         }
  4071.         else
  4072.         {
  4073.             // registered user
  4074.             if (!SESSION_BYPASS)
  4075.             {
  4076.                 if (TIMENOW - $this->userinfo['lastactivity'] > $this->registry->options['cookietimeout'])
  4077.                 {
  4078.                     // see if session has 'expired' and if new post indicators need resetting
  4079.                     $this->registry->db->shutdown_query("
  4080.                         UPDATE " . TABLE_PREFIX . "user
  4081.                         SET
  4082.                             lastvisit = lastactivity,
  4083.                             lastactivity = " . TIMENOW . "
  4084.                         WHERE userid = " . $this->userinfo['userid'] . "
  4085.                     ", 'lastvisit');
  4086.  
  4087.                     $this->userinfo['lastvisit'] = $this->userinfo['lastactivity'];
  4088.                 }
  4089.                 else
  4090.                 {
  4091.                     // if this line is removed (say to be replaced by a cron job, you will need to change all of the 'online'
  4092.                     // status indicators as they use $userinfo['lastactivity'] to determine if a user is online which relies
  4093.                     // on this to be updated in real time.
  4094.                     $this->registry->db->shutdown_query("
  4095.                         UPDATE " . TABLE_PREFIX . "user
  4096.                         SET lastactivity = " . TIMENOW . "
  4097.                         WHERE userid = " . $this->userinfo['userid'] . "
  4098.                     ", 'lastvisit');
  4099.                 }
  4100.             }
  4101.         }
  4102.     }
  4103. }
  4104.  
  4105. /**
  4106. * Class to handle shutdown
  4107. *
  4108. * @package  vBulletin
  4109. * @version  $Revision: 62833 $
  4110. * @author   vBulletin Development Team
  4111. * @date     $Date: 2012-05-19 14:21:12 -0700 (Sat, 19 May 2012) $
  4112. */
  4113. class vB_Shutdown
  4114. {
  4115.     /**
  4116.      * A reference to the singleton instance
  4117.      *
  4118.      * @var vB_Cache_Observer
  4119.      */
  4120.     protected static $instance;
  4121.  
  4122.     /**
  4123.      * An array of shutdown callbacks to call on shutdown
  4124.      */
  4125.     protected $callbacks;
  4126.  
  4127.     /**
  4128.      * Constructor protected to enforce singleton use.
  4129.      * @see instance()
  4130.      */
  4131.     protected function __construct(){}
  4132.  
  4133.     /**
  4134.      * Returns singleton instance of self.
  4135.      *
  4136.      * @return vB_Shutdown
  4137.      */
  4138.     public function instance()
  4139.     {
  4140.         if (!isset(self::$instance))
  4141.         {
  4142.             $class = __CLASS__;
  4143.             self::$instance = new $class();
  4144.         }
  4145.  
  4146.         return self::$instance;
  4147.     }
  4148.  
  4149.     /**
  4150.     * Add callback to be executed at shutdown
  4151.     *
  4152.     * @param array $callback                    - Call back to call on shutdown
  4153.     */
  4154.     public function add($callback)
  4155.     {
  4156.         if (!is_array($this->callbacks))
  4157.         {
  4158.             $this->callbacks = array();
  4159.         }
  4160.  
  4161.         $this->callbacks[] = $callback;
  4162.     }
  4163.  
  4164.     // only called when an object is destroyed, so $this is appropriate
  4165.     public function shutdown()
  4166.     {
  4167.         if (sizeof($this->callbacks))
  4168.         {
  4169.             foreach ($this->callbacks AS $callback)
  4170.             {
  4171.                 call_user_func($callback);
  4172.             }
  4173.  
  4174.             unset($this->callbacks);
  4175.         }
  4176.     }
  4177.  
  4178.     public function __wakeup()
  4179.     {
  4180.         unset($this->callbacks);
  4181.     }
  4182. }
  4183.  
  4184. /**
  4185. * This class implements variable-registration-based template evaluation,
  4186. * wrapped around the legacy template format. It will be extended in the
  4187. * future to support the new format/syntax without requiring changes to
  4188. * code written with it.
  4189. *
  4190. * Currently these vars are automatically registered: $vbphrase
  4191. *    $show, $bbuserinfo, $session, $vboptions
  4192. *
  4193. * @package  vBulletin
  4194. */
  4195. class vB_Template
  4196. {
  4197.     /**
  4198.      * Preregistered variables.
  4199.      * Variables can be preregistered before a template is created and will be
  4200.      * imported and reset when the template is created.
  4201.      * The array should be in the form array(template_name => array(key => variable))
  4202.      *
  4203.      * @var array mixed
  4204.      */
  4205.     protected static $pre_registered = array();
  4206.  
  4207.     /**
  4208.     * Name of the template to render
  4209.     *
  4210.     * @var  string
  4211.     */
  4212.     protected $template = '';
  4213.  
  4214.     /**
  4215.      * Array of registered variables.
  4216.      * @see vB_Template::preRegister()
  4217.     *
  4218.     * @var  array
  4219.     */
  4220.     protected $registered = array();
  4221.  
  4222.     /**
  4223.      * Whether the globally accessible vars have been registered.
  4224.      *
  4225.      * @var bool
  4226.      */
  4227.     protected $registered_globals;
  4228.  
  4229.     /**
  4230.     * Debug helper to count how many times a template was used on a page.
  4231.     *
  4232.     * @var  array
  4233.     */
  4234.     public static $template_usage = array();
  4235.  
  4236.     /**
  4237.     * Debug helper to list the templates that were fetched out of the database (not cached properly).
  4238.     *
  4239.     * @var  array
  4240.     */
  4241.     public static $template_queries = array();
  4242.  
  4243.     /**
  4244.      * Hook code for register.
  4245.      * @see vB_Template::register()
  4246.      *
  4247.      * @var string
  4248.      */
  4249.     protected static $hook_code = false;
  4250.  
  4251.  
  4252.  
  4253.     /**
  4254.      * Factory method to create the template object.
  4255.      * Will choose the correct template type based on the request. Any preregistered
  4256.      * variables are also registered and cleared from the preregister cache.
  4257.     *
  4258.     * @param    string  Name of the template to be evaluated
  4259.     * @return   vB_Template Template object
  4260.     */
  4261.     public static function create($template_name, $forcenoapi = false)
  4262.     {
  4263.         static $output_type;
  4264.  
  4265.         if (defined('VB_API') AND VB_API AND !$forcenoapi)
  4266.         {
  4267.             // TODO: Use an option to enable/disable the api output
  4268.  
  4269.             if (!isset($output_type))
  4270.             {
  4271. //              global $vbulletin;
  4272. //
  4273. //              $vbulletin->input->clean_gpc('r', 'api');
  4274. //              $output_type = in_array($vbulletin->GPC['api'], array('xml', 'json')) ? $vbulletin->GPC['api'] : 'json';
  4275.                 // Currently we support json only
  4276.                 $output_type = 'json';
  4277.             }
  4278.  
  4279.             if ($output_type == 'xml')
  4280.             {
  4281.                 $template = new vB_Template_XML($template_name);
  4282.             }
  4283.             else
  4284.             {
  4285.                 $template = new vB_Template_JSON($template_name);
  4286.             }
  4287.  
  4288.             if (!VB_API_CMS)
  4289.             {
  4290.                 global $show;
  4291.                 $copyofshow = $show;
  4292.                 self::remove_common_show($copyofshow);
  4293.                 $template->register('show', $copyofshow);
  4294.             }
  4295.         }
  4296.         else
  4297.         {
  4298.             $template = new vB_Template($template_name);
  4299.         }
  4300.  
  4301.         if (isset(self::$pre_registered[$template_name]))
  4302.         {
  4303.             $template->quickRegister(self::$pre_registered[$template_name]);
  4304.             // TODO: Reinstate once search uses a single template object
  4305.             // unset(self::$pre_registered[$template_name]);
  4306.         }
  4307.  
  4308.         return $template;
  4309.     }
  4310.  
  4311.     /**
  4312.      * Unset common items in $show array for API
  4313.      */
  4314.     protected static function remove_common_show(&$show)
  4315.     {
  4316.         // Unset common show variables
  4317.         unset(
  4318.             $show['old_explorer'], $show['rtl'], $show['admincplink'], $show['modcplink'],
  4319.             $show['registerbutton'], $show['searchbuttons'], $show['quicksearch'],
  4320.             $show['memberslist'], $show['guest'], $show['member'], $show['popups'],
  4321.             $show['nojs_link'], $show['pmwarning'], $show['pmstats'], $show['pmmainlink'],
  4322.             $show['pmtracklink'], $show['pmsendlink'], $show['siglink'], $show['avatarlink'],
  4323.             $show['detailedtime'], $show['profilepiclink'], $show['wollink'], $show['spacer'],
  4324.             $show['dst_correction'], $show['contactus'], $show['nopasswordempty'],
  4325.             $show['quick_links_groups'], $show['quick_links_albums'], $show['friends_and_contacts'],
  4326.             $show['communitylink'], $show['search_engine'], $show['editor_css']
  4327.         );
  4328.     }
  4329.  
  4330.     /**
  4331.      * Protected constructor to enforce the factory pattern.
  4332.      * Ensures the chrome templates have been processed.
  4333.     */
  4334.     protected function __construct($template_name)
  4335.     {
  4336.         global $bootstrap;
  4337.  
  4338.         if (!empty($bootstrap) AND !$bootstrap->called('template'))
  4339.         {
  4340.             $bootstrap->process_templates();
  4341.         }
  4342.  
  4343.         $this->template = $template_name;
  4344.     }
  4345.  
  4346.     /**
  4347.     * Returns the name of the template that will be rendered.
  4348.     *
  4349.     * @return   string
  4350.     */
  4351.     public function get_template_name()
  4352.     {
  4353.         return $this->template;
  4354.     }
  4355.  
  4356.     /**
  4357.      * Preregisters variables before template instantiation.
  4358.      *
  4359.      * @param   string  The name of the template to register for
  4360.      * @param   array   The variables to register
  4361.      */
  4362.     public static function preRegister($template_name, array $variables = NULL)
  4363.     {
  4364.         if ($variables)
  4365.         {
  4366.             if (!isset(self::$pre_registered[$template_name]))
  4367.             {
  4368.                 self::$pre_registered[$template_name] = array();
  4369.             }
  4370.  
  4371.             self::$pre_registered[$template_name] = array_merge(self::$pre_registered[$template_name], $variables);
  4372.         }
  4373.     }
  4374.  
  4375.     /**
  4376.     * Register a variable with the template.
  4377.     *
  4378.     * @param    string  Name of the variable to be registered
  4379.     * @param    mixed   Value to be registered. This may be a scalar or an array.
  4380.      * @param   bool    Whether to overwrite existing vars
  4381.      * @return  bool    Whether the var was registered
  4382.     */
  4383.     public function register($name, $value, $overwrite = true)
  4384.     {
  4385.         if (!$overwrite AND $this->is_registered($name))
  4386.         {
  4387.             return false;
  4388.         }
  4389.  
  4390.         // Run register hook
  4391.         self::assert_register_hook();
  4392.  
  4393.         if (self::$hook_code)
  4394.         {
  4395.             eval(self::$hook_code);
  4396.         }
  4397.  
  4398.         $this->registered[$name] = $value;
  4399.  
  4400.         return true;
  4401.     }
  4402.  
  4403.     /**
  4404.      * Registers an array of variables with the template.
  4405.      *
  4406.      * @param   mixed   Assoc array of name => value to be registered
  4407.      */
  4408.     public function quickRegister($values, $overwrite = true)
  4409.     {
  4410.         if (!is_array($values))
  4411.         {
  4412.             return;
  4413.         }
  4414.  
  4415.         foreach ($values AS $name => $value)
  4416.         {
  4417.             $this->register($name, $value, $overwrite);
  4418.         }
  4419.     }
  4420.  
  4421.     /**
  4422.      * Registers a named global variable with the template.
  4423.      *
  4424.      * @param   string  The global to register
  4425.      * @param   bool    Whether to overwrite on a name collision
  4426.      */
  4427.     public function register_global($name, $overwrite = true)
  4428.     {
  4429.         if (!$overwrite AND $this->is_registered($name))
  4430.         {
  4431.             return false;
  4432.         }
  4433.  
  4434.         return isset($GLOBALS[$name]) ? $this->register_ref($name, $GLOBALS[$name]) : false;
  4435.     }
  4436.  
  4437.     /**
  4438.      * Registers a reference to a variable.
  4439.      *
  4440.      * @param   string  Name of the variable to be registered
  4441.      * @param   mixed   Value to be registered. This may be a scalar or an array
  4442.      * @param   bool    Whether to overwrite existing vars
  4443.      * @return  bool    Whether the var was registered
  4444.      */
  4445.     public function register_ref($name, &$value, $overwrite = true)
  4446.     {
  4447.         if (!$overwrite AND $this->is_registered($name))
  4448.         {
  4449.             return false;
  4450.         }
  4451.  
  4452.         // Run register hook
  4453.         self::assert_register_hook();
  4454.  
  4455.         if (self::$hook_code)
  4456.         {
  4457.             eval(self::$hook_code);
  4458.         }
  4459.  
  4460.         $this->registered[$name] =& $value;
  4461.  
  4462.         return true;
  4463.     }
  4464.  
  4465.     /**
  4466.     * Unregisters a previously registered variable.
  4467.     *
  4468.     * @param    string  Name of variable to be unregistered
  4469.     * @return   mixed   Null if the variable wasn't registered, otherwise the value of the variable
  4470.     */
  4471.     public function unregister($name)
  4472.     {
  4473.         if (isset($this->registered[$name]))
  4474.         {
  4475.             $value = $this->registered[$name];
  4476.             unset($this->registered[$name]);
  4477.             return $value;
  4478.         }
  4479.         else
  4480.         {
  4481.             return null;
  4482.         }
  4483.     }
  4484.  
  4485.     /**
  4486.      * Determines if a named variable is registered.
  4487.     *
  4488.     * @param    string  Name of variable to check
  4489.     * @return   bool
  4490.     */
  4491.     public function is_registered($name)
  4492.     {
  4493.         return isset($this->registered[$name]);
  4494.     }
  4495.  
  4496.     /**
  4497.     * Return the value of a registered variable or all registered values
  4498.      * If no variable name is specified then all variables are returned.
  4499.     *
  4500.     * @param    string  The name of the variable to get the value for.
  4501.     * @return   mixed   If a name is specified, the value of the variable or null if it doesn't exist.
  4502.     */
  4503.     public function registered($name = '')
  4504.     {
  4505.         if ($name !== '')
  4506.         {
  4507.             return (isset($this->registered[$name]) ? $this->registered[$name] : null);
  4508.         }
  4509.         else
  4510.         {
  4511.             return $this->registered;
  4512.         }
  4513.     }
  4514.  
  4515.     /**
  4516.      * Caches the register var hook code locally.
  4517.      */
  4518.     protected static function assert_register_hook()
  4519.     {
  4520.         if (self::$hook_code === false AND class_exists('vBulletinHook', false))
  4521.         {
  4522.             self::$hook_code = vBulletinHook::fetch_hook('template_register_var');
  4523.         }
  4524.     }
  4525.  
  4526.     /**
  4527.     * Automatically register the page-level templates footer, header,
  4528.     * and headinclude based on their global values.
  4529.     */
  4530.     public function register_page_templates()
  4531.     {
  4532.         // Only method forum requires these templates
  4533.         if (defined('VB_API') AND VB_API === true AND VB_ENTRY !== 'forum.php')
  4534.         {
  4535.             return true;
  4536.         }
  4537.  
  4538.         $this->register_global('footer');
  4539.         $this->register_global('header');
  4540.         $this->register_global('headinclude');
  4541.         $this->register_global('headinclude_bottom');
  4542.  
  4543.         ($hook = vBulletinHook::fetch_hook('page_templates')) ? eval($hook) : false;
  4544.     }
  4545.  
  4546.     /**
  4547.      * Register globally accessible vars.
  4548.      *
  4549.      * @param bool $final_render                - Whether we are rendering the final response
  4550.     */
  4551.     protected function register_globals($final_render = false)
  4552.     {
  4553.         if ($this->registered_globals)
  4554.         {
  4555.             return;
  4556.         }
  4557.         $this->registered_globals = true;
  4558.  
  4559.         global $vbulletin, $style;
  4560.  
  4561.         $this->register_ref('bbuserinfo', $vbulletin->userinfo);
  4562.         $this->register_ref('vboptions', $vbulletin->options);
  4563.         $this->register_ref('session', $vbulletin->session->vars);
  4564.         $this->register('relpath', htmlspecialchars($vbulletin->input->fetch_relpath()));
  4565.  
  4566.         $this->register_global('vbphrase');
  4567.         $this->register_global('vbcollapse');
  4568.         $this->register_global('ad_location');
  4569.         $this->register_global('style');
  4570.  
  4571.         $this->register_global('show', false);
  4572.         $this->register_global('template_hook', false);
  4573.  
  4574.         $vbcsspath = vB_Template::fetch_css_path();
  4575.         $this->register('vbcsspath', $vbcsspath);
  4576.         $this->register('yui_version', YUI_VERSION);
  4577.  
  4578.         if ($vbulletin->products['vbcms'])
  4579.         {
  4580.             $this->register('vb_suite_installed', true);
  4581.         }
  4582.  
  4583.         // If we're using bgclass, we might be using exec_switch_bg()
  4584.         // but we can only be sure if we match the global value.
  4585.         // A hack that will hopefully go away.
  4586.         if (isset($bgclass) AND $bgclass == $GLOBALS['bgclass'])
  4587.         {
  4588.             $this->register_ref('bgclass', $GLOBALS['bgclass']);
  4589.         }
  4590.     }
  4591.  
  4592.  
  4593.     /**
  4594.      * Renders the template.
  4595.      *
  4596.      * @param   boolean Whether to suppress the HTML comment surrounding option (for JS, etc)
  4597.      * @return  string  Rendered version of the template
  4598.      */
  4599.     public function render($suppress_html_comments = false, $final_render = false)
  4600.     {
  4601.         // Register globally accessible data
  4602.         $this->register_globals($final_render);
  4603.  
  4604.         // Render the output in the appropriate format
  4605.         return $this->render_output($suppress_html_comments);
  4606.     }
  4607.  
  4608.  
  4609.     /**
  4610.      * Renders the output after preperation.
  4611.      * @see vB_Template::render()
  4612.      *
  4613.      * @param boolean   Whether to suppress the HTML comment surrounding option (for JS, etc)
  4614.      * @return string
  4615.      */
  4616.     protected function render_output($suppress_html_comments = false)
  4617.     {
  4618.         //This global statement is here to expose $vbulletin to the templates.
  4619.         //It must remain in the same function as the template eval
  4620.         global $vbulletin;
  4621.         extract($this->registered, EXTR_SKIP | EXTR_REFS);
  4622.  
  4623.         $actioned = false;
  4624.         ($hook = vBulletinHook::fetch_hook('template_render_output')) ? eval($hook) : false;
  4625.  
  4626.         if (!$actioned)
  4627.         {
  4628.             $template_code = self::fetch_template($this->template);
  4629.         }
  4630.  
  4631.         if (strpos($template_code, '$final_rendered') !== false)
  4632.         {
  4633.             eval($template_code);
  4634.         }
  4635.         else
  4636.         {
  4637.             eval('$final_rendered = "' . $template_code . '";');
  4638.         }
  4639.  
  4640.         if ($vbulletin->options['addtemplatename'] AND !$suppress_html_comments)
  4641.         {
  4642.             $template_name = preg_replace('#[^a-z0-9_]#i', '', $this->template);
  4643.             $final_rendered = "<!-- BEGIN TEMPLATE: $template_name -->\n$final_rendered\n<!-- END TEMPLATE: $template_name -->";
  4644.         }
  4645.  
  4646.         return $final_rendered;
  4647.     }
  4648.  
  4649.  
  4650.     /**
  4651.     * Returns the CSS path needed for the {vb:cssfile} template tag
  4652.     *
  4653.     * @return   string  CSS path
  4654.     */
  4655.     public static function fetch_css_path()
  4656.     {
  4657.         global $vbulletin, $style, $foruminfo;
  4658.  
  4659.         if ($vbulletin->options['storecssasfile'])
  4660.         {
  4661.             $vbcsspath = 'clientscript/vbulletin_css/style' . str_pad($style['styleid'], 5, '0', STR_PAD_LEFT) . $vbulletin->stylevars['textdirection']['string'][0] . '/';
  4662.         }
  4663.         else
  4664.         {
  4665.             // Forum ID added when in forums with style overrides and the "Allow Users To Change Styles"
  4666.             // option is off, otherwise the requested styleid will be denied. Not added across the board
  4667.             // to ensure the highest cache hit rate possible. Not needed when CSS is stored as files.
  4668.             // See bug: VBIV-5647
  4669.             $forumid = intval($foruminfo['forumid']);
  4670.             $forum_styleid = intval($foruminfo['styleid']);
  4671.             if (!$vbulletin->options['allowchangestyles'] AND $forumid > 0 AND $forum_styleid > 0)
  4672.             {
  4673.                 $add_forumid = '&amp;forumid=' . $forumid;
  4674.             }
  4675.             else
  4676.             {
  4677.                 $add_forumid = '';
  4678.             }
  4679.  
  4680.             // textdirection var added to prevent cache if admin modified language text_direction. See bug #32640
  4681.             $vbcsspath = 'css.php?styleid=' . $style['styleid'] . $add_forumid . '&amp;langid=' . LANGUAGEID . '&amp;d=' . $style['dateline'] . '&amp;td=' . $vbulletin->stylevars['textdirection']['string'] . '&amp;sheet=';
  4682.         }
  4683.  
  4684.         return $vbulletin->options['cssurl'] . $vbcsspath;
  4685.     }
  4686.  
  4687.     /**
  4688.     * Returns a single template from the templatecache or the database and returns
  4689.     * the raw contents of it. Note that text will be escaped for eval'ing.
  4690.     *
  4691.     * @param    string  Name of template to be fetched
  4692.     *
  4693.     * @return   string
  4694.     */
  4695.     public static function fetch_template_raw($template_name)
  4696.     {
  4697.         $template_code = self::fetch_template($template_name);
  4698.  
  4699.         if (strpos($template_code, '$final_rendered') !== false)
  4700.         {
  4701.             return preg_replace('#^\$final_rendered = \'(.*)\';$#s', '\\1', $template_code);
  4702.         }
  4703.         else
  4704.         {
  4705.             return $template_code;
  4706.         }
  4707.     }
  4708.  
  4709.     /**
  4710.     * Returns a single template from the templatecache or the database
  4711.     *
  4712.     * @param    string  Name of template to be fetched
  4713.     *
  4714.     * @return   string
  4715.     */
  4716.     protected static function fetch_template($template_name)
  4717.     {
  4718.         global $vbulletin, $tempusagecache, $templateassoc;
  4719.  
  4720.         // use legacy postbit if necessary
  4721.         if ($vbulletin->options['legacypostbit'] AND $template_name == 'postbit')
  4722.         {
  4723.             $template_name = 'postbit_legacy';
  4724.         }
  4725.  
  4726.         $fetched = false;
  4727.         ($hook = vBulletinHook::fetch_hook('fetch_template_start')) ? eval($hook) : false;
  4728.  
  4729.         if (!$fetched)
  4730.         {
  4731.             if (isset($vbulletin->templatecache["$template_name"]))
  4732.             {
  4733.                 $template = $vbulletin->templatecache["$template_name"];
  4734.             }
  4735.             else
  4736.             {
  4737.                 self::$template_queries[$template_name] = true;
  4738.                 $fetch_tid = intval($templateassoc["$template_name"]);
  4739.                 if (!$fetch_tid)
  4740.                 {
  4741.                     $gettemp = array('template' => '');
  4742.                 }
  4743.                 else
  4744.                 {
  4745.                     $gettemp = $vbulletin->db->query_first_slave("
  4746.                         SELECT template
  4747.                         FROM " . TABLE_PREFIX . "template
  4748.                         WHERE templateid = $fetch_tid
  4749.                     ");
  4750.                 }
  4751.                 $template = $gettemp['template'];
  4752.                 $vbulletin->templatecache["$template_name"] = $template;
  4753.             }
  4754.         }
  4755.  
  4756.         if (!isset(self::$template_usage[$template_name]))
  4757.         {
  4758.             self::$template_usage[$template_name] = 1;
  4759.         }
  4760.         else
  4761.         {
  4762.             self::$template_usage[$template_name]++;
  4763.         }
  4764.  
  4765.         ($hook = vBulletinHook::fetch_hook('fetch_template_complete')) ? eval($hook) : false;
  4766.  
  4767.         return $template;
  4768.     }
  4769. }
  4770.  
  4771. abstract class vB_Template_Data extends vB_Template
  4772. {
  4773.     /**
  4774.      * Registered templates and their local vars.
  4775.      * The array should be in the form:
  4776.      *  array(template_name => array(registered, registered [,...]))
  4777.      *
  4778.      * @var array
  4779.      */
  4780.     protected static $registered_templates = array();
  4781.  
  4782.     /**
  4783.      * Prefix for the template token.
  4784.      * If this is matched as the prefix of a registered variable then the value is
  4785.      * picked up from $registered_templates.
  4786.      */
  4787.     protected static $token_prefix = '_-_-template-_-_';
  4788.  
  4789.     /**
  4790.      * Register a variable with the template.
  4791.      * If the variable is prefixed with the template token then it is assumed as a
  4792.      * child template and picked up from $registered_templates.
  4793.      *
  4794.      * @param   string  Name of the variable to be registered
  4795.      * @param   mixed   Value to be registered. This may be a scalar or an array.
  4796.      * @param   bool    Whether to overwrite existing vars
  4797.      * @return  bool    Whether the var was registered
  4798.      */
  4799.     public function register($name, $value, $overwrite = true)
  4800.     {
  4801.         if (!$overwrite AND $this->is_registered($name))
  4802.         {
  4803.             return false;
  4804.         }
  4805.  
  4806.         // Run register hook
  4807.         self::assert_register_hook();
  4808.  
  4809.         if (self::$hook_code)
  4810.         {
  4811.             eval(self::$hook_code);
  4812.         }
  4813.  
  4814.         if (defined('VB_API_CMS') AND VB_API_CMS === true)
  4815.         {
  4816.             $value = $this->escapeView($value);
  4817.         }
  4818.  
  4819.         // Convert any tokenised templates into the local vars
  4820.         $this->parse_token($value);
  4821.  
  4822.         $this->registered[$name] = $value;
  4823.  
  4824.         return true;
  4825.     }
  4826.  
  4827.  
  4828.     /**
  4829.      * Identical to register, but registers a value as a reference.
  4830.      *
  4831.      * @param   string  Name of the variable to be registered
  4832.      * @param   mixed   Value to be registered. This may be a scalar or an array.
  4833.      * @param   bool    Whether to overwrite existing vars
  4834.      * @return  bool    Whether the var was registered
  4835.      */
  4836.     public function register_ref($name, &$value, $overwrite = true)
  4837.     {
  4838.         if (!$overwrite AND $this->is_registered($name))
  4839.         {
  4840.             return false;
  4841.         }
  4842.  
  4843.         // Run register hook
  4844.         self::assert_register_hook();
  4845.  
  4846.         if (self::$hook_code)
  4847.         {
  4848.             eval(self::$hook_code);
  4849.         }
  4850.  
  4851.         if (defined('VB_API_CMS') AND VB_API_CMS === true)
  4852.         {
  4853.             $value = $this->escapeView($value);
  4854.         }
  4855.  
  4856.         // Convert any tokenised templates into the local vars
  4857.         $this->parse_token($value);
  4858.  
  4859.         $this->registered[$name] = &$value;
  4860.  
  4861.         return true;
  4862.     }
  4863.  
  4864.  
  4865.     /**
  4866.      * Checks if a registered value is a template token.
  4867.      * If it is, the registered vars of the child template are picked up and
  4868.      * assigned to this template.
  4869.      *
  4870.      * @param   string  Name of the variable to be registered
  4871.      * @param   mixed   Value to be registered. This may be a scalar or an array.
  4872.      * @return  bool    Whether the value was picked up as a token, or the resovled value
  4873.      */
  4874.     public function parse_token(&$value)
  4875.     {
  4876.         if (is_array($value))
  4877.         {
  4878.             array_walk($value, array($this, 'parse_token'));
  4879.         }
  4880.         else
  4881.         {
  4882.             $matched = false;
  4883.             $matches = array();
  4884.             if (is_string($value) AND preg_match_all('#' . preg_quote(self::$token_prefix) . '(.+?):(\d+)#', $value, $matches, PREG_SET_ORDER))
  4885.             {
  4886.                 $old_value = $value;
  4887.                 $value = array();
  4888.  
  4889.                 foreach ($matches AS $match)
  4890.                 {
  4891.                     $template_name = $match[1];
  4892.                     $index = intval($match[2]);
  4893.  
  4894.                     if (isset(self::$registered_templates[$template_name][$index]))
  4895.                     {
  4896.                         $value[] = self::$registered_templates[$template_name][$index];
  4897.                         $matched = true;
  4898.                     }
  4899.                 }
  4900.  
  4901.                 if (sizeof($value) <= 1)
  4902.                 {
  4903.                     $value = current($value);
  4904.                 }
  4905.             }
  4906.         }
  4907.  
  4908.         return $matched;
  4909.     }
  4910.  
  4911.     protected function whitelist_filter()
  4912.     {
  4913.         global $VB_API_WHITELIST;
  4914.  
  4915.         // errormessage should be always added to the whitelist
  4916.         $VB_API_WHITELIST['response']['errormessage'] = '*';
  4917.         if (!$VB_API_WHITELIST['show'] AND !is_array($VB_API_WHITELIST['show']))
  4918.         {
  4919.             $VB_API_WHITELIST['show'] = '*';
  4920.         }
  4921.  
  4922.         $temp = array();
  4923.         $this->whitelist_filter_recur($VB_API_WHITELIST, $temp, $this->registered);
  4924.         $this->registered = $temp;
  4925.  
  4926.     }
  4927.  
  4928.     protected function whitelist_filter_recur($whitelist, &$arr, &$registered)
  4929.     {
  4930.         foreach ($whitelist as $k => $v)
  4931.         {
  4932.             if ($k !== '*')
  4933.             {
  4934.                 if (is_numeric($k) AND isset($registered[$v]))
  4935.                 {
  4936.                     if (is_array($registered[$v]))
  4937.                     {
  4938.                         $this->removeShow($registered[$v]);
  4939.                     }
  4940.                     $arr[$v] = $registered[$v];
  4941.                 }
  4942.                 elseif (array_key_exists($k, (array)$registered))
  4943.                 {
  4944.                     if ($v === '*')
  4945.                     {
  4946.                         if (is_array($registered[$v]))
  4947.                         {
  4948.                             $this->removeShow($registered[$v]);
  4949.                         }
  4950.                         $arr[$k] = $registered[$k];
  4951.                     }
  4952.                     elseif (is_array($v))
  4953.                     {
  4954.                         $arr[$k] = array();
  4955.                         $this->whitelist_filter_recur($whitelist[$k], $arr[$k], $registered[$k]);
  4956.                         if (empty($arr[$k]))
  4957.                         {
  4958.                             unset($arr[$k]);
  4959.                         }
  4960.                     }
  4961.                 }
  4962.             }
  4963.             elseif ($k === '*')
  4964.             {
  4965.                 if (is_array($registered))
  4966.                 {
  4967.                     $registeredkeys = array_keys($registered);
  4968.                     if (is_numeric($registeredkeys[0]))
  4969.                     {
  4970.                         foreach ($registered as $k2 => $v2)
  4971.                         {
  4972.                             if (is_array($whitelist[$k]) AND !in_array('show', array_keys($whitelist[$k])))
  4973.                             {
  4974.                                 if (is_array($registered[$k2]))
  4975.                                 {
  4976.                                     $this->removeShow($registered[$k2]);
  4977.                                 }
  4978.                             }
  4979.                             $arr[$k2] = array();
  4980.                             $this->whitelist_filter_recur($whitelist[$k], $arr[$k2], $registered[$k2]);
  4981.                         }
  4982.                     }
  4983.                     else
  4984.                     {
  4985.                         if (is_array($whitelist[$k]) AND !in_array('show', array_keys($whitelist[$k])))
  4986.                         {
  4987.                             if (is_array($registered))
  4988.                             {
  4989.                                 $this->removeShow($registered);
  4990.                             }
  4991.                         }
  4992.                         $this->whitelist_filter_recur($whitelist[$k], $arr, $registered);
  4993.                     }
  4994.                 }
  4995.                 else
  4996.                 {
  4997.                     $arr = $registered;
  4998.                     unset($registered);
  4999.                 }
  5000.             }
  5001.         }
  5002.     }
  5003.  
  5004.     protected function removeShow(&$arr)
  5005.     {
  5006.         if (is_array($arr))
  5007.         {
  5008.             unset($arr['show']);
  5009.             foreach($arr as &$v)
  5010.             {
  5011.                 $this->removeShow($v);
  5012.             }
  5013.         }
  5014.     }
  5015.  
  5016.     protected function escapeView($value)
  5017.     {
  5018.         if (is_array($value))
  5019.         {
  5020.             foreach ($value AS &$el)
  5021.             {
  5022.                 $el = $this->escapeView($el);
  5023.             }
  5024.         }
  5025.  
  5026.         if ($value instanceof vB_View)
  5027.         {
  5028.             $value = $value->render();
  5029.         }
  5030.         else if ($value instanceof vB_Phrase)
  5031.         {
  5032.             $value = (string)$value;
  5033.         }
  5034.  
  5035.         return $value;
  5036.     }
  5037.  
  5038.  
  5039.     /**
  5040.      * Renders the template.
  5041.      *
  5042.      * @param   boolean Whether to suppress the HTML comment surrounding option (for JS, etc)
  5043.      * @return  string  Rendered version of the template
  5044.      */
  5045.     public function render($suppress_html_comments = false, $final = false)
  5046.     {
  5047.         global $vbulletin, $show;
  5048.  
  5049.         $callback = vB_APICallback::instance();
  5050.  
  5051.         if ($final)
  5052.         {
  5053.             self::remove_common_show($show);
  5054.  
  5055.             // register whitelisted globals
  5056.             $this->register_globals();
  5057.  
  5058.             $callback->setname('result_prewhitelist');
  5059.             $callback->addParamRef(0, $this->registered);
  5060.             $callback->callback();
  5061.  
  5062.             if (!($vbulletin->debug AND $vbulletin->GPC['showall']))
  5063.             {
  5064.                 $this->whitelist_filter();
  5065.             }
  5066.  
  5067.             $callback->setname('result_overwrite');
  5068.             $callback->addParamRef(0, $this->registered);
  5069.             $callback->callback();
  5070.  
  5071.             if ($vbulletin->debug AND $vbulletin->GPC['debug'])
  5072.             {
  5073.                 return '<pre>'.htmlspecialchars(var_export($this->registered, true)).'</pre>' . '<br />' . number_format((memory_get_usage() / 1024)) . 'KB';
  5074.             }
  5075.             else
  5076.             {
  5077.                 // only render data on final render
  5078.                 return $this->render_output($suppress_html_comments);
  5079.             }
  5080.         }
  5081.         else
  5082.         {
  5083.             $callback->setname('result_prerender');
  5084.             $callback->addParam(0, $this->template);
  5085.             $callback->addParamRef(1, $this->registered);
  5086.             $callback->callback();
  5087.         }
  5088.  
  5089.  
  5090.         return $this->render_token();
  5091.     }
  5092.  
  5093.  
  5094.     /**
  5095.      * Buffers locally registered vars and returns a token representation of the template.
  5096.      *
  5097.      * @return string
  5098.      */
  5099.     protected function render_token()
  5100.     {
  5101.         if (!isset(self::$registered_templates[$this->template]))
  5102.         {
  5103.             self::$registered_templates[$this->template] = array();
  5104.         }
  5105.  
  5106.         // Buffer local vars to be picked up by the parent template
  5107.         self::$registered_templates[$this->template][] = $this->registered;
  5108.  
  5109.         $index = sizeof(self::$registered_templates[$this->template])-1;
  5110.  
  5111.         return self::$token_prefix . $this->template . ':' . $index;
  5112.     }
  5113.  
  5114.  
  5115.     /**
  5116.      * Renders the output after preperation.
  5117.      * @see vB_Template::render()
  5118.      *
  5119.      * @param boolean   Whether to suppress the HTML comment surrounding option (for JS, etc)
  5120.      * @return string
  5121.      */
  5122.     protected function render_output($suppress_html_comments = false)
  5123.     {
  5124.         return false;
  5125.     }
  5126.  
  5127.     public static function dump_templates()
  5128.     {
  5129.         return print_r(self::$registered_templates,1);
  5130.     }
  5131. }
  5132.  
  5133.  
  5134. class vB_Template_XML extends vB_Template_Data
  5135. {
  5136.     /**
  5137.      * Renders the output after preperation.
  5138.      * @see vB_Template::render()
  5139.      *
  5140.      * @param boolean   Whether to suppress the HTML comment surrounding option (for JS, etc)
  5141.      * @return string
  5142.      */
  5143.     protected function render_output($suppress_html_comments = false)
  5144.     {
  5145.         return xmlrpc_encode($this->registered);
  5146.     }
  5147. }
  5148.  
  5149. class vB_Template_JSON extends vB_Template_Data
  5150. {
  5151.     /**
  5152.      * Renders the output after preperation.
  5153.      * @see vB_Template::render()
  5154.      *
  5155.      * @param boolean   Whether to suppress the HTML comment surrounding option (for JS, etc)
  5156.      * @return string
  5157.      */
  5158.     protected function render_output($suppress_html_comments = false)
  5159.     {
  5160.         if (!($charset = vB_Template_Runtime::fetchStyleVar('charset')))
  5161.         {
  5162.             global $vbulletin;
  5163.             $charset = $vbulletin->userinfo['lang_charset'];
  5164.         }
  5165.  
  5166.         $lower_charset = strtolower($charset);
  5167.         if ($lower_charset != 'utf-8')
  5168.         {
  5169.             // Browsers tend to interpret character set iso-8859-1 as windows-1252
  5170.             if ($lower_charset == 'iso-8859-1')
  5171.             {
  5172.                 $lower_charset = 'windows-1252';
  5173.             }
  5174.             $this->processregistered($this->registered, $lower_charset);
  5175.         }
  5176.  
  5177.         return json_encode($this->registered);
  5178.     }
  5179.  
  5180.     private function processregistered(&$value, $charset)
  5181.     {
  5182.         global $VB_API_REQUESTS;
  5183.  
  5184.         if (is_array($value))
  5185.         {
  5186.             foreach ($value AS &$el)
  5187.             {
  5188.                 $this->processregistered($el, $charset);
  5189.             }
  5190.         }
  5191.  
  5192.         if (is_string($value))
  5193.         {
  5194.             $value = preg_replace('/&#([0-9]+);/esiU', "convert_int_to_utf8('\\1')", to_utf8($value, $charset, true));
  5195.             $trimmed = trim($value);
  5196.             if ($VB_API_REQUESTS['api_version'] > 1 AND ($trimmed == 'checked="checked"' OR $trimmed == 'selected="selected"'))
  5197.             {
  5198.                 $value = 1;
  5199.             }
  5200.         }
  5201.  
  5202.         if ($VB_API_REQUESTS['api_version'] > 1 AND is_bool($value))
  5203.         {
  5204.             if ($value)
  5205.             {
  5206.                 $value = 1;
  5207.             }
  5208.             else
  5209.             {
  5210.                 $value = 0;
  5211.             }
  5212.         }
  5213.     }
  5214. }
  5215.  
  5216. class vB_Template_Runtime
  5217. {
  5218.     public static $units = array('%', 'px', 'pt', 'em', 'ex', 'pc', 'in', 'cm', 'mm');
  5219.  
  5220.     public static function date($timestamp, $format = 'r')
  5221.     {
  5222.         if (empty($format))
  5223.         {
  5224.             $format = 'r';
  5225.         }
  5226.         return vbdate($format, intval($timestamp), true);
  5227.     }
  5228.  
  5229.     public static function time($timestamp)
  5230.     {
  5231.         global $vbulletin;
  5232.         if (empty($timestamp)) { $timestamp = 0; }
  5233.         return vbdate($vbulletin->options['timeformat'], $timestamp);
  5234.     }
  5235.  
  5236.     public static function escapeJS($javascript)
  5237.     {
  5238.         return addcslashes($javascript, "'\\");
  5239.     }
  5240.  
  5241.     public static function numberFormat($number, $decimals = 0)
  5242.     {
  5243.         return vb_number_format($number, $decimals);
  5244.     }
  5245.  
  5246.     public static function urlEncode($text)
  5247.     {
  5248.         return urlencode($text);
  5249.     }
  5250.  
  5251.     public static function parsePhrase($phraseName)
  5252.     {
  5253.         global $vbphrase;
  5254.         $arg_list = func_get_args();
  5255.         $arg_list[0] = $vbphrase[$phraseName];
  5256.         return construct_phrase_from_array($arg_list);
  5257.     }
  5258.  
  5259.     public static function addStyleVar($name, $value, $datatype = 'string')
  5260.     {
  5261.         global $vbulletin;
  5262.  
  5263.         switch ($datatype)
  5264.         {
  5265.             case 'string':
  5266.                 $vbulletin->stylevars["$name"] = array(
  5267.                     'datatype' => $datatype,
  5268.                     'string'   => $value,
  5269.                 );
  5270.             break;
  5271.             case 'imgdir':
  5272.                 $vbulletin->stylevars["$name"] = array(
  5273.                     'datatype' => 'imagedir',
  5274.                     'imagedir' => $value,
  5275.                 );
  5276.             break;
  5277.         }
  5278.     }
  5279.  
  5280.     public static function fetchStyleVar($stylevar)
  5281.     {
  5282.         global $vbulletin;
  5283.  
  5284.         $parts = explode('.', $stylevar);
  5285.         $base_stylevar = $vbulletin->stylevars[$parts[0]];
  5286.  
  5287.         // this for accessing subparts of a complex data type
  5288.         if (isset($parts[1]))
  5289.         {
  5290.             $types = array(
  5291.                 'background' => array(
  5292.                     'backgroundColor' => 'color',
  5293.                     'backgroundImage' => 'image',
  5294.                     'backgroundRepeat' => 'repeat',
  5295.                     'backgroundPositionX' => 'x',
  5296.                     'backgroundPositionY' => 'y',
  5297.                     'backgroundPositionUnits' => 'units'
  5298.                 ),
  5299.  
  5300.                 'font' => array(
  5301.                     'fontWeight' => 'weight',
  5302.                     'units' => 'units',
  5303.                     'fontSize' => 'size',
  5304.                     'fontFamily' => 'family',
  5305.                     'fontStyle' => 'style',
  5306.                     'fontVariant' => 'variant',
  5307.                 ),
  5308.  
  5309.                 'padding' => array(
  5310.                     'units' => 'units',
  5311.                     'paddingTop' => 'top',
  5312.                     'paddingRight' => 'right',
  5313.                     'paddingBottom' => 'bottom',
  5314.                     'paddingLeft' => 'left',
  5315.                 ),
  5316.  
  5317.                 'margin' => array(
  5318.                     'units' => 'units',
  5319.                     'marginTop' => 'top',
  5320.                     'marginRight' => 'right',
  5321.                     'marginBottom' => 'bottom',
  5322.                     'marginLeft' => 'left',
  5323.                 ),
  5324.  
  5325.                 'border' => array(
  5326.                     'borderStyle' => 'style',
  5327.                     'units' => 'units',
  5328.                     'borderWidth' => 'width',
  5329.                     'borderColor' => 'color',
  5330.                 ),
  5331.             );
  5332.  
  5333.             //handle is same for margin and padding -- allows the top value to be
  5334.             //used for all padding values
  5335.             if (in_array($base_stylevar['datatype'], array('padding', 'margin')) AND $parts[1] <> 'units')
  5336.             {
  5337.                 if (isset($base_stylevar['same']) AND $base_stylevar['same'])
  5338.                 {
  5339.                     $parts[1] = $base_stylevar['datatype'] . 'Top';
  5340.                 }
  5341.             }
  5342.  
  5343.             if (isset($types[$base_stylevar['datatype']]))
  5344.             {
  5345.                 $mapping = $types[$base_stylevar['datatype']][$parts[1]];
  5346.                 $output = $base_stylevar[$mapping];
  5347.             }
  5348.             else
  5349.             {
  5350.                 $output = $base_stylevar;
  5351.                 for ($i = 1; $i < sizeof($parts); $i++) {
  5352.                     $output = $output[$parts[$i]];
  5353.                 }
  5354.             }
  5355.         }
  5356.         else
  5357.         {
  5358.             $output = '';
  5359.  
  5360.             switch($base_stylevar['datatype'])
  5361.             {
  5362.                 case 'color':
  5363.                     $output = $base_stylevar['color'];
  5364.                 break;
  5365.  
  5366.                 case 'background':
  5367.                     switch ($base_stylevar['x'])
  5368.                     {
  5369.                         case 'stylevar-left':
  5370.                             $base_stylevar['x'] = $vbulletin->stylevars['left']['string'];break;
  5371.                         case 'stylevar-right':
  5372.                             $base_stylevar['x'] = $vbulletin->stylevars['right']['string'];break;
  5373.                         default:
  5374.                             $base_stylevar['x'] = $base_stylevar['x'].$base_stylevar['units'];break;
  5375.                     }
  5376.                     $output = $base_stylevar['color'] . ' ' . (!empty($base_stylevar['image']) ? "$base_stylevar[image]" : 'none') . ' ' .
  5377.                         $base_stylevar['repeat'] . ' ' .$base_stylevar['x'] . ' ' .
  5378.                         $base_stylevar['y'] .
  5379.                         $base_stylevar['units'];
  5380.                 break;
  5381.  
  5382.                 case 'textdecoration':
  5383.                     if ($base_stylevar['none'])
  5384.                     {
  5385.                         $output = 'none';
  5386.                     }
  5387.                     else
  5388.                     {
  5389.                         unset($base_stylevar['datatype'], $base_stylevar['none']);
  5390.                         $output = implode(' ', array_keys(array_filter($base_stylevar)));
  5391.                     }
  5392.                 break;
  5393.  
  5394.                 case 'font':
  5395.                     $output = $base_stylevar['style'] . ' ' . $base_stylevar['variant'] . ' ' .
  5396.                     $base_stylevar['weight'] . ' ' . $base_stylevar['size'] . $base_stylevar['units'] . ' ' .
  5397.                     $base_stylevar['family'];
  5398.                 break;
  5399.  
  5400.                 case 'imagedir':
  5401.                     $output = $base_stylevar['imagedir'];
  5402.                 break;
  5403.  
  5404.                 case 'string':
  5405.                     $output = $base_stylevar['string'];
  5406.                 break;
  5407.  
  5408.                 case 'numeric':
  5409.                     $output = $base_stylevar['numeric'];
  5410.                 break;
  5411.  
  5412.                 case 'size':
  5413.                     $output =  $base_stylevar['size'] . $base_stylevar['units'];
  5414.                 break;
  5415.  
  5416.                 case 'url':
  5417.                     $output = $base_stylevar['url'];
  5418.                 break;
  5419.  
  5420.                 case 'path':
  5421.                     $output = $base_stylevar['path'];
  5422.                 break;
  5423.  
  5424.                 case 'fontlist':
  5425.                     $output = implode(',', preg_split('/[\r\n]+/', trim($base_stylevar['fontlist']), -1, PREG_SPLIT_NO_EMPTY));
  5426.                 break;
  5427.  
  5428.                 case 'border':
  5429.                     $output = $base_stylevar['width'] . $base_stylevar['units'] . ' ' .
  5430.                         $base_stylevar['style'] . ' ' . $base_stylevar['color'];
  5431.                 break;
  5432.  
  5433.                 case 'dimension':
  5434.                     $output = 'width: ' . intval($base_stylevar['width'])  . $base_stylevar['units'] .
  5435.                         '; height: ' . intval($base_stylevar['height']) . $base_stylevar['units'] . ';';
  5436.                 break;
  5437.  
  5438.                 case 'padding':
  5439.                 case 'margin':
  5440.                     foreach (array('top', 'right', 'bottom', 'left') AS $side)
  5441.                     {
  5442.                         if ($base_stylevar[$side] != 'auto')
  5443.                         {
  5444.                             $base_stylevar[$side] = $base_stylevar[$side] . $base_stylevar['units'];
  5445.                         }
  5446.                     }
  5447.                     if (isset($base_stylevar['same']) AND $base_stylevar['same'])
  5448.                     {
  5449.                         $output = $base_stylevar['top'];
  5450.                     }
  5451.                     else
  5452.                     {
  5453.                         if (vB_Template_Runtime::fetchStyleVar('textdirection') == 'ltr')
  5454.                         {
  5455.                             $output = $base_stylevar['top'] . ' ' . $base_stylevar['right'] . ' ' . $base_stylevar['bottom'] . ' ' . $base_stylevar['left'];
  5456.                         }
  5457.                         else
  5458.                         {
  5459.                             $output = $base_stylevar['top'] . ' ' . $base_stylevar['left'] . ' ' . $base_stylevar['bottom'] . ' ' . $base_stylevar['right'];
  5460.                         }
  5461.                     }
  5462.                 break;
  5463.             }
  5464.         }
  5465.  
  5466.         return $output;
  5467.     }
  5468.  
  5469.     public static function runMaths($str)
  5470.     {
  5471.         //this would usually be dangerous, but none of the units make sense
  5472.         //in a math string anyway.  Note that there is ambiguty between the '%'
  5473.         //unit and the modulo operator.  We don't allow the latter anyway
  5474.         //(though we do allow bitwise operations !?)
  5475.         $units_found = null;
  5476.         foreach (self::$units AS $unit)
  5477.         {
  5478.             if (strpos($str, $unit))
  5479.             {
  5480.                 $units_found[] = $unit;
  5481.             }
  5482.         }
  5483.  
  5484.         //mixed units.
  5485.         if (count($units_found) > 1)
  5486.         {
  5487.             return "/* ~~cannot perform math on mixed units ~~ found (" .
  5488.                 implode(",", $units_found) . ") in $str */";
  5489.         }
  5490.  
  5491.         $str = preg_replace('#([^+\-*=/\(\)\d\^<>&|\.]*)#', '', $str);
  5492.  
  5493.         if (empty($str))
  5494.         {
  5495.             $str = '0';
  5496.         }
  5497.         else
  5498.         {
  5499.             //hack: if the math string is invalid we can get a php parse error here.
  5500.             //a bad expression or even a bad variable value (blank instead of a number) can
  5501.             //cause this to occur.  This fails quietly, but also sets the status code to 500
  5502.             //(but, due to a bug in php only if display_errors is *off* -- if display errors
  5503.             //is on, then it will work just fine only $str below will not be set.
  5504.             //
  5505.             //This can result is say an almost correct css file being ignored by the browser
  5506.             //for reasons that aren't clear (and goes away if you turn error reporting on).
  5507.             //We can check to see if eval hit a parse error and, if so, we'll attempt to
  5508.             //clear the 500 status (this does more harm then good) and send an error
  5509.             //to the file.  Since math is mostly used in css, we'll provide error text
  5510.             //that works best with that.
  5511.             $status = @eval("\$str = $str;");
  5512.             if ($status === false)
  5513.             {
  5514.                 if (!headers_sent())
  5515.                 {
  5516.                     header($_SERVER['SERVER_PROTOCOL'] . ' 200 OK');
  5517.                 }
  5518.                 return "/* Invalid math expression */";
  5519.             }
  5520.  
  5521.             if (count($units_found) == 1)
  5522.             {
  5523.                 $str = $str.$units_found[0];
  5524.             }
  5525.         }
  5526.         return $str;
  5527.     }
  5528.  
  5529.     public static function linkBuild($type, $info = array(), $extra = array(), $primaryid = null, $primarytitle = null)
  5530.     {
  5531.         //allow strings of form of query strings for info or extra.  This allows us to hard code some values
  5532.         //in the templates instead of having to pass everything in from the php code.  Limitations
  5533.         //in the markup do not allow us to build arrays in the template so we need to use strings.
  5534.         //We still can't build strings from variables to pass here so we can't mix hardcoded and
  5535.         //passed values, but we do what we can.
  5536.  
  5537.         if (is_string($info))
  5538.         {
  5539.             parse_str($info, $new_vals);
  5540.             $info = $new_vals;
  5541.         }
  5542.  
  5543.         if (is_string($extra))
  5544.         {
  5545.             parse_str($extra, $new_vals);
  5546.             $extra = $new_vals;
  5547.         }
  5548.  
  5549.         return fetch_seo_url($type, $info, $extra, $primaryid, $primarytitle);
  5550.     }
  5551. }
  5552.  
  5553. // #############################################################################
  5554. // misc functions
  5555.  
  5556. // #############################################################################
  5557. /**
  5558. * Feeds database connection errors into the halt() method of the vB_Database class.
  5559. *
  5560. * @param    integer Error number
  5561. * @param    string  PHP error text string
  5562. * @param    strig   File that contained the error
  5563. * @param    integer Line in the file that contained the error
  5564. */
  5565. function catch_db_error($errno, $errstr, $errfile, $errline)
  5566. {
  5567.     global $db;
  5568.     static $failures;
  5569.  
  5570.     if (strstr($errstr, 'Lost connection') AND $failures < 5)
  5571.     {
  5572.         $failures++;
  5573.         return;
  5574.     }
  5575.  
  5576.     if (is_object($db))
  5577.     {
  5578.         $db->halt("$errstr\r\n$errfile on line $errline");
  5579.     }
  5580.     else
  5581.     {
  5582.         vb_error_handler($errno, $errstr, $errfile, $errline);
  5583.     }
  5584. }
  5585.  
  5586. // #############################################################################
  5587. /**
  5588. * Removes the full path from being disclosed on any errors
  5589. *
  5590. * @param    integer Error number
  5591. * @param    string  PHP error text string
  5592. * @param    strig   File that contained the error
  5593. * @param    integer Line in the file that contained the error
  5594. */
  5595. function vb_error_handler($errno, $errstr, $errfile, $errline)
  5596. {
  5597.     global $vbulletin;
  5598.  
  5599.     switch ($errno)
  5600.     {
  5601.         case E_WARNING:
  5602.         case E_USER_WARNING:
  5603.             /* Don't log warnings due to to the false bug reports about valid warnings that we suppress, but still appear in the log
  5604.             require_once(DIR . '/includes/functions_log_error.php');
  5605.             $message = "Warning: $errstr in $errfile on line $errline";
  5606.             log_vbulletin_error($message, 'php');
  5607.             */
  5608.  
  5609.             if (!error_reporting() OR !ini_get('display_errors'))
  5610.             {
  5611.                 return;
  5612.             }
  5613.             $errfile = str_replace(DIR, '[path]', $errfile);
  5614.             $errstr = str_replace(DIR, '[path]', $errstr);
  5615.             echo "<br /><strong>Warning</strong>: $errstr in <strong>$errfile</strong> on line <strong>$errline</strong><br />";
  5616.         break;
  5617.  
  5618.         case E_USER_ERROR:
  5619.             require_once(DIR . '/includes/functions_log_error.php');
  5620.             $message = "Fatal error: $errstr in $errfile on line $errline";
  5621.             log_vbulletin_error($message, 'php');
  5622.  
  5623.             if (!headers_sent())
  5624.             {
  5625.                 if (SAPI_NAME == 'cgi' OR SAPI_NAME == 'cgi-fcgi')
  5626.                 {
  5627.                     header('Status: 500 Internal Server Error');
  5628.                 }
  5629.                 else
  5630.                 {
  5631.                     header($_SERVER['SERVER_PROTOCOL'] . ' 500 Internal Server Error');
  5632.                 }
  5633.             }
  5634.  
  5635.             if (error_reporting() OR ini_get('display_errors'))
  5636.             {
  5637.                 $errfile = str_replace(DIR, '[path]', $errfile);
  5638.                 $errstr = str_replace(DIR, '[path]', $errstr);
  5639.                 echo "<br /><strong>Fatal error:</strong> $errstr in <strong>$errfile</strong> on line <strong>$errline</strong><br />";
  5640.                 if (function_exists('debug_print_backtrace') AND ($vbulletin->userinfo['usergroupid'] == 6 OR ($vbulletin->userinfo['permissions']['adminpermissions'] & $vbulletin->bf_ugp_adminpermissions)))
  5641.                 {
  5642.                     // This is needed so IE doesn't show the pretty error messages
  5643.                     echo str_repeat(' ', 512);
  5644.                     debug_print_backtrace();
  5645.                 }
  5646.             }
  5647.             exit;
  5648.         break;
  5649.     }
  5650. }
  5651.  
  5652. // #############################################################################
  5653. /**
  5654. * Unicode-safe version of htmlspecialchars()
  5655. *
  5656. * @param    string  Text to be made html-safe
  5657. *
  5658. * @return   string
  5659. */
  5660. function htmlspecialchars_uni($text, $entities = true)
  5661. {
  5662.     if ($entities)
  5663.     {
  5664.         $text = preg_replace_callback(
  5665.             '/&((#([0-9]+)|[a-z]+);)?/si',
  5666.             'htmlspecialchars_uni_callback',
  5667.             $text
  5668.         );
  5669.     }
  5670.     else
  5671.     {
  5672.         $text = preg_replace(
  5673.             // translates all non-unicode entities
  5674.             '/&(?!(#[0-9]+|[a-z]+);)/si',
  5675.             '&amp;',
  5676.             $text
  5677.         );
  5678.     }
  5679.  
  5680.     return str_replace(
  5681.         // replace special html characters
  5682.         array('<', '>', '"'),
  5683.         array('&lt;', '&gt;', '&quot;'),
  5684.             $text
  5685.     );
  5686. }
  5687.  
  5688. function htmlspecialchars_uni_callback($matches)
  5689. {
  5690.     if (count($matches) == 1)
  5691.     {
  5692.         return '&amp;';
  5693.     }
  5694.  
  5695.     if (strpos($matches[2], '#') === false)
  5696.     {
  5697.         // &gt; like
  5698.         if ($matches[2] == 'shy')
  5699.         {
  5700.             return '&shy;';
  5701.         }
  5702.         else
  5703.         {
  5704.             return "&amp;$matches[2];";
  5705.         }
  5706.     }
  5707.     else
  5708.     {
  5709.         // Only convert chars that are in ISO-8859-1
  5710.         if (($matches[3] >= 32 AND $matches[3] <= 126)
  5711.             OR
  5712.             ($matches[3] >= 160 AND $matches[3] <= 255))
  5713.         {
  5714.             return "&amp;#$matches[3];";
  5715.         }
  5716.         else
  5717.         {
  5718.             return "&#$matches[3];";
  5719.         }
  5720.     }
  5721. }
  5722.  
  5723.  
  5724. function css_escape_string($string)
  5725. {
  5726.     static $map = null;
  5727.     //url(<something>) is valid.
  5728.  
  5729.     $checkstr = strtolower(trim($string));
  5730.     $add_url = false;
  5731.     if ((substr($checkstr, 0, 4) == 'url(') AND (substr($checkstr,-1,1) == ')'))
  5732.     {
  5733.         //we need to leave the "url()" part alone.
  5734.         $add_url = true;
  5735.         $string = trim($string);
  5736.         $string = substr($string,4, strlen($string)- 5);
  5737.         if ((($string[0] == '"') AND (substr($checkstr,-1,1) == '"'))
  5738.             OR
  5739.             (($string[0] == "'") AND (substr($checkstr,-1,1) == "'")))
  5740.         {
  5741.             $string = substr($string,1, strlen($string)- 2);
  5742.         }
  5743.     }
  5744.  
  5745.     if(is_null($map))
  5746.     {
  5747.         $chars = array(
  5748.             '\\', '!', '@', '#', '$', '%', '^',  '*', '"', "'",
  5749.             '<', '>', ',', '`', '~','/','&', '.',':', ')','(', ';'
  5750.         );
  5751.  
  5752.         foreach ($chars as $char)
  5753.         {
  5754.             $map[$char] = '\\' . dechex(ord($char)) . ' ';
  5755.         }
  5756.         //var_dump($map);
  5757.     }
  5758.  
  5759.     $string = str_replace(array_keys($map), $map, $string);
  5760.  
  5761.     //add back the url() if we need it.
  5762.     if ($add_url)
  5763.     {
  5764.         $string = 'url(\'' . $string . '\')';
  5765.     }
  5766.     return $string;
  5767. }
  5768. /*======================================================================*\
  5769. || ####################################################################
  5770. || #
  5771. || # CVS: $RCSfile$ - $Revision: 62833 $
  5772. || ####################################################################
  5773. \*======================================================================*/
Advertisement
Add Comment
Please, Sign In to add comment