Advertisement
Guest User

Untitled

a guest
Feb 27th, 2016
395
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 87.91 KB | None | 0 0
  1. <?php
  2. /*======================================================================*\
  3. || #################################################################### ||
  4. || # vBulletin 3.8.7 Patch Level 3 - Licence Number VBF2G48E09
  5. || # ---------------------------------------------------------------- # ||
  6. || # Copyright �2000-2013 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', '3.8.7'); // this should match install.php
  14. define('SIMPLE_VERSION', '387'); // see vB_Datastore::check_options()
  15. define('YUI_VERSION', '2.9.0'); // define the YUI version we bundle, used for external YUI
  16.  
  17. /**#@+
  18. * The maximum sizes for the "small" profile avatars
  19. */
  20. define('FIXED_SIZE_AVATAR_WIDTH', 60);
  21. define('FIXED_SIZE_AVATAR_HEIGHT', 80);
  22. /**#@-*/
  23.  
  24. /**#@+
  25. * These make up the bit field to disable specific types of BB codes.
  26. */
  27. define('ALLOW_BBCODE_BASIC', 1);
  28. define('ALLOW_BBCODE_COLOR', 2);
  29. define('ALLOW_BBCODE_SIZE', 4);
  30. define('ALLOW_BBCODE_FONT', 8);
  31. define('ALLOW_BBCODE_ALIGN', 16);
  32. define('ALLOW_BBCODE_LIST', 32);
  33. define('ALLOW_BBCODE_URL', 64);
  34. define('ALLOW_BBCODE_CODE', 128);
  35. define('ALLOW_BBCODE_PHP', 256);
  36. define('ALLOW_BBCODE_HTML', 512);
  37. define('ALLOW_BBCODE_IMG', 1024);
  38. define('ALLOW_BBCODE_QUOTE', 2048);
  39. define('ALLOW_BBCODE_CUSTOM', 4096);
  40. /**#@-*/
  41.  
  42. /**#@+
  43. * These make up the bit field to control what "special" BB codes are found in the text.
  44. */
  45. define('BBCODE_HAS_IMG', 1);
  46. define('BBCODE_HAS_ATTACH', 2);
  47. define('BBCODE_HAS_SIGPIC', 4);
  48. /**#@-*/
  49.  
  50. /**#@+
  51. * Bitfield values for the inline moderation javascript selector which should be self-explanitory
  52. */
  53. define('POST_FLAG_INVISIBLE', 1);
  54. define('POST_FLAG_DELETED', 2);
  55. define('POST_FLAG_ATTACH', 4);
  56. define('POST_FLAG_GUEST', 8);
  57.  
  58. // #############################################################################
  59. // MySQL Database Class
  60.  
  61. /**#@+
  62. * The type of result set to return from the database for a specific row.
  63. */
  64. define('DBARRAY_BOTH', 0);
  65. define('DBARRAY_ASSOC', 1);
  66. define('DBARRAY_NUM', 2);
  67. /**#@-*/
  68.  
  69. /**
  70. * Class to interface with a database
  71. *
  72. * This class also handles data replication between a master and slave(s) servers
  73. *
  74. * @package vBulletin
  75. * @version $Revision: 43870 $
  76. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  77. */
  78. class vB_Database
  79. {
  80. /**
  81. * Array of function names, mapping a simple name to the RDBMS specific function name
  82. *
  83. * @var array
  84. */
  85. var $functions = array(
  86. 'connect' => 'mysql_connect',
  87. 'pconnect' => 'mysql_pconnect',
  88. 'select_db' => 'mysql_select_db',
  89. 'query' => 'mysql_query',
  90. 'query_unbuffered' => 'mysql_unbuffered_query',
  91. 'fetch_row' => 'mysql_fetch_row',
  92. 'fetch_array' => 'mysql_fetch_array',
  93. 'fetch_field' => 'mysql_fetch_field',
  94. 'free_result' => 'mysql_free_result',
  95. 'data_seek' => 'mysql_data_seek',
  96. 'error' => 'mysql_error',
  97. 'errno' => 'mysql_errno',
  98. 'affected_rows' => 'mysql_affected_rows',
  99. 'num_rows' => 'mysql_num_rows',
  100. 'num_fields' => 'mysql_num_fields',
  101. 'field_name' => 'mysql_field_name',
  102. 'insert_id' => 'mysql_insert_id',
  103. 'escape_string' => 'mysql_escape_string',
  104. 'real_escape_string' => 'mysql_real_escape_string',
  105. 'close' => 'mysql_close',
  106. 'client_encoding' => 'mysql_client_encoding',
  107. );
  108.  
  109. /**
  110. * The vBulletin registry object
  111. *
  112. * @var vB_Registry
  113. */
  114. var $registry = null;
  115.  
  116. /**
  117. * Array of constants for use in fetch_array
  118. *
  119. * @var array
  120. */
  121. var $fetchtypes = array(
  122. DBARRAY_NUM => MYSQL_NUM,
  123. DBARRAY_ASSOC => MYSQL_ASSOC,
  124. DBARRAY_BOTH => MYSQL_BOTH
  125. );
  126.  
  127. /**
  128. * Full name of the system
  129. *
  130. * @var string
  131. */
  132. var $appname = 'vBulletin';
  133.  
  134. /**
  135. * Short name of the system
  136. *
  137. * @var string
  138. */
  139. var $appshortname = 'vBulletin';
  140.  
  141. /**
  142. * Database name
  143. *
  144. * @var string
  145. */
  146. var $database = null;
  147.  
  148. /**
  149. * Link variable. The connection to the master/write server.
  150. *
  151. * @var string
  152. */
  153. var $connection_master = null;
  154.  
  155. /**
  156. * Link variable. The connection to the slave/read server(s).
  157. *
  158. * @var string
  159. */
  160. var $connection_slave = null;
  161.  
  162. /**
  163. * Link variable. The connection last used.
  164. *
  165. * @var string
  166. */
  167. var $connection_recent = null;
  168.  
  169. /**
  170. * Whether or not we will be using different connections for read and write queries
  171. *
  172. * @var boolean
  173. */
  174. var $multiserver = false;
  175.  
  176. /**
  177. * Array of queries to be executed when the script shuts down
  178. *
  179. * @var array
  180. */
  181. var $shutdownqueries = array();
  182.  
  183. /**
  184. * The contents of the most recent SQL query string.
  185. *
  186. * @var string
  187. */
  188. var $sql = '';
  189.  
  190. /**
  191. * Whether or not to show and halt on database errors
  192. *
  193. * @var boolean
  194. */
  195. var $reporterror = true;
  196.  
  197. /**
  198. * The text of the most recent database error message
  199. *
  200. * @var string
  201. */
  202. var $error = '';
  203.  
  204. /**
  205. * The error number of the most recent database error message
  206. *
  207. * @var integer
  208. */
  209. var $errno = '';
  210.  
  211. /**
  212. * SQL Query String
  213. *
  214. * @var integer The maximum size of query string permitted by the master server
  215. */
  216. var $maxpacket = 0;
  217.  
  218. /**
  219. * Track lock status of tables. True if a table lock has been issued
  220. *
  221. * @var bool
  222. */
  223. var $locked = false;
  224.  
  225. /**
  226. * Number of queries executed
  227. *
  228. * @var integer The number of SQL queries run by the system
  229. */
  230. var $querycount = 0;
  231.  
  232. /**
  233. * Constructor. If x_real_escape_string() is available, switches to use that
  234. * function over x_escape_string().
  235. *
  236. * @param vB_Registry Registry object
  237. */
  238. function vB_Database(&$registry)
  239. {
  240. if (is_object($registry))
  241. {
  242. $this->registry =& $registry;
  243. }
  244. else
  245. {
  246. trigger_error("vB_Database::Registry object is not an object", E_USER_ERROR);
  247. }
  248. }
  249.  
  250. /**
  251. * Connects to the specified database server(s)
  252. *
  253. * @param string Name of the database that we will be using for select_db()
  254. * @param string Name of the master (write) server - should be either 'localhost' or an IP address
  255. * @param integer Port for the master server
  256. * @param string Username to connect to the master server
  257. * @param string Password associated with the username for the master server
  258. * @param boolean Whether or not to use persistent connections to the master server
  259. * @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
  260. * @param integer (Optional) Port of the slave server
  261. * @param string (Optional) Username to connect to the slave server
  262. * @param string (Optional) Password associated with the username for the slave server
  263. * @param boolean (Optional) Whether or not to use persistent connections to the slave server
  264. * @param string (Optional) Parse given MySQL config file to set options
  265. * @param string (Optional) Connection Charset MySQLi / PHP 5.1.0+ or 5.0.5+ / MySQL 4.1.13+ or MySQL 5.1.10+ Only
  266. *
  267. * @return none
  268. */
  269. 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 = '')
  270. {
  271. $this->database = $database;
  272.  
  273. $w_port = $w_port ? $w_port : 3306;
  274. $r_port = $r_port ? $r_port : 3306;
  275.  
  276. $this->connection_master = $this->db_connect($w_servername, $w_port, $w_username, $w_password, $w_usepconnect, $configfile, $charset);
  277. $this->multiserver = false;
  278. $this->connection_slave =& $this->connection_master;
  279.  
  280. if ($this->connection_master)
  281. {
  282. $this->select_db($this->database);
  283. }
  284. }
  285.  
  286. /**
  287. * Initialize database connection(s)
  288. *
  289. * Connects to the specified master database server, and also to the slave server if it is specified
  290. *
  291. * @param string Name of the database server - should be either 'localhost' or an IP address
  292. * @param integer Port of the database server (usually 3306)
  293. * @param string Username to connect to the database server
  294. * @param string Password associated with the username for the database server
  295. * @param boolean Whether or not to use persistent connections to the database server
  296. * @param string Not applicable; config file for MySQLi only
  297. * @param string Force connection character set (to prevent collation errors)
  298. *
  299. * @return boolean
  300. */
  301. function db_connect($servername, $port, $username, $password, $usepconnect, $configfile = '', $charset = '')
  302. {
  303. if (function_exists('catch_db_error'))
  304. {
  305. set_error_handler('catch_db_error');
  306. }
  307.  
  308. // catch_db_error will handle exiting, no infinite loop here
  309. do
  310. {
  311. $link = $this->functions[$usepconnect ? 'pconnect' : 'connect']("$servername:$port", $username, $password);
  312. }
  313. while ($link == false AND $this->reporterror);
  314.  
  315. restore_error_handler();
  316.  
  317. if (!empty($charset))
  318. {
  319. if (function_exists('mysql_set_charset'))
  320. {
  321. mysql_set_charset($charset);
  322. }
  323. else
  324. {
  325. $this->sql = "SET NAMES $charset";
  326. $this->execute_query(true, $link);
  327. }
  328. }
  329.  
  330. return $link;
  331. }
  332.  
  333. /**
  334. * Selects a database to use
  335. *
  336. * @param string The name of the database located on the database server(s)
  337. *
  338. * @return boolean
  339. */
  340. function select_db($database = '')
  341. {
  342. if ($database != '')
  343. {
  344. $this->database = $database;
  345. }
  346.  
  347. if ($check_write = @$this->select_db_wrapper($this->database, $this->connection_master))
  348. {
  349. $this->connection_recent =& $this->connection_master;
  350. return true;
  351. }
  352. else
  353. {
  354. $this->connection_recent =& $this->connection_master;
  355. $this->halt('Cannot use database ' . $this->database);
  356. return false;
  357. }
  358. }
  359.  
  360. /**
  361. * Simple wrapper for select_db(), to allow argument order changes
  362. *
  363. * @param string Database name
  364. * @param integer Link identifier
  365. *
  366. * @return boolean
  367. */
  368. function select_db_wrapper($database = '', $link = null)
  369. {
  370. return $this->functions['select_db']($database, $link);
  371. }
  372.  
  373. /**
  374. * Forces the sql_mode varaible to a specific mode. Certain modes may be
  375. * incompatible with vBulletin. Applies to MySQL 4.1+.
  376. *
  377. * @param string The mode to set the sql_mode variable to
  378. */
  379. function force_sql_mode($mode)
  380. {
  381. $reset_errors = $this->reporterror;
  382. if ($reset_errors)
  383. {
  384. $this->hide_errors();
  385. }
  386.  
  387. $this->query_write("SET @@sql_mode = '" . $this->escape_string($mode) . "'");
  388.  
  389. if ($reset_errors)
  390. {
  391. $this->show_errors();
  392. }
  393. }
  394.  
  395. /**
  396. * Executes an SQL query through the specified connection
  397. *
  398. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  399. * @param string The connection ID to the database server
  400. *
  401. * @return string
  402. */
  403. function &execute_query($buffered = true, &$link)
  404. {
  405. $this->connection_recent =& $link;
  406. $this->querycount++;
  407.  
  408. if ($queryresult = $this->functions[$buffered ? 'query' : 'query_unbuffered']($this->sql, $link))
  409. {
  410. // unset $sql to lower memory .. this isn't an error, so it's not needed
  411. $this->sql = '';
  412.  
  413. return $queryresult;
  414. }
  415. else
  416. {
  417. $this->halt();
  418.  
  419. // unset $sql to lower memory .. error will have already been thrown
  420. $this->sql = '';
  421. }
  422. }
  423.  
  424. /**
  425. * Executes a data-writing SQL query through the 'master' database connection
  426. *
  427. * @param string The text of the SQL query to be executed
  428. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  429. *
  430. * @return string
  431. */
  432. function query_write($sql, $buffered = true)
  433. {
  434. $this->sql =& $sql;
  435. return $this->execute_query($buffered, $this->connection_master);
  436. }
  437.  
  438. /**
  439. * Executes a data-reading SQL query through the 'master' database connection
  440. * we don't know if the 'read' database is up to date so be on the safe side
  441. *
  442. * @param string The text of the SQL query to be executed
  443. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  444. *
  445. * @return string
  446. */
  447. function query_read($sql, $buffered = true)
  448. {
  449. $this->sql =& $sql;
  450. return $this->execute_query($buffered, $this->connection_master);
  451. }
  452.  
  453. /**
  454. * Executes a data-reading SQL query through the 'slave' database connection
  455. *
  456. * @param string The text of the SQL query to be executed
  457. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is buffered.
  458. *
  459. * @return string
  460. */
  461. function query_read_slave($sql, $buffered = true)
  462. {
  463. $this->sql =& $sql;
  464. return $this->execute_query($buffered, $this->connection_master);
  465. }
  466.  
  467. /**
  468. * Executes an SQL query, using either the write connection
  469. *
  470. * @deprecated Deprecated as of 3.6. Use query_(read/write)
  471. *
  472. * @param string The text of the SQL query to be executed
  473. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  474. *
  475. * @return string
  476. */
  477. function query($sql, $buffered = true)
  478. {
  479. $this->sql =& $sql;
  480. return $this->execute_query($buffered, $this->connection_master);
  481. }
  482.  
  483. /**
  484. * Executes a data-reading SQL query, then returns an array of the data from the first row from the result set
  485. *
  486. * @param string The text of the SQL query to be executed
  487. * @param string One of (NUM, ASSOC, BOTH)
  488. *
  489. * @return array
  490. */
  491. function &query_first($sql, $type = DBARRAY_ASSOC)
  492. {
  493. $this->sql =& $sql;
  494. $queryresult = $this->execute_query(true, $this->connection_master);
  495. $returnarray = $this->fetch_array($queryresult, $type);
  496. $this->free_result($queryresult);
  497. return $returnarray;
  498. }
  499.  
  500. /**
  501. * Executes a FOUND_ROWS query to get the results of SQL_CALC_FOUND_ROWS
  502. *
  503. * @return integer
  504. */
  505. function found_rows()
  506. {
  507. $this->sql = "SELECT FOUND_ROWS()";
  508. $queryresult = $this->execute_query(true, $this->connection_recent);
  509. $returnarray = $this->fetch_array($queryresult, DBARRAY_NUM);
  510. $this->free_result($queryresult);
  511.  
  512. return intval($returnarray[0]);
  513. }
  514.  
  515. /**
  516. * 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
  517. *
  518. * @param string The text of the SQL query to be executed
  519. * @param string One of (NUM, ASSOC, BOTH)
  520. *
  521. * @return array
  522. */
  523. function &query_first_slave($sql, $type = DBARRAY_ASSOC)
  524. {
  525. $returnarray = $this->query_first($sql, $type);
  526. return $returnarray;
  527. }
  528.  
  529. /**
  530. * Executes an INSERT INTO query, using extended inserts if possible
  531. *
  532. * @param string Name of the table into which data should be inserted
  533. * @param string Comma-separated list of the fields to affect
  534. * @param array Array of SQL values
  535. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  536. *
  537. * @return mixed
  538. */
  539. function &query_insert($table, $fields, &$values, $buffered = true)
  540. {
  541. return $this->insert_multiple("INSERT INTO $table $fields VALUES", $values, $buffered);
  542. }
  543.  
  544. /**
  545. * Executes a REPLACE INTO query, using extended inserts if possible
  546. *
  547. * @param string Name of the table into which data should be inserted
  548. * @param string Comma-separated list of the fields to affect
  549. * @param array Array of SQL values
  550. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  551. *
  552. * @return mixed
  553. */
  554. function &query_replace($table, $fields, &$values, $buffered = true)
  555. {
  556. return $this->insert_multiple("REPLACE INTO $table $fields VALUES", $values, $buffered);
  557. }
  558.  
  559. /**
  560. * Executes an INSERT or REPLACE query with multiple values, splitting large queries into manageable chunks based on $this->maxpacket
  561. *
  562. * @param string The text of the first part of the SQL query to be executed - example "INSERT INTO table (field1, field2) VALUES"
  563. * @param mixed The values to be inserted. Example: (0 => "('value1', 'value2')", 1 => "('value3', 'value4')")
  564. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  565. *
  566. * @return mixed
  567. */
  568. function insert_multiple($sql, &$values, $buffered)
  569. {
  570. if ($this->maxpacket == 0)
  571. {
  572. // must do a READ query on the WRITE link here!
  573. $vars = $this->query_write("SHOW VARIABLES LIKE 'max_allowed_packet'");
  574. $var = $this->fetch_row($vars);
  575. $this->maxpacket = $var[1];
  576. $this->free_result($vars);
  577. }
  578.  
  579. $i = 0;
  580. $num_values = sizeof($values);
  581. $this->sql = $sql;
  582.  
  583. while ($i < $num_values)
  584. {
  585. $sql_length = strlen($this->sql);
  586. $value_length = strlen("\r\n" . $values["$i"] . ",");
  587.  
  588. if (($sql_length + $value_length) < $this->maxpacket)
  589. {
  590. $this->sql .= "\r\n" . $values["$i"] . ",";
  591. unset($values["$i"]);
  592. $i++;
  593. }
  594. else
  595. {
  596. $this->sql = (substr($this->sql, -1) == ',') ? substr($this->sql, 0, -1) : $this->sql;
  597. $this->execute_query($buffered, $this->connection_master);
  598. $this->sql = $sql;
  599. }
  600. }
  601. if ($this->sql != $sql)
  602. {
  603. $this->sql = (substr($this->sql, -1) == ',') ? substr($this->sql, 0, -1) : $this->sql;
  604. $this->execute_query($buffered, $this->connection_master);
  605. }
  606.  
  607. if (sizeof($values) == 1)
  608. {
  609. return $this->insert_id();
  610. }
  611. else
  612. {
  613. return true;
  614. }
  615. }
  616.  
  617. /**
  618. * Registers an SQL query to be executed at shutdown time. If shutdown functions are disabled, the query is run immediately.
  619. *
  620. * @param string The text of the SQL query to be executed
  621. * @param mixed (Optional) Allows particular shutdown queries to be labelled
  622. *
  623. * @return boolean
  624. */
  625. function shutdown_query($sql, $arraykey = -1)
  626. {
  627. if ($arraykey === -1)
  628. {
  629. $this->shutdownqueries[] = $sql;
  630. return true;
  631. }
  632. else
  633. {
  634. $this->shutdownqueries["$arraykey"] = $sql;
  635. return true;
  636. }
  637. }
  638.  
  639. /**
  640. * Returns the number of rows contained within a query result set
  641. *
  642. * @param string The query result ID we are dealing with
  643. *
  644. * @return integer
  645. */
  646. function num_rows($queryresult)
  647. {
  648. return @$this->functions['num_rows']($queryresult);
  649. }
  650.  
  651. /**
  652. * Returns the number of fields contained within a query result set
  653. *
  654. * @param string The query result ID we are dealing with
  655. *
  656. * @return integer
  657. */
  658. function num_fields($queryresult)
  659. {
  660. return @$this->functions['num_fields']($queryresult);
  661. }
  662.  
  663. /**
  664. * Returns the name of a field from within a query result set
  665. *
  666. * @param string The query result ID we are dealing with
  667. * @param integer The index position of the field
  668. *
  669. * @return string
  670. */
  671. function field_name($queryresult, $index)
  672. {
  673. return @$this->functions['field_name']($queryresult, $index);
  674. }
  675.  
  676. /**
  677. * Returns the ID of the item just inserted into an auto-increment field
  678. *
  679. * @return integer
  680. */
  681. function insert_id()
  682. {
  683. return @$this->functions['insert_id']($this->connection_master);
  684. }
  685.  
  686. /**
  687. * Returns the name of the character set
  688. *
  689. * @return string
  690. */
  691. function client_encoding()
  692. {
  693. return @$this->functions['client_encoding']($this->connection_master);
  694. }
  695.  
  696. /**
  697. * Closes the connection to the database server
  698. *
  699. * @return integer
  700. */
  701. function close()
  702. {
  703. return @$this->functions['close']($this->connection_master);
  704. }
  705.  
  706. /**
  707. * Escapes a string to make it safe to be inserted into an SQL query
  708. *
  709. * @param string The string to be escaped
  710. *
  711. * @return string
  712. */
  713. function escape_string($string)
  714. {
  715. if ($this->functions['escape_string'] == $this->functions['real_escape_string'])
  716. {
  717. return $this->functions['escape_string']($string, $this->connection_master);
  718. }
  719. else
  720. {
  721. return $this->functions['escape_string']($string);
  722. }
  723. }
  724.  
  725. /**
  726. * Escapes a string using the appropriate escape character for the RDBMS for use in LIKE conditions
  727. *
  728. * @param string The string to be escaped
  729. *
  730. * @return string
  731. */
  732. function escape_string_like($string)
  733. {
  734. return str_replace(array('%', '_') , array('\%' , '\_') , $this->escape_string($string));
  735. }
  736.  
  737. /**
  738. * Takes a piece of data and prepares it to be put into an SQL query by adding quotes etc.
  739. *
  740. * @param mixed The data to be used
  741. *
  742. * @return mixed The prepared data
  743. */
  744. function sql_prepare($value)
  745. {
  746. if (is_string($value))
  747. {
  748. return "'" . $this->escape_string($value) . "'";
  749. }
  750. else if (is_numeric($value) AND $value + 0 == $value)
  751. {
  752. return $value;
  753. }
  754. else if (is_bool($value))
  755. {
  756. return $value ? 1 : 0;
  757. }
  758. else
  759. {
  760. return "'" . $this->escape_string($value) . "'";
  761. }
  762. }
  763.  
  764. /**
  765. * Fetches a row from a query result and returns the values from that row as an array
  766. *
  767. * The value of $type defines whether the array will have numeric or associative keys, or both
  768. *
  769. * @param string The query result ID we are dealing with
  770. * @param integer One of DBARRAY_ASSOC / DBARRAY_NUM / DBARRAY_BOTH
  771. *
  772. * @return array
  773. */
  774. function fetch_array($queryresult, $type = DBARRAY_ASSOC)
  775. {
  776. return @$this->functions['fetch_array']($queryresult, $this->fetchtypes["$type"]);
  777. }
  778.  
  779. /**
  780. * Fetches a row from a query result and returns the values from that row as an array with numeric keys
  781. *
  782. * @param string The query result ID we are dealing with
  783. *
  784. * @return array
  785. */
  786. function fetch_row($queryresult)
  787. {
  788. return @$this->functions['fetch_row']($queryresult);
  789. }
  790.  
  791. /**
  792. * Fetches a row information from a query result and returns the values from that row as an array
  793. *
  794. * @param string The query result ID we are dealing with
  795. *
  796. * @return array
  797. */
  798. function fetch_field($queryresult)
  799. {
  800. return @$this->functions['fetch_field']($queryresult);
  801. }
  802.  
  803. /**
  804. * Moves the internal result pointer within a query result set
  805. *
  806. * @param string The query result ID we are dealing with
  807. * @param integer The position to which to move the pointer (first position is 0)
  808. *
  809. * @return boolean
  810. */
  811. function data_seek($queryresult, $index)
  812. {
  813. return @$this->functions['data_seek']($queryresult, $index);
  814. }
  815.  
  816. /**
  817. * Frees all memory associated with the specified query result
  818. *
  819. * @param string The query result ID we are dealing with
  820. *
  821. * @return boolean
  822. */
  823. function free_result($queryresult)
  824. {
  825. $this->sql = '';
  826. return @$this->functions['free_result']($queryresult);
  827. }
  828.  
  829. /**
  830. * Retuns the number of rows affected by the most recent insert/replace/update query
  831. *
  832. * @return integer
  833. */
  834. function affected_rows()
  835. {
  836. $this->rows = $this->functions['affected_rows']($this->connection_recent);
  837. return $this->rows;
  838. }
  839.  
  840. /**
  841. * Lock tables
  842. *
  843. * @param mixed List of tables to lock
  844. * @param string Type of lock to perform
  845. *
  846. */
  847. function lock_tables($tablelist)
  848. {
  849. if (!empty($tablelist) AND is_array($tablelist))
  850. {
  851. // Don't lock tables if we know we might get stuck with them locked (pconnect = true)
  852. // mysqli doesn't support pconnect! YAY!
  853. if (strtolower($this->registry->config['Database']['dbtype']) != 'mysqli' AND $this->registry->config['MasterServer']['usepconnect'])
  854. {
  855. return;
  856. }
  857.  
  858. $sql = '';
  859. foreach($tablelist AS $name => $type)
  860. {
  861. $sql .= (!empty($sql) ? ', ' : '') . TABLE_PREFIX . $name . " " . $type;
  862. }
  863.  
  864. $this->query_write("LOCK TABLES $sql");
  865. $this->locked = true;
  866.  
  867. }
  868. }
  869.  
  870. /**
  871. * Unlock tables
  872. *
  873. */
  874. function unlock_tables()
  875. {
  876. # must be called from exec_shutdown as tables can get stuck locked if pconnects are enabled
  877. # note: the above case never actually happens as we skip the lock if pconnects are enabled (to be safe) =)
  878. if ($this->locked)
  879. {
  880. $this->query_write("UNLOCK TABLES");
  881. }
  882. }
  883.  
  884. /**
  885. * Returns the text of the error message from previous database operation
  886. *
  887. * @return string
  888. */
  889. function error()
  890. {
  891. if ($this->connection_recent === null)
  892. {
  893. $this->error = '';
  894. }
  895. else
  896. {
  897. $this->error = $this->functions['error']($this->connection_recent);
  898. }
  899. return $this->error;
  900. }
  901.  
  902. /**
  903. * Returns the numerical value of the error message from previous database operation
  904. *
  905. * @return integer
  906. */
  907. function errno()
  908. {
  909. if ($this->connection_recent === null)
  910. {
  911. $this->errno = 0;
  912. }
  913. else
  914. {
  915. $this->errno = $this->functions['errno']($this->connection_recent);
  916. }
  917. return $this->errno;
  918. }
  919.  
  920. /**
  921. * Switches database error display ON
  922. */
  923. function show_errors()
  924. {
  925. $this->reporterror = true;
  926. }
  927.  
  928. /**
  929. * Switches database error display OFF
  930. */
  931. function hide_errors()
  932. {
  933. $this->reporterror = false;
  934. }
  935.  
  936. /**
  937. * Halts execution of the entire system and displays an error message
  938. *
  939. * @param string Text of the error message. Leave blank to use $this->sql as error text.
  940. *
  941. * @return integer
  942. */
  943. function halt($errortext = '')
  944. {
  945. global $vbulletin;
  946.  
  947. if ($this->connection_recent)
  948. {
  949. $this->error = $this->error($this->connection_recent);
  950. $this->errno = $this->errno($this->connection_recent);
  951. }
  952.  
  953. if ($this->reporterror)
  954. {
  955. if ($errortext == '')
  956. {
  957. $this->sql = "Invalid SQL:\r\n" . chop($this->sql) . ';';
  958. $errortext =& $this->sql;
  959. }
  960.  
  961. if (!headers_sent())
  962. {
  963. if (SAPI_NAME == 'cgi' OR SAPI_NAME == 'cgi-fcgi')
  964. {
  965. header('Status: 503 Service Unavailable');
  966. }
  967. else
  968. {
  969. header('HTTP/1.1 503 Service Unavailable');
  970. }
  971. }
  972.  
  973. $vboptions =& $vbulletin->options;
  974. $technicalemail =& $vbulletin->config['Database']['technicalemail'];
  975. $bbuserinfo =& $vbulletin->userinfo;
  976. $requestdate = date('l, F jS Y @ h:i:s A', TIMENOW);
  977. $date = date('l, F jS Y @ h:i:s A');
  978. $scriptpath = str_replace('&amp;', '&', $vbulletin->scriptpath);
  979. $referer = REFERRER;
  980. $ipaddress = IPADDRESS;
  981. $classname = get_class($this);
  982.  
  983. if ($this->connection_recent)
  984. {
  985. $this->hide_errors();
  986. list($mysqlversion) = $this->query_first("SELECT VERSION() AS version", DBARRAY_NUM);
  987. $this->show_errors();
  988. }
  989.  
  990. $display_db_error = (VB_AREA == 'Upgrade' OR VB_AREA == 'Install' OR $vbulletin->userinfo['usergroupid'] == 6 OR ($vbulletin->userinfo['permissions']['adminpermissions'] & $vbulletin->bf_ugp_adminpermissions));
  991.  
  992. // Hide the MySQL Version if its going in the source
  993. if (!$display_db_error)
  994. {
  995. $mysqlversion = '';
  996. }
  997.  
  998. eval('$message = "' . str_replace('"', '\"', file_get_contents(DIR . '/includes/database_error_message.html')) . '";');
  999.  
  1000. require_once(DIR . '/includes/functions_log_error.php');
  1001. if (function_exists('log_vbulletin_error'))
  1002. {
  1003. log_vbulletin_error($message, 'database');
  1004. }
  1005.  
  1006. if ($technicalemail != '' AND !$vbulletin->options['disableerroremail'] AND verify_email_vbulletin_error($this->errno, 'database'))
  1007. {
  1008. // If vBulletinHook is defined then we know that options are loaded, so we can then use vbmail
  1009. if (class_exists('vBulletinHook'))
  1010. {
  1011. @vbmail($technicalemail, $this->appshortname . ' Database Error!', $message, true, $technicalemail);
  1012. }
  1013. else
  1014. {
  1015. @mail($technicalemail, $this->appshortname . ' Database Error!', preg_replace("#(\r\n|\r|\n)#s", (@ini_get('sendmail_path') === '') ? "\r\n" : "\n", $message), "From: $technicalemail");
  1016. }
  1017. }
  1018.  
  1019. if ($display_db_error)
  1020. {
  1021. // display error message on screen
  1022. $message = '<form><textarea rows="15" cols="70" wrap="off" id="message">' . htmlspecialchars_uni($message) . '</textarea></form>';
  1023. }
  1024. else
  1025. {
  1026. // display hidden error message
  1027. $message = "\r\n<!--\r\n" . htmlspecialchars_uni($message) . "\r\n-->\r\n";
  1028. }
  1029.  
  1030. if ($vbulletin->options['bburl'])
  1031. {
  1032. $imagepath = $vbulletin->options['bburl'];
  1033. }
  1034. else
  1035. {
  1036. // this might not work with too many slashes in the archive
  1037. $imagepath = (VB_AREA == 'Forum' ? '.' : '..');
  1038. }
  1039.  
  1040. eval('$message = "' . str_replace('"', '\"', file_get_contents(DIR . '/includes/database_error_page.html')) . '";');
  1041. // This is needed so IE doesn't show the pretty error messages
  1042. $message .= str_repeat(' ', 512);
  1043. die($message);
  1044. }
  1045. else if (!empty($errortext))
  1046. {
  1047. $this->error = $errortext;
  1048. }
  1049. }
  1050. }
  1051.  
  1052. // #############################################################################
  1053. // MySQLi Database Class
  1054.  
  1055. /**
  1056. * Class to interface with a MySQL 4.1 database
  1057. *
  1058. * This class also handles data replication between a master and slave(s) servers
  1059. *
  1060. * @package vBulletin
  1061. * @version $Revision: 43870 $
  1062. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  1063. */
  1064. class vB_Database_MySQLi extends vB_Database
  1065. {
  1066. /**
  1067. * Array of function names, mapping a simple name to the RDBMS specific function name
  1068. *
  1069. * @var array
  1070. */
  1071. var $functions = array(
  1072. 'connect' => 'mysqli_real_connect',
  1073. 'pconnect' => 'mysqli_real_connect', // mysqli doesn't support persistent connections THANK YOU!
  1074. 'select_db' => 'mysqli_select_db',
  1075. 'query' => 'mysqli_query',
  1076. 'query_unbuffered' => 'mysqli_unbuffered_query',
  1077. 'fetch_row' => 'mysqli_fetch_row',
  1078. 'fetch_array' => 'mysqli_fetch_array',
  1079. 'fetch_field' => 'mysqli_fetch_field',
  1080. 'free_result' => 'mysqli_free_result',
  1081. 'data_seek' => 'mysqli_data_seek',
  1082. 'error' => 'mysqli_error',
  1083. 'errno' => 'mysqli_errno',
  1084. 'affected_rows' => 'mysqli_affected_rows',
  1085. 'num_rows' => 'mysqli_num_rows',
  1086. 'num_fields' => 'mysqli_num_fields',
  1087. 'field_name' => 'mysqli_field_tell',
  1088. 'insert_id' => 'mysqli_insert_id',
  1089. 'escape_string' => 'mysqli_real_escape_string',
  1090. 'real_escape_string' => 'mysqli_real_escape_string',
  1091. 'close' => 'mysqli_close',
  1092. 'client_encoding' => 'mysqli_client_encoding',
  1093. );
  1094.  
  1095. /**
  1096. * Array of constants for use in fetch_array
  1097. *
  1098. * @var array
  1099. */
  1100. var $fetchtypes = array(
  1101. DBARRAY_NUM => MYSQLI_NUM,
  1102. DBARRAY_ASSOC => MYSQLI_ASSOC,
  1103. DBARRAY_BOTH => MYSQLI_BOTH
  1104. );
  1105.  
  1106. /**
  1107. * Initialize database connection(s)
  1108. *
  1109. * Connects to the specified master database server, and also to the slave server if it is specified
  1110. *
  1111. * @param string Name of the database server - should be either 'localhost' or an IP address
  1112. * @param integer Port of the database server - usually 3306
  1113. * @param string Username to connect to the database server
  1114. * @param string Password associated with the username for the database server
  1115. * @param string Persistent Connections - Not supported with MySQLi
  1116. * @param string Configuration file from config.php.ini (my.ini / my.cnf)
  1117. * @param string Mysqli Connection Charset PHP 5.1.0+ or 5.0.5+ / MySQL 4.1.13+ or MySQL 5.1.10+ Only
  1118. *
  1119. * @return object Mysqli Resource
  1120. */
  1121. function db_connect($servername, $port, $username, $password, $usepconnect, $configfile = '', $charset = '')
  1122. {
  1123. if (function_exists('catch_db_error'))
  1124. {
  1125. set_error_handler('catch_db_error');
  1126. }
  1127.  
  1128. $link = mysqli_init();
  1129. # Set Options Connection Options
  1130. if (!empty($configfile))
  1131. {
  1132. mysqli_options($link, MYSQLI_READ_DEFAULT_FILE, $configfile);
  1133. }
  1134.  
  1135. // this will execute at most 5 times, see catch_db_error()
  1136. do
  1137. {
  1138. $connect = $this->functions['connect']($link, $servername, $username, $password, '', $port);
  1139. }
  1140. while ($connect == false AND $this->reporterror);
  1141.  
  1142. restore_error_handler();
  1143.  
  1144. if (!empty($charset))
  1145. {
  1146. if (function_exists('mysqli_set_charset'))
  1147. {
  1148. mysqli_set_charset($link, $charset);
  1149. }
  1150. else
  1151. {
  1152. $this->sql = "SET NAMES $charset";
  1153. $this->execute_query(true, $link);
  1154. }
  1155. }
  1156.  
  1157. return (!$connect) ? false : $link;
  1158. }
  1159.  
  1160. /**
  1161. * Executes an SQL query through the specified connection
  1162. *
  1163. * @param boolean Whether or not to run this query buffered (true) or unbuffered (false). Default is unbuffered.
  1164. * @param string The connection ID to the database server
  1165. *
  1166. * @return string
  1167. */
  1168. function &execute_query($buffered = true, &$link)
  1169. {
  1170. $this->connection_recent =& $link;
  1171. $this->querycount++;
  1172.  
  1173. if ($queryresult = mysqli_query($link, $this->sql, ($buffered ? MYSQLI_STORE_RESULT : MYSQLI_USE_RESULT)))
  1174. {
  1175. // unset $sql to lower memory .. this isn't an error, so it's not needed
  1176. $this->sql = '';
  1177.  
  1178. return $queryresult;
  1179. }
  1180. else
  1181. {
  1182. $this->halt();
  1183.  
  1184. // unset $sql to lower memory .. error will have already been thrown
  1185. $this->sql = '';
  1186. }
  1187. }
  1188.  
  1189. /**
  1190. * Simple wrapper for select_db(), to allow argument order changes
  1191. *
  1192. * @param string Database name
  1193. * @param integer Link identifier
  1194. *
  1195. * @return boolean
  1196. */
  1197. function select_db_wrapper($database = '', $link = null)
  1198. {
  1199. return $this->functions['select_db']($link, $database);
  1200. }
  1201.  
  1202. /**
  1203. * Escapes a string to make it safe to be inserted into an SQL query
  1204. *
  1205. * @param string The string to be escaped
  1206. *
  1207. * @return string
  1208. */
  1209. function escape_string($string)
  1210. {
  1211. return $this->functions['real_escape_string']($this->connection_master, $string);
  1212. }
  1213.  
  1214. /**
  1215. * Returns the name of a field from within a query result set
  1216. *
  1217. * @param string The query result ID we are dealing with
  1218. * @param integer The index position of the field
  1219. *
  1220. * @return string
  1221. */
  1222. function field_name($queryresult, $index)
  1223. {
  1224. $field = @$this->functions['fetch_field']($queryresult);
  1225. return $field->name;
  1226. }
  1227. }
  1228.  
  1229. // #############################################################################
  1230. // datastore class
  1231.  
  1232. /**
  1233. * Class for fetching and initializing the vBulletin datastore from the database
  1234. *
  1235. * @package vBulletin
  1236. * @version $Revision: 43870 $
  1237. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  1238. */
  1239. class vB_Datastore
  1240. {
  1241. /**
  1242. * Default items that are always loaded by fetch();
  1243. *
  1244. * @var array
  1245. */
  1246. var $defaultitems = array(
  1247. 'options',
  1248. 'bitfields',
  1249. 'attachmentcache',
  1250. 'forumcache',
  1251. 'usergroupcache',
  1252. 'stylecache',
  1253. 'languagecache',
  1254. 'products',
  1255. 'pluginlist',
  1256. 'cron',
  1257. 'profilefield',
  1258. 'loadcache',
  1259. 'noticecache'
  1260. );
  1261.  
  1262. /**
  1263. * This variable contains a list of all items to be returned from the datastore
  1264. *
  1265. * @var array
  1266. */
  1267. var $itemarray = array();
  1268.  
  1269. /**
  1270. * This variable should be set to be a reference to the registry object
  1271. *
  1272. * @var vB_Registry
  1273. */
  1274. var $registry = null;
  1275.  
  1276. /**
  1277. * This variable should be set to be a reference to the database object
  1278. *
  1279. * @var vB_Database
  1280. */
  1281. var $dbobject = null;
  1282.  
  1283. /**
  1284. * Unique prefix for item's title, required for multiple forums on the same server using the same classes that read/write to memory
  1285. *
  1286. * @var string
  1287. */
  1288. var $prefix = '';
  1289.  
  1290. /**
  1291. * Constructor - establishes the database object to use for datastore queries
  1292. *
  1293. * @param vB_Registry The registry object
  1294. * @param vB_Database The database object
  1295. */
  1296. function vB_Datastore(&$registry, &$dbobject)
  1297. {
  1298. $this->registry =& $registry;
  1299. $this->dbobject =& $dbobject;
  1300.  
  1301. $this->prefix =& $this->registry->config['Datastore']['prefix'];
  1302.  
  1303. if (defined('SKIP_DEFAULTDATASTORE'))
  1304. {
  1305. $this->defaultitems = array('options', 'bitfields', 'pluginlist');
  1306. }
  1307.  
  1308. if (!is_object($registry))
  1309. {
  1310. trigger_error('<strong>vB_Datastore</strong>: $this->registry is not an object', E_USER_ERROR);
  1311. }
  1312. if (!is_object($dbobject))
  1313. {
  1314. trigger_error('<strong>vB_Datastore</strong>: $this->dbobject is not an object!', E_USER_ERROR);
  1315. }
  1316. }
  1317.  
  1318. /**
  1319. * Sorts the data returned from the cache and places it into appropriate places
  1320. *
  1321. * @param string The name of the data item to be processed
  1322. * @param mixed The data associated with the title
  1323. * @param integer If the data needs to be unserialized, 0 = no, 1 = yes, 2 = auto detect
  1324. *
  1325. * @return boolean
  1326. */
  1327. function register($title, $data, $unserialize_detect = 2)
  1328. {
  1329. // specifies whether or not $data should be an array
  1330. $try_unserialize = (($unserialize_detect == 2) AND ($data[0] == 'a' AND $data[1] == ':'));
  1331.  
  1332. if ($try_unserialize OR $unserialize_detect == 1)
  1333. {
  1334. // unserialize returned an error so return false
  1335. if (($data = unserialize($data)) === false)
  1336. {
  1337. return false;
  1338. }
  1339. }
  1340.  
  1341. if ($title == 'bitfields')
  1342. {
  1343. $registry =& $this->registry;
  1344.  
  1345. foreach (array_keys($data) AS $group)
  1346. {
  1347. $registry->{'bf_' . $group} =& $data["$group"];
  1348.  
  1349. $group_prefix = 'bf_' . $group . '_';
  1350. $group_info =& $data["$group"];
  1351.  
  1352. foreach (array_keys($group_info) AS $subgroup)
  1353. {
  1354. $registry->{$group_prefix . $subgroup} =& $group_info["$subgroup"];
  1355. }
  1356. }
  1357. }
  1358. else if (!empty($title))
  1359. {
  1360. $this->registry->$title = $data;
  1361. }
  1362.  
  1363. return true;
  1364. }
  1365.  
  1366. /**
  1367. * Fetches the contents of the datastore from the database
  1368. *
  1369. * @param array Array of items to fetch from the datastore
  1370. *
  1371. * @return void
  1372. */
  1373. function fetch($itemarray)
  1374. {
  1375. $db =& $this->dbobject;
  1376.  
  1377. $itemlist = "''";
  1378.  
  1379. foreach ($this->defaultitems AS $item)
  1380. {
  1381. $itemlist .= ",'" . $db->escape_string($item) . "'";
  1382. }
  1383.  
  1384. if (is_array($itemarray))
  1385. {
  1386. foreach ($itemarray AS $item)
  1387. {
  1388. $itemlist .= ",'" . $db->escape_string($item) . "'";
  1389. }
  1390. }
  1391.  
  1392. $this->do_db_fetch($itemlist);
  1393.  
  1394. $this->check_options();
  1395.  
  1396. // set the version number variable
  1397. $this->registry->versionnumber =& $this->registry->options['templateversion'];
  1398. }
  1399.  
  1400. /**
  1401. * Performs the actual fetching of the datastore items for the database, child classes may use this
  1402. *
  1403. * @param string title of the datastore item
  1404. *
  1405. * @return void
  1406. */
  1407. function do_db_fetch($itemlist)
  1408. {
  1409. $db =& $this->dbobject;
  1410.  
  1411. $dataitems = $db->query_read("
  1412. SELECT *
  1413. FROM " . TABLE_PREFIX . "datastore
  1414. WHERE title IN ($itemlist)
  1415. ");
  1416. while ($dataitem = $db->fetch_array($dataitems))
  1417. {
  1418. $this->register($dataitem['title'], $dataitem['data'], (isset($dataitem['unserialize']) ? $dataitem['unserialize'] : 2));
  1419. }
  1420. $db->free_result($dataitems);
  1421. }
  1422.  
  1423. /**
  1424. * Checks that the options item has come out of the datastore correctly
  1425. * and sets the 'versionnumber' variable
  1426. */
  1427. function check_options()
  1428. {
  1429. if (!isset($this->registry->options['templateversion']))
  1430. {
  1431. // fatal error - options not loaded correctly
  1432. require_once(DIR . '/includes/adminfunctions.php');
  1433. require_once(DIR . '/includes/functions.php');
  1434. $this->register('options', build_options(), 0);
  1435. }
  1436.  
  1437. // set the short version number
  1438. $this->registry->options['simpleversion'] = SIMPLE_VERSION . $this->registry->config['Misc']['jsver'];
  1439. }
  1440. }
  1441.  
  1442. // #############################################################################
  1443. // input handler class
  1444.  
  1445. /**#@+
  1446. * Ways of cleaning input. Should be mostly self-explanatory.
  1447. */
  1448. define('TYPE_NOCLEAN', 0); // no change
  1449.  
  1450. define('TYPE_BOOL', 1); // force boolean
  1451. define('TYPE_INT', 2); // force integer
  1452. define('TYPE_UINT', 3); // force unsigned integer
  1453. define('TYPE_NUM', 4); // force number
  1454. define('TYPE_UNUM', 5); // force unsigned number
  1455. define('TYPE_UNIXTIME', 6); // force unix datestamp (unsigned integer)
  1456. define('TYPE_STR', 7); // force trimmed string
  1457. define('TYPE_NOTRIM', 8); // force string - no trim
  1458. define('TYPE_NOHTML', 9); // force trimmed string with HTML made safe
  1459. define('TYPE_ARRAY', 10); // force array
  1460. define('TYPE_FILE', 11); // force file
  1461. define('TYPE_BINARY', 12); // force binary string
  1462. define('TYPE_NOHTMLCOND', 13); // force trimmed string with HTML made safe if determined to be unsafe
  1463.  
  1464. define('TYPE_ARRAY_BOOL', 101);
  1465. define('TYPE_ARRAY_INT', 102);
  1466. define('TYPE_ARRAY_UINT', 103);
  1467. define('TYPE_ARRAY_NUM', 104);
  1468. define('TYPE_ARRAY_UNUM', 105);
  1469. define('TYPE_ARRAY_UNIXTIME', 106);
  1470. define('TYPE_ARRAY_STR', 107);
  1471. define('TYPE_ARRAY_NOTRIM', 108);
  1472. define('TYPE_ARRAY_NOHTML', 109);
  1473. define('TYPE_ARRAY_ARRAY', 110);
  1474. define('TYPE_ARRAY_FILE', 11); // An array of "Files" behaves differently than other <input> arrays. TYPE_FILE handles both types.
  1475. define('TYPE_ARRAY_BINARY', 112);
  1476. define('TYPE_ARRAY_NOHTMLCOND',113);
  1477.  
  1478. define('TYPE_ARRAY_KEYS_INT', 202);
  1479. define('TYPE_ARRAY_KEYS_STR', 207);
  1480.  
  1481. define('TYPE_CONVERT_SINGLE', 100); // value to subtract from array types to convert to single types
  1482. define('TYPE_CONVERT_KEYS', 200); // value to subtract from array => keys types to convert to single types
  1483. /**#@-*/
  1484.  
  1485. // temporary
  1486. define('INT', TYPE_INT);
  1487. define('STR', TYPE_STR);
  1488. define('STR_NOHTML', TYPE_NOHTML);
  1489. define('FILE', TYPE_FILE);
  1490.  
  1491. /**
  1492. * Class to handle and sanitize variables from GET, POST and COOKIE etc
  1493. *
  1494. * @package vBulletin
  1495. * @version $Revision: 43870 $
  1496. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  1497. */
  1498. class vB_Input_Cleaner
  1499. {
  1500. /**
  1501. * Translation table for short name to long name
  1502. *
  1503. * @var array
  1504. */
  1505. var $shortvars = array(
  1506. 'f' => 'forumid',
  1507. 't' => 'threadid',
  1508. 'p' => 'postid',
  1509. 'u' => 'userid',
  1510. 'a' => 'announcementid',
  1511. 'c' => 'calendarid',
  1512. 'e' => 'eventid',
  1513. 'q' => 'query',
  1514. 'pp' => 'perpage',
  1515. 'page' => 'pagenumber',
  1516. 'sort' => 'sortfield',
  1517. 'order' => 'sortorder',
  1518. );
  1519.  
  1520. /**
  1521. * Translation table for short superglobal name to long superglobal name
  1522. *
  1523. * @var array
  1524. */
  1525. var $superglobal_lookup = array(
  1526. 'g' => '_GET',
  1527. 'p' => '_POST',
  1528. 'r' => '_REQUEST',
  1529. 'c' => '_COOKIE',
  1530. 's' => '_SERVER',
  1531. 'e' => '_ENV',
  1532. 'f' => '_FILES'
  1533. );
  1534.  
  1535. /**
  1536. * System state. The complete URL of the current page, without sessionhash
  1537. *
  1538. * @var string
  1539. */
  1540. var $scriptpath = '';
  1541.  
  1542. /**
  1543. * Reload URL. Complete URL of the current page including sessionhash
  1544. *
  1545. * @var string
  1546. */
  1547. var $reloadurl = '';
  1548.  
  1549. /**
  1550. * System state. The complete URL of the page for Who's Online purposes
  1551. *
  1552. * @var string
  1553. */
  1554. var $wolpath = '';
  1555.  
  1556. /**
  1557. * System state. The complete URL of the referring page
  1558. *
  1559. * @var string
  1560. */
  1561. var $url = '';
  1562.  
  1563. /**
  1564. * System state. The IP address of the current visitor
  1565. *
  1566. * @var string
  1567. */
  1568. var $ipaddress = '';
  1569.  
  1570. /**
  1571. * System state. An attempt to find a second IP for the current visitor (proxy etc)
  1572. *
  1573. * @var string
  1574. */
  1575. var $alt_ip = '';
  1576.  
  1577. /**
  1578. * A reference to the main registry object
  1579. *
  1580. * @var vB_Registry
  1581. */
  1582. var $registry = null;
  1583.  
  1584. /**
  1585. * Keep track of variables that have already been cleaned
  1586. *
  1587. * @var array
  1588. */
  1589. var $cleaned_vars = array();
  1590.  
  1591. /**
  1592. * Constructor
  1593. *
  1594. * First, reverses the effects of magic quotes on GPC
  1595. * Second, translates short variable names to long (u --> userid)
  1596. * Third, deals with $_COOKIE[userid] conflicts
  1597. *
  1598. * @param vB_Registry The instance of the vB_Registry object
  1599. */
  1600. function vB_Input_Cleaner(&$registry)
  1601. {
  1602. $this->registry =& $registry;
  1603.  
  1604. if (!is_array($GLOBALS))
  1605. {
  1606. die('<strong>Fatal Error:</strong> Invalid URL.');
  1607. }
  1608.  
  1609. // overwrite GET[x] and REQUEST[x] with POST[x] if it exists (overrides server's GPC order preference)
  1610. if ($_SERVER['REQUEST_METHOD'] == 'POST')
  1611. {
  1612. foreach (array_keys($_POST) AS $key)
  1613. {
  1614. if (isset($_GET["$key"]))
  1615. {
  1616. $_GET["$key"] = $_REQUEST["$key"] = $_POST["$key"];
  1617. }
  1618. }
  1619. }
  1620.  
  1621. // deal with session bypass situation
  1622. if (!defined('SESSION_BYPASS'))
  1623. {
  1624. define('SESSION_BYPASS', !empty($_REQUEST['bypass']));
  1625. }
  1626.  
  1627. // reverse the effects of magic quotes if necessary
  1628. if (function_exists('get_magic_quotes_gpc') AND get_magic_quotes_gpc())
  1629. {
  1630. $this->stripslashes_deep($_REQUEST); // needed for some reason (at least on php5 - not tested on php4)
  1631. $this->stripslashes_deep($_GET);
  1632. $this->stripslashes_deep($_POST);
  1633. $this->stripslashes_deep($_COOKIE);
  1634.  
  1635. if (is_array($_FILES))
  1636. {
  1637. foreach ($_FILES AS $key => $val)
  1638. {
  1639. $_FILES["$key"]['tmp_name'] = str_replace('\\', '\\\\', $val['tmp_name']);
  1640. }
  1641. $this->stripslashes_deep($_FILES);
  1642. }
  1643. }
  1644. set_magic_quotes_runtime(0);
  1645. @ini_set('magic_quotes_sybase', 0);
  1646.  
  1647. foreach (array('_GET', '_POST') AS $arrayname)
  1648. {
  1649. if (isset($GLOBALS["$arrayname"]['do']))
  1650. {
  1651. $GLOBALS["$arrayname"]['do'] = trim($GLOBALS["$arrayname"]['do']);
  1652. }
  1653.  
  1654. $this->convert_shortvars($GLOBALS["$arrayname"]);
  1655. }
  1656.  
  1657. // set the AJAX flag if we have got an AJAX submission
  1658. if ($_SERVER['REQUEST_METHOD'] == 'POST' AND $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest')
  1659. {
  1660. $_POST['ajax'] = $_REQUEST['ajax'] = 1;
  1661. }
  1662.  
  1663. // reverse the effects of register_globals if necessary
  1664. if (@ini_get('register_globals') OR !@ini_get('gpc_order'))
  1665. {
  1666. foreach ($this->superglobal_lookup AS $arrayname)
  1667. {
  1668. $registry->superglobal_size["$arrayname"] = sizeof($GLOBALS["$arrayname"]);
  1669.  
  1670. foreach (array_keys($GLOBALS["$arrayname"]) AS $varname)
  1671. {
  1672. // make sure we dont unset any global arrays like _SERVER
  1673. if (!in_array($varname, $this->superglobal_lookup))
  1674. {
  1675. unset($GLOBALS["$varname"]);
  1676. }
  1677. }
  1678. }
  1679. }
  1680. else
  1681. {
  1682. foreach ($this->superglobal_lookup AS $arrayname)
  1683. {
  1684. $registry->superglobal_size["$arrayname"] = sizeof($GLOBALS["$arrayname"]);
  1685. }
  1686. }
  1687.  
  1688. // deal with cookies that may conflict with _GET and _POST data, and create our own _REQUEST with no _COOKIE input
  1689. foreach (array_keys($_COOKIE) AS $varname)
  1690. {
  1691. unset($_REQUEST["$varname"]);
  1692. if (isset($_POST["$varname"]))
  1693. {
  1694. $_REQUEST["$varname"] =& $_POST["$varname"];
  1695. }
  1696. else if (isset($_GET["$varname"]))
  1697. {
  1698. $_REQUEST["$varname"] =& $_GET["$varname"];
  1699. }
  1700. }
  1701.  
  1702. // fetch client IP address
  1703. $registry->ipaddress = $this->fetch_ip();
  1704. define('IPADDRESS', $registry->ipaddress);
  1705.  
  1706. // attempt to fetch IP address from behind proxies - useful, but don't rely on it...
  1707. $registry->alt_ip = $this->fetch_alt_ip();
  1708. define('ALT_IP', $registry->alt_ip);
  1709.  
  1710. // defines if the current page was visited via SSL or not
  1711. define('REQ_PROTOCOL', (($_SERVER['HTTPS'] == 'on' OR $_SERVER['HTTPS'] == '1') ? 'https' : 'http'));
  1712.  
  1713. // fetch complete url of current page
  1714. $registry->scriptpath = $this->fetch_scriptpath();
  1715. define('SCRIPTPATH', $registry->scriptpath);
  1716.  
  1717. // fetch url of current page without the variable string
  1718. $quest_pos = strpos($registry->scriptpath, '?');
  1719. if ($quest_pos !== false)
  1720. {
  1721. $registry->script = substr($registry->scriptpath, 0, $quest_pos);
  1722. }
  1723. else
  1724. {
  1725. $registry->script = $registry->scriptpath;
  1726. }
  1727. define('SCRIPT', $registry->script);
  1728.  
  1729. // fetch url of current page for Who's Online
  1730. $registry->wolpath = $this->fetch_wolpath();
  1731. define('WOLPATH', $registry->wolpath);
  1732.  
  1733. // define session constants
  1734. define('SESSION_HOST', substr($registry->ipaddress, 0, 15));
  1735.  
  1736. // define some useful contants related to environment
  1737. define('USER_AGENT', $_SERVER['HTTP_USER_AGENT']);
  1738. define('REFERRER', $_SERVER['HTTP_REFERER']);
  1739. }
  1740.  
  1741. /**
  1742. * Makes data in an array safe to use
  1743. *
  1744. * @param array The source array containing the data to be cleaned
  1745. * @param array Array of variable names and types we want to extract from the source array
  1746. *
  1747. * @return array
  1748. */
  1749. function &clean_array(&$source, $variables)
  1750. {
  1751. $return = array();
  1752.  
  1753. foreach ($variables AS $varname => $vartype)
  1754. {
  1755. $return["$varname"] =& $this->clean($source["$varname"], $vartype, isset($source["$varname"]));
  1756. }
  1757.  
  1758. return $return;
  1759. }
  1760.  
  1761. /**
  1762. * Makes GPC variables safe to use
  1763. *
  1764. * @param string Either, g, p, c, r or f (corresponding to get, post, cookie, request and files)
  1765. * @param array Array of variable names and types we want to extract from the source array
  1766. *
  1767. * @return array
  1768. */
  1769. function clean_array_gpc($source, $variables)
  1770. {
  1771. $sg =& $GLOBALS[$this->superglobal_lookup["$source"]];
  1772.  
  1773. foreach ($variables AS $varname => $vartype)
  1774. {
  1775. // clean a variable only once unless its a different type
  1776. if (!isset($this->cleaned_vars["$varname"]) OR $this->cleaned_vars["$varname"] != $vartype)
  1777. {
  1778. $this->registry->GPC_exists["$varname"] = isset($sg["$varname"]);
  1779. $this->registry->GPC["$varname"] =& $this->clean(
  1780. $sg["$varname"],
  1781. $vartype,
  1782. isset($sg["$varname"])
  1783. );
  1784. $this->cleaned_vars["$varname"] = $vartype;
  1785. }
  1786. }
  1787. }
  1788.  
  1789. /**
  1790. * Makes a single GPC variable safe to use and returns it
  1791. *
  1792. * @param array The source array containing the data to be cleaned
  1793. * @param string The name of the variable in which we are interested
  1794. * @param integer The type of the variable in which we are interested
  1795. *
  1796. * @return mixed
  1797. */
  1798. function &clean_gpc($source, $varname, $vartype = TYPE_NOCLEAN)
  1799. {
  1800. // clean a variable only once unless its a different type
  1801. if (!isset($this->cleaned_vars["$varname"]) OR $this->cleaned_vars["$varname"] != $vartype)
  1802. {
  1803. $sg =& $GLOBALS[$this->superglobal_lookup["$source"]];
  1804.  
  1805. $this->registry->GPC_exists["$varname"] = isset($sg["$varname"]);
  1806. $this->registry->GPC["$varname"] =& $this->clean(
  1807. $sg["$varname"],
  1808. $vartype,
  1809. isset($sg["$varname"])
  1810. );
  1811. $this->cleaned_vars["$varname"] = $vartype;
  1812. }
  1813.  
  1814. return $this->registry->GPC["$varname"];
  1815. }
  1816.  
  1817. /**
  1818. * Makes a single variable safe to use and returns it
  1819. *
  1820. * @param mixed The variable to be cleaned
  1821. * @param integer The type of the variable in which we are interested
  1822. * @param boolean Whether or not the variable to be cleaned actually is set
  1823. *
  1824. * @return mixed The cleaned value
  1825. */
  1826. function &clean(&$var, $vartype = TYPE_NOCLEAN, $exists = true)
  1827. {
  1828. if ($exists)
  1829. {
  1830. if ($vartype < TYPE_CONVERT_SINGLE)
  1831. {
  1832. $this->do_clean($var, $vartype);
  1833. }
  1834. else if (is_array($var))
  1835. {
  1836. if ($vartype >= TYPE_CONVERT_KEYS)
  1837. {
  1838. $var = array_keys($var);
  1839. $vartype -= TYPE_CONVERT_KEYS;
  1840. }
  1841. else
  1842. {
  1843. $vartype -= TYPE_CONVERT_SINGLE;
  1844. }
  1845.  
  1846. foreach (array_keys($var) AS $key)
  1847. {
  1848. $this->do_clean($var["$key"], $vartype);
  1849. }
  1850. }
  1851. else
  1852. {
  1853. $var = array();
  1854. }
  1855. return $var;
  1856. }
  1857. else
  1858. {
  1859. if ($vartype < TYPE_CONVERT_SINGLE)
  1860. {
  1861. switch ($vartype)
  1862. {
  1863. case TYPE_INT:
  1864. case TYPE_UINT:
  1865. case TYPE_NUM:
  1866. case TYPE_UNUM:
  1867. case TYPE_UNIXTIME:
  1868. {
  1869. $var = 0;
  1870. break;
  1871. }
  1872. case TYPE_STR:
  1873. case TYPE_NOHTML:
  1874. case TYPE_NOTRIM:
  1875. case TYPE_NOHTMLCOND:
  1876. {
  1877. $var = '';
  1878. break;
  1879. }
  1880. case TYPE_BOOL:
  1881. {
  1882. $var = 0;
  1883. break;
  1884. }
  1885. case TYPE_ARRAY:
  1886. case TYPE_FILE:
  1887. {
  1888. $var = array();
  1889. break;
  1890. }
  1891. case TYPE_NOCLEAN:
  1892. {
  1893. $var = null;
  1894. break;
  1895. }
  1896. default:
  1897. {
  1898. $var = null;
  1899. }
  1900. }
  1901. }
  1902. else
  1903. {
  1904. $var = array();
  1905. }
  1906.  
  1907. return $var;
  1908. }
  1909. }
  1910.  
  1911. /**
  1912. * Does the actual work to make a variable safe
  1913. *
  1914. * @param mixed The data we want to make safe
  1915. * @param integer The type of the data
  1916. *
  1917. * @return mixed
  1918. */
  1919. function &do_clean(&$data, $type)
  1920. {
  1921. static $booltypes = array('1', 'yes', 'y', 'true');
  1922.  
  1923. switch ($type)
  1924. {
  1925. case TYPE_INT: $data = intval($data); break;
  1926. case TYPE_UINT: $data = ($data = intval($data)) < 0 ? 0 : $data; break;
  1927. case TYPE_NUM: $data = strval($data) + 0; break;
  1928. case TYPE_UNUM: $data = strval($data) + 0;
  1929. $data = ($data < 0) ? 0 : $data; break;
  1930. case TYPE_BINARY: $data = strval($data); break;
  1931. case TYPE_STR: $data = trim(strval($data)); break;
  1932. case TYPE_NOTRIM: $data = strval($data); break;
  1933. case TYPE_NOHTML: $data = htmlspecialchars_uni(trim(strval($data))); break;
  1934. case TYPE_BOOL: $data = in_array(strtolower($data), $booltypes) ? 1 : 0; break;
  1935. case TYPE_ARRAY: $data = (is_array($data)) ? $data : array(); break;
  1936. case TYPE_NOHTMLCOND:
  1937. {
  1938. $data = trim(strval($data));
  1939. if (strcspn($data, '<>"') < strlen($data) OR (strpos($data, '&') !== false AND !preg_match('/&(#[0-9]+|amp|lt|gt|quot);/si', $data)))
  1940. {
  1941. // data is not htmlspecialchars because it still has characters or entities it shouldn't
  1942. $data = htmlspecialchars_uni($data);
  1943. }
  1944. break;
  1945. }
  1946. case TYPE_FILE:
  1947. {
  1948. // perhaps redundant :p
  1949. if (is_array($data))
  1950. {
  1951. if (is_array($data['name']))
  1952. {
  1953. $files = count($data['name']);
  1954. for ($index = 0; $index < $files; $index++)
  1955. {
  1956. $data['name']["$index"] = trim(strval($data['name']["$index"]));
  1957. $data['type']["$index"] = trim(strval($data['type']["$index"]));
  1958. $data['tmp_name']["$index"] = trim(strval($data['tmp_name']["$index"]));
  1959. $data['error']["$index"] = intval($data['error']["$index"]);
  1960. $data['size']["$index"] = intval($data['size']["$index"]);
  1961. }
  1962. }
  1963. else
  1964. {
  1965. $data['name'] = trim(strval($data['name']));
  1966. $data['type'] = trim(strval($data['type']));
  1967. $data['tmp_name'] = trim(strval($data['tmp_name']));
  1968. $data['error'] = intval($data['error']);
  1969. $data['size'] = intval($data['size']);
  1970. }
  1971. }
  1972. else
  1973. {
  1974. $data = array(
  1975. 'name' => '',
  1976. 'type' => '',
  1977. 'tmp_name' => '',
  1978. 'error' => 0,
  1979. 'size' => 4, // UPLOAD_ERR_NO_FILE
  1980. );
  1981. }
  1982. break;
  1983. }
  1984. case TYPE_UNIXTIME:
  1985. {
  1986. if (is_array($data))
  1987. {
  1988. $data = $this->clean($data, TYPE_ARRAY_UINT);
  1989. if ($data['month'] AND $data['day'] AND $data['year'])
  1990. {
  1991. require_once(DIR . '/includes/functions_misc.php');
  1992. $data = vbmktime($data['hour'], $data['minute'], $data['second'], $data['month'], $data['day'], $data['year']);
  1993. }
  1994. else
  1995. {
  1996. $data = 0;
  1997. }
  1998. }
  1999. else
  2000. {
  2001. $data = ($data = intval($data)) < 0 ? 0 : $data;
  2002. }
  2003. break;
  2004. }
  2005. // null actions should be deifned here so we can still catch typos below
  2006. case TYPE_NOCLEAN:
  2007. {
  2008. break;
  2009. }
  2010.  
  2011. default:
  2012. {
  2013. if ($this->registry->debug)
  2014. {
  2015. trigger_error('vB_Input_Cleaner::do_clean() Invalid data type specified', E_USER_WARNING);
  2016. }
  2017. }
  2018. }
  2019.  
  2020. // strip out characters that really have no business being in non-binary data
  2021. switch ($type)
  2022. {
  2023. case TYPE_STR:
  2024. case TYPE_NOTRIM:
  2025. case TYPE_NOHTML:
  2026. case TYPE_NOHTMLCOND:
  2027. $data = str_replace(chr(0), '', $data);
  2028. }
  2029.  
  2030. return $data;
  2031. }
  2032.  
  2033. /**
  2034. * Removes HTML characters and potentially unsafe scripting words from a string
  2035. *
  2036. * @param string The variable we want to make safe
  2037. *
  2038. * @return string
  2039. */
  2040. function xss_clean($var)
  2041. {
  2042. static
  2043. $preg_find = array('#^javascript#i', '#^vbscript#i'),
  2044. $preg_replace = array('java script', 'vb script');
  2045.  
  2046. return preg_replace($preg_find, $preg_replace, htmlspecialchars(trim($var)));
  2047. }
  2048.  
  2049. /**
  2050. * Reverses the effects of magic_quotes on an entire array of variables
  2051. *
  2052. * @param array The array on which we want to work
  2053. */
  2054. function stripslashes_deep(&$value, $depth = 0)
  2055. {
  2056. if (is_array($value))
  2057. {
  2058. foreach ($value AS $key => $val)
  2059. {
  2060. if (is_string($val))
  2061. {
  2062. $value["$key"] = stripslashes($val);
  2063. }
  2064. else if (is_array($val) AND $depth < 10)
  2065. {
  2066. $this->stripslashes_deep($value["$key"], $depth + 1);
  2067. }
  2068. }
  2069. }
  2070. }
  2071.  
  2072. /**
  2073. * Turns $_POST['t'] into $_POST['threadid'] etc.
  2074. *
  2075. * @param array The name of the array
  2076. */
  2077. function convert_shortvars(&$array)
  2078. {
  2079. // extract long variable names from short variable names
  2080. foreach ($this->shortvars AS $shortname => $longname)
  2081. {
  2082. if (isset($array["$shortname"]) AND !isset($array["$longname"]))
  2083. {
  2084. $array["$longname"] =& $array["$shortname"];
  2085. $GLOBALS['_REQUEST']["$longname"] =& $array["$shortname"];
  2086. }
  2087. }
  2088. }
  2089.  
  2090. /**
  2091. * Strips out the s=gobbledygook& rubbish from URLs
  2092. *
  2093. * @param string The URL string from which to remove the session stuff
  2094. *
  2095. * @return string
  2096. */
  2097. function strip_sessionhash($string)
  2098. {
  2099. $string = preg_replace('/(s|sessionhash)=[a-z0-9]{32}?&?/', '', $string);
  2100. return $string;
  2101. }
  2102.  
  2103. /**
  2104. * Fetches the 'scriptpath' variable - ie: the URI of the current page
  2105. *
  2106. * @return string
  2107. */
  2108. function fetch_scriptpath()
  2109. {
  2110. if ($this->registry->scriptpath != '')
  2111. {
  2112. return $this->registry->scriptpath;
  2113. }
  2114. else
  2115. {
  2116. if ($_SERVER['REQUEST_URI'] OR $_ENV['REQUEST_URI'])
  2117. {
  2118. $scriptpath = $_SERVER['REQUEST_URI'] ? $_SERVER['REQUEST_URI'] : $_ENV['REQUEST_URI'];
  2119. }
  2120. else
  2121. {
  2122. if ($_SERVER['PATH_INFO'] OR $_ENV['PATH_INFO'])
  2123. {
  2124. $scriptpath = $_SERVER['PATH_INFO'] ? $_SERVER['PATH_INFO'] : $_ENV['PATH_INFO'];
  2125. }
  2126. else if ($_SERVER['REDIRECT_URL'] OR $_ENV['REDIRECT_URL'])
  2127. {
  2128. $scriptpath = $_SERVER['REDIRECT_URL'] ? $_SERVER['REDIRECT_URL'] : $_ENV['REDIRECT_URL'];
  2129. }
  2130. else
  2131. {
  2132. $scriptpath = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_ENV['PHP_SELF'];
  2133. }
  2134.  
  2135. if ($_SERVER['QUERY_STRING'] OR $_ENV['QUERY_STRING'])
  2136. {
  2137. $scriptpath .= '?' . ($_SERVER['QUERY_STRING'] ? $_SERVER['QUERY_STRING'] : $_ENV['QUERY_STRING']);
  2138. }
  2139. }
  2140.  
  2141. // in the future we should set $registry->script here too
  2142. $quest_pos = strpos($scriptpath, '?');
  2143. if ($quest_pos !== false)
  2144. {
  2145. $script = urldecode(substr($scriptpath, 0, $quest_pos));
  2146. $scriptpath = $script . substr($scriptpath, $quest_pos);
  2147. }
  2148. else
  2149. {
  2150. $scriptpath = urldecode($scriptpath);
  2151. }
  2152.  
  2153. // store a version that includes the sessionhash
  2154. $this->registry->reloadurl = $this->xss_clean($scriptpath);
  2155.  
  2156. $scriptpath = $this->strip_sessionhash($scriptpath);
  2157. $scriptpath = $this->xss_clean($scriptpath);
  2158. $this->registry->scriptpath = $scriptpath;
  2159.  
  2160. return $scriptpath;
  2161. }
  2162. }
  2163.  
  2164. /**
  2165. * Fetches the 'wolpath' variable - ie: the same as 'scriptpath' but with a handler for the POST request method
  2166. *
  2167. * @return string
  2168. */
  2169. function fetch_wolpath()
  2170. {
  2171. $wolpath = $this->fetch_scriptpath();
  2172.  
  2173. if ($_SERVER['REQUEST_METHOD'] == 'POST')
  2174. {
  2175. // Tag the variables back on to the filename if we are coming from POST so that WOL can access them.
  2176. $tackon = '';
  2177.  
  2178. if (is_array($_POST))
  2179. {
  2180. foreach ($_POST AS $varname => $value)
  2181. {
  2182. switch ($varname)
  2183. {
  2184. case 'forumid':
  2185. case 'threadid':
  2186. case 'postid':
  2187. case 'userid':
  2188. case 'eventid':
  2189. case 'calendarid':
  2190. case 'do':
  2191. case 'method': // postings.php
  2192. case 'dowhat': // private.php
  2193. {
  2194. $tackon .= ($tackon == '' ? '' : '&amp;') . $varname . '=' . $value;
  2195. break;
  2196. }
  2197. }
  2198. }
  2199. }
  2200. if ($tackon != '')
  2201. {
  2202. $wolpath .= (strpos($wolpath, '?') !== false ? '&amp;' : '?') . "$tackon";
  2203. }
  2204. }
  2205.  
  2206. return $wolpath;
  2207. }
  2208.  
  2209. /**
  2210. * Fetches the 'url' variable - usually the URL of the previous page in the history
  2211. *
  2212. * @return string
  2213. */
  2214. function fetch_url()
  2215. {
  2216. $temp_url = $_REQUEST['url'];
  2217.  
  2218. $scriptpath = $this->fetch_scriptpath();
  2219.  
  2220. if (empty($temp_url))
  2221. {
  2222. $url = $_SERVER['HTTP_REFERER'];
  2223. }
  2224. else
  2225. {
  2226. if ($temp_url == $_SERVER['HTTP_REFERER'])
  2227. {
  2228. $url = 'index.php';
  2229. }
  2230. else
  2231. {
  2232. $url = $temp_url;
  2233. }
  2234. }
  2235.  
  2236. if ($url == $scriptpath OR empty($url))
  2237. {
  2238. $url = 'index.php';
  2239. }
  2240.  
  2241. // if $url is set to forum home page, check it against options
  2242. if ($url == 'index.php' AND $this->registry->options['forumhome'] != 'index')
  2243. {
  2244. $url = $this->registry->options['forumhome'] . '.php';
  2245. }
  2246.  
  2247. $url = $this->xss_clean($url);
  2248.  
  2249. return $url;
  2250. }
  2251.  
  2252. /**
  2253. * Fetches the IP address of the current visitor
  2254. *
  2255. * @return string
  2256. */
  2257. function fetch_ip()
  2258. {
  2259. return $_SERVER['REMOTE_ADDR'];
  2260. }
  2261.  
  2262. /**
  2263. * Fetches an alternate IP address of the current visitor, attempting to detect proxies etc.
  2264. *
  2265. * @return string
  2266. */
  2267. function fetch_alt_ip()
  2268. {
  2269. $alt_ip = $_SERVER['REMOTE_ADDR'];
  2270.  
  2271. if (isset($_SERVER['HTTP_CLIENT_IP']))
  2272. {
  2273. $alt_ip = $_SERVER['HTTP_CLIENT_IP'];
  2274. }
  2275. else if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) AND preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches))
  2276. {
  2277. // try to avoid using an internal IP address, its probably a proxy
  2278. $ranges = array(
  2279. '10.0.0.0/8' => array(ip2long('10.0.0.0'), ip2long('10.255.255.255')),
  2280. '127.0.0.0/8' => array(ip2long('127.0.0.0'), ip2long('127.255.255.255')),
  2281. '169.254.0.0/16' => array(ip2long('169.254.0.0'), ip2long('169.254.255.255')),
  2282. '172.16.0.0/12' => array(ip2long('172.16.0.0'), ip2long('172.31.255.255')),
  2283. '192.168.0.0/16' => array(ip2long('192.168.0.0'), ip2long('192.168.255.255')),
  2284. );
  2285. foreach ($matches[0] AS $ip)
  2286. {
  2287. $ip_long = ip2long($ip);
  2288. if ($ip_long === false OR $ip_long == -1)
  2289. {
  2290. continue;
  2291. }
  2292.  
  2293. $private_ip = false;
  2294. foreach ($ranges AS $range)
  2295. {
  2296. if ($ip_long >= $range[0] AND $ip_long <= $range[1])
  2297. {
  2298. $private_ip = true;
  2299. break;
  2300. }
  2301. }
  2302.  
  2303. if (!$private_ip)
  2304. {
  2305. $alt_ip = $ip;
  2306. break;
  2307. }
  2308. }
  2309. }
  2310. else if (isset($_SERVER['HTTP_FROM']))
  2311. {
  2312. $alt_ip = $_SERVER['HTTP_FROM'];
  2313. }
  2314.  
  2315. return $alt_ip;
  2316. }
  2317. }
  2318.  
  2319. // #############################################################################
  2320. // data registry class
  2321.  
  2322. /**
  2323. * Class to store commonly-used variables
  2324. *
  2325. * @package vBulletin
  2326. * @version $Revision: 43870 $
  2327. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  2328. */
  2329. class vB_Registry
  2330. {
  2331. // general objects
  2332. /**
  2333. * Datastore object.
  2334. *
  2335. * @var vB_Datastore
  2336. */
  2337. var $datastore;
  2338.  
  2339. /**
  2340. * Input cleaner object.
  2341. *
  2342. * @var vB_Input_Cleaner
  2343. */
  2344. var $input;
  2345.  
  2346. /**
  2347. * Database object.
  2348. *
  2349. * @var vB_Database
  2350. */
  2351. var $db;
  2352.  
  2353. // user/session related
  2354. /**
  2355. * Array of info about the current browsing user. In the case of a registered
  2356. * user, this will be results of fetch_userinfo(). A guest will have slightly
  2357. * different entries.
  2358. *
  2359. * @var array
  2360. */
  2361. var $userinfo;
  2362.  
  2363. /**
  2364. * Session object.
  2365. *
  2366. * @var vB_Session
  2367. */
  2368. var $session;
  2369.  
  2370. /**
  2371. * Array of do actions that are exempt from checks
  2372. *
  2373. * @var array
  2374. */
  2375. var $csrf_skip_list = array();
  2376.  
  2377. // configuration
  2378. /**
  2379. * Array of data from config.php.
  2380. *
  2381. * @var array
  2382. */
  2383. var $config;
  2384.  
  2385. // GPC input
  2386. /**
  2387. * Array of data that has been cleaned by the input cleaner.
  2388. *
  2389. * @var array
  2390. */
  2391. var $GPC = array();
  2392.  
  2393. /**
  2394. * Array of booleans. When cleaning a variable, you often lose the ability
  2395. * to determine if it was specified in the user's input. Entries in this
  2396. * array are true if the variable existed before cleaning.
  2397. *
  2398. * @var array
  2399. */
  2400. var $GPC_exists = array();
  2401.  
  2402. /**
  2403. * The size of the super global arrays.
  2404. *
  2405. * @var array
  2406. */
  2407. var $superglobal_size = array();
  2408.  
  2409. // single variables
  2410. /**
  2411. * IP Address of the current browsing user.
  2412. *
  2413. * @var string
  2414. */
  2415. var $ipaddress;
  2416.  
  2417. /**
  2418. * Alternate IP for the browsing user. This attempts to use various HTTP headers
  2419. * to find the real IP of a user that may be behind a proxy.
  2420. *
  2421. * @var string
  2422. */
  2423. var $alt_ip;
  2424.  
  2425. /**
  2426. * The URL of the currently browsed page.
  2427. *
  2428. * @var string
  2429. */
  2430. var $scriptpath;
  2431.  
  2432. /**
  2433. * Similar to the URL of the current page, but expands some items and includes
  2434. * data submitted via POST. Used for Who's Online purposes.
  2435. *
  2436. * @var string
  2437. */
  2438. var $wolpath;
  2439.  
  2440. /**
  2441. * The URL of the current page, without anything after the '?'.
  2442. *
  2443. * @var string
  2444. */
  2445. var $script;
  2446.  
  2447. /**
  2448. * Generally the URL of the referring page if there is one, though it is often
  2449. * set in various places of the code. Used to determine the page to redirect
  2450. * to, if necessary.
  2451. *
  2452. * @var string
  2453. */
  2454. var $url;
  2455.  
  2456. // usergroup permission bitfields
  2457. /**#@+
  2458. * Bitfield arrays for usergroup permissions.
  2459. *
  2460. * @var array
  2461. */
  2462. var $bf_ugp;
  2463. // $bf_ugp_x is a reference to $bf_ugp['x']
  2464. var $bf_ugp_adminpermissions;
  2465. var $bf_ugp_calendarpermissions;
  2466. var $bf_ugp_forumpermissions;
  2467. var $bf_ugp_genericoptions;
  2468. var $bf_ugp_genericpermissions;
  2469. var $bf_ugp_pmpermissions;
  2470. var $bf_ugp_wolpermissions;
  2471. var $bf_ugp_visitormessagepermissions;
  2472. /**#@-*/
  2473.  
  2474. // misc bitfield arrays
  2475. /**#@+
  2476. * Bitfield arrays for miscellaneous permissions and options.
  2477. *
  2478. * @var array
  2479. */
  2480. var $bf_misc;
  2481. // $bf_misc_x is a reference to $bf_misc['x']
  2482. var $bf_misc_calmoderatorpermissions;
  2483. var $bf_misc_forumoptions;
  2484. var $bf_misc_intperms;
  2485. var $bf_misc_languageoptions;
  2486. var $bf_misc_moderatorpermissions;
  2487. var $bf_misc_useroptions;
  2488. var $bf_misc_hvcheck;
  2489. /**#@-*/
  2490.  
  2491. /**#@+
  2492. * Results for specific entries in the datastore.
  2493. *
  2494. * @var mixed Mixed, though mostly arrays.
  2495. */
  2496. var $options = null;
  2497. var $attachmentcache = null;
  2498. var $avatarcache = null;
  2499. var $birthdaycache = null;
  2500. var $eventcache = null;
  2501. var $forumcache = null;
  2502. var $iconcache = null;
  2503. var $markupcache = null;
  2504. var $stylecache = null;
  2505. var $languagecache = null;
  2506. var $smiliecache = null;
  2507. var $usergroupcache = null;
  2508. var $bbcodecache = null;
  2509. var $socialsitecache = null;
  2510. var $cron = null;
  2511. var $mailqueue = null;
  2512. var $banemail = null;
  2513. var $maxloggedin = null;
  2514. var $pluginlist = null;
  2515. var $products = null;
  2516. var $ranks = null;
  2517. var $statement = null;
  2518. var $userstats = null;
  2519. var $wol_spiders = null;
  2520. var $loadcache = null;
  2521. var $noticecache = null;
  2522. var $prefixcache = null;
  2523. /**#@-*/
  2524.  
  2525. /**#@+
  2526. * Miscellaneous variables
  2527. *
  2528. * @var mixed
  2529. */
  2530. var $bbcode_style = array('code' => -1, 'html' => -1, 'php' => -1, 'quote' => -1);
  2531. var $templatecache = array();
  2532. var $iforumcache = array();
  2533. var $versionnumber;
  2534. var $nozip;
  2535. var $debug;
  2536. var $noheader;
  2537. var $shutdown;
  2538. /**#@-*/
  2539.  
  2540. /**
  2541. * Constructor - initializes the nozip system,
  2542. * and calls and instance of the vB_Input_Cleaner class
  2543. */
  2544. function vB_Registry()
  2545. {
  2546. // variable to allow bypassing of gzip compression
  2547. $this->nozip = defined('NOZIP') ? true : (@ini_get('zlib.output_compression') ? true : false);
  2548. // variable that controls HTTP header output
  2549. $this->noheader = defined('NOHEADER') ? true : false;
  2550.  
  2551. // initialize the input handler
  2552. $this->input =& new vB_Input_Cleaner($this);
  2553.  
  2554. // initialize the shutdown handler
  2555. $this->shutdown = vB_Shutdown::init();
  2556.  
  2557. $this->csrf_skip_list = (defined('CSRF_SKIP_LIST') ? explode(',', CSRF_SKIP_LIST) : array());
  2558. }
  2559.  
  2560. /**
  2561. * Fetches database/system configuration
  2562. */
  2563. function fetch_config()
  2564. {
  2565. // parse the config file
  2566. $config = array();
  2567. include(CWD . '/includes/config.php');
  2568.  
  2569. if (sizeof($config) == 0)
  2570. {
  2571. if (file_exists(CWD. '/includes/config.php'))
  2572. {
  2573. // config.php exists, but does not define $config
  2574. 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.');
  2575. }
  2576. else
  2577. {
  2578. 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');
  2579. }
  2580. }
  2581.  
  2582. $this->config =& $config;
  2583. // if a configuration exists for this exact HTTP host, use it
  2584. if (isset($this->config["$_SERVER[HTTP_HOST]"]))
  2585. {
  2586. $this->config['MasterServer'] = $this->config["$_SERVER[HTTP_HOST]"];
  2587. }
  2588.  
  2589. // define table and cookie prefix constants
  2590. define('TABLE_PREFIX', trim($this->config['Database']['tableprefix']));
  2591. define('COOKIE_PREFIX', (empty($this->config['Misc']['cookieprefix']) ? 'bb' : $this->config['Misc']['cookieprefix']));
  2592.  
  2593. // set debug mode
  2594. $this->debug = !empty($this->config['Misc']['debug']);
  2595. define('DEBUG', $this->debug);
  2596. }
  2597.  
  2598. /**
  2599. * Takes the contents of an array and recursively uses each title/data
  2600. * pair to create a new defined constant.
  2601. */
  2602. function array_define($array)
  2603. {
  2604. foreach ($array AS $title => $data)
  2605. {
  2606. if (is_array($data))
  2607. {
  2608. vB_Registry::array_define($data);
  2609. }
  2610. else
  2611. {
  2612. define(strtoupper($title), $data);
  2613. }
  2614. }
  2615. }
  2616. }
  2617.  
  2618. // #############################################################################
  2619. // session management class
  2620.  
  2621. /**
  2622. * Class to handle sessions
  2623. *
  2624. * Creates, updates, and validates sessions; retrieves user info of browsing user
  2625. *
  2626. * @package vBulletin
  2627. * @version $Revision: 43870 $
  2628. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  2629. */
  2630. class vB_Session
  2631. {
  2632. /**
  2633. * The individual session variables. Equivalent to $session from the past.
  2634. *
  2635. * @var array
  2636. */
  2637. var $vars = array();
  2638.  
  2639. /**
  2640. * A list of variables in the $vars member that are in the database. Includes their types.
  2641. *
  2642. * @var array
  2643. */
  2644. var $db_fields = array(
  2645. 'sessionhash' => TYPE_STR,
  2646. 'userid' => TYPE_INT,
  2647. 'host' => TYPE_STR,
  2648. 'idhash' => TYPE_STR,
  2649. 'lastactivity' => TYPE_INT,
  2650. 'location' => TYPE_STR,
  2651. 'styleid' => TYPE_INT,
  2652. 'languageid' => TYPE_INT,
  2653. 'loggedin' => TYPE_INT,
  2654. 'inforum' => TYPE_INT,
  2655. 'inthread' => TYPE_INT,
  2656. 'incalendar' => TYPE_INT,
  2657. 'badlocation' => TYPE_INT,
  2658. 'useragent' => TYPE_STR,
  2659. 'bypass' => TYPE_INT,
  2660. 'profileupdate' => TYPE_INT,
  2661. );
  2662.  
  2663. /**
  2664. * An array of changes. Used to prevent superfluous updates from being made.
  2665. *
  2666. * @var array
  2667. */
  2668. var $changes = array();
  2669.  
  2670. /**
  2671. * Whether the session was created or existed previously
  2672. *
  2673. * @var bool
  2674. */
  2675. var $created = false;
  2676.  
  2677. /**
  2678. * Reference to a vB_Registry object that keeps various data we need.
  2679. *
  2680. * @var vB_Registry
  2681. */
  2682. var $registry = null;
  2683.  
  2684. /**
  2685. * Information about the user that this session belongs to.
  2686. *
  2687. * @var array
  2688. */
  2689. var $userinfo = null;
  2690.  
  2691. /**
  2692. * Is the sessionhash to be passed through URLs?
  2693. *
  2694. * @var boolean
  2695. */
  2696. var $visible = true;
  2697.  
  2698. /**
  2699. * Constructor. Attempts to grab a session that matches parameters, but will create one if it can't.
  2700. *
  2701. * @param vB_Registry Reference to a registry object
  2702. * @param string Previously specified sessionhash
  2703. * @param integer User ID (passed in through a cookie)
  2704. * @param string Password, must arrive in cookie format: md5(md5(md5(password) . salt) . 'abcd1234')
  2705. * @param integer Style ID for this session
  2706. * @param integer Language ID for this session
  2707. */
  2708. function vB_Session(&$registry, $sessionhash = '', $userid = 0, $password = '', $styleid = 0, $languageid = 0)
  2709. {
  2710. $userid = intval($userid);
  2711. $styleid = intval($styleid);
  2712. $languageid = intval($languageid);
  2713.  
  2714. $this->registry =& $registry;
  2715. $db =& $this->registry->db;
  2716. $gotsession = false;
  2717.  
  2718. if (!defined('SESSION_IDHASH'))
  2719. {
  2720. define('SESSION_IDHASH', md5($_SERVER['HTTP_USER_AGENT'] . $this->fetch_substr_ip($registry->alt_ip))); // this should *never* change during a session
  2721. }
  2722.  
  2723. // sessionhash specified, so see if it already exists
  2724. if ($sessionhash AND !defined('SKIP_SESSIONCREATE'))
  2725. {
  2726. if ($session = $db->query_first_slave("
  2727. SELECT *
  2728. FROM " . TABLE_PREFIX . "session
  2729. WHERE sessionhash = '" . $db->escape_string($sessionhash) . "'
  2730. AND lastactivity > " . (TIMENOW - $registry->options['cookietimeout']) . "
  2731. AND idhash = '" . $this->registry->db->escape_string(SESSION_IDHASH) . "'
  2732. ") AND $this->fetch_substr_ip($session['host']) == $this->fetch_substr_ip(SESSION_HOST))
  2733. {
  2734. $gotsession = true;
  2735. $this->vars =& $session;
  2736. $this->created = false;
  2737.  
  2738. // found a session - get the userinfo
  2739. if ($session['userid'] != 0)
  2740. {
  2741. $useroptions = (defined('IN_CONTROL_PANEL') ? 16 : 0) + (defined('AVATAR_ON_NAVBAR') ? 2 : 0);
  2742. $userinfo = fetch_userinfo($session['userid'], $useroptions, (!empty($languageid) ? $languageid : $session['languageid']));
  2743. $this->userinfo =& $userinfo;
  2744. }
  2745. }
  2746. }
  2747.  
  2748. // or maybe we can use a cookie..
  2749. if (($gotsession == false OR empty($session['userid'])) AND $userid AND $password AND !defined('SKIP_SESSIONCREATE'))
  2750. {
  2751. $useroptions = (defined('IN_CONTROL_PANEL') ? FETCH_USERINFO_ADMIN : 0) + (defined('AVATAR_ON_NAVBAR') ? FETCH_USERINFO_AVATAR : 0);
  2752. $userinfo = fetch_userinfo($userid, $useroptions, $languageid);
  2753.  
  2754. if (md5($userinfo['password'] . COOKIE_SALT) == $password)
  2755. {
  2756. $gotsession = true;
  2757.  
  2758. // combination is valid
  2759. if (!empty($session['sessionhash']))
  2760. {
  2761. // old session still exists; kill it
  2762. $db->shutdown_query("
  2763. DELETE FROM " . TABLE_PREFIX . "session
  2764. WHERE sessionhash = '" . $this->registry->db->escape_string($session['sessionhash']). "'
  2765. ");
  2766. }
  2767.  
  2768. $this->vars = $this->fetch_session($userinfo['userid']);
  2769. $this->created = true;
  2770.  
  2771. $this->userinfo =& $userinfo;
  2772. }
  2773. }
  2774.  
  2775. // at this point, we're a guest, so lets try to *find* a session
  2776. // you can prevent this check from being run by passing in a userid with no password
  2777. if ($gotsession == false AND $userid == 0 AND !defined('SKIP_SESSIONCREATE'))
  2778. {
  2779. if ($session = $db->query_first_slave("
  2780. SELECT *
  2781. FROM " . TABLE_PREFIX . "session
  2782. WHERE userid = 0
  2783. AND host = '" . $this->registry->db->escape_string(SESSION_HOST) . "'
  2784. AND idhash = '" . $this->registry->db->escape_string(SESSION_IDHASH) . "'
  2785. LIMIT 1
  2786. "))
  2787. {
  2788. $gotsession = true;
  2789.  
  2790. $this->vars =& $session;
  2791. $this->created = false;
  2792. }
  2793. }
  2794.  
  2795. // well, nothing worked, time to create a new session
  2796. if ($gotsession == false)
  2797. {
  2798. $gotsession = true;
  2799.  
  2800. $this->vars = $this->fetch_session(0);
  2801. $this->created = true;
  2802. }
  2803.  
  2804. $this->vars['dbsessionhash'] = $this->vars['sessionhash'];
  2805.  
  2806. $this->set('styleid', $styleid);
  2807. $this->set('languageid', $languageid);
  2808. if ($this->created == false)
  2809. {
  2810. $this->set('useragent', USER_AGENT);
  2811. $this->set('lastactivity', TIMENOW);
  2812. if (!defined('LOCATION_BYPASS'))
  2813. {
  2814. $this->set('location', WOLPATH);
  2815. }
  2816. $this->set('bypass', SESSION_BYPASS);
  2817. }
  2818. }
  2819.  
  2820. /**
  2821. * Saves the session into the database by inserting it or updating an existing one.
  2822. */
  2823. function save()
  2824. {
  2825. if (defined('SKIP_SESSIONCREATE'))
  2826. {
  2827. return;
  2828. }
  2829.  
  2830. $cleaned = $this->build_query_array();
  2831.  
  2832. // since the sessionhash can be blanked out, lets make sure we pull from "dbsessionhash"
  2833. $cleaned['sessionhash'] = "'" . $this->registry->db->escape_string($this->vars['dbsessionhash']) . "'";
  2834.  
  2835. if ($this->created == true)
  2836. {
  2837. /*insert query*/
  2838. $this->registry->db->query_write("
  2839. INSERT IGNORE INTO " . TABLE_PREFIX . "session
  2840. (" . implode(', ', array_keys($cleaned)) . ")
  2841. VALUES
  2842. (" . implode(', ', $cleaned) . ")
  2843. ");
  2844. }
  2845. else
  2846. {
  2847. // update query
  2848.  
  2849. unset($this->changes['sessionhash']); // the sessionhash is not updateable
  2850. $update = array();
  2851. foreach ($cleaned AS $key => $value)
  2852. {
  2853. if (!empty($this->changes["$key"]))
  2854. {
  2855. $update[] = "$key = $value";
  2856. }
  2857. }
  2858.  
  2859. if (sizeof($update) > 0)
  2860. {
  2861. // note that $cleaned['sessionhash'] has been escaped as necessary above!
  2862. $this->registry->db->query_write("
  2863. UPDATE " . TABLE_PREFIX . "session
  2864. SET " . implode(', ', $update) . "
  2865. WHERE sessionhash = $cleaned[sessionhash]
  2866. ");
  2867. }
  2868. }
  2869.  
  2870. $this->changes = array();
  2871. }
  2872.  
  2873. /**
  2874. * Builds an array that can be used to build a query to insert/update the session
  2875. *
  2876. * @return array Array of column name => prepared value
  2877. */
  2878. function build_query_array()
  2879. {
  2880. $return = array();
  2881. foreach ($this->db_fields AS $fieldname => $cleantype)
  2882. {
  2883. switch ($cleantype)
  2884. {
  2885. case TYPE_INT:
  2886. $cleaned = intval($this->vars["$fieldname"]);
  2887. break;
  2888. case TYPE_STR:
  2889. default:
  2890. $cleaned = "'" . $this->registry->db->escape_string($this->vars["$fieldname"]) . "'";
  2891. }
  2892. $return["$fieldname"] = $cleaned;
  2893. }
  2894.  
  2895. return $return;
  2896. }
  2897.  
  2898. /**
  2899. * Sets a session variable and updates the change list.
  2900. *
  2901. * @param string Name of session variable to update
  2902. * @param string Value to update it with
  2903. */
  2904. function set($key, $value)
  2905. {
  2906. if (!isset($this->vars["$key"]) OR $this->vars["$key"] != $value)
  2907. {
  2908. $this->vars["$key"] = $value;
  2909. $this->changes["$key"] = true;
  2910. }
  2911. }
  2912.  
  2913. /**
  2914. * Sets the session visibility (whether session info shows up in a URL). Updates are put in the $vars member.
  2915. *
  2916. * @param bool Whether the session elements should be visible.
  2917. */
  2918. function set_session_visibility($invisible)
  2919. {
  2920. $this->visible = !$invisible;
  2921.  
  2922. if ($invisible)
  2923. {
  2924. $this->vars['sessionhash'] = '';
  2925. $this->vars['sessionurl'] = '';
  2926. $this->vars['sessionurl_q'] = '';
  2927. $this->vars['sessionurl_js'] = '';
  2928. }
  2929. else
  2930. {
  2931. $this->vars['sessionurl'] = 's=' . $this->vars['dbsessionhash'] . '&amp;';
  2932. $this->vars['sessionurl_q'] = '?s=' . $this->vars['dbsessionhash'];
  2933. $this->vars['sessionurl_js'] = 's=' . $this->vars['dbsessionhash'] . '&';
  2934. }
  2935. }
  2936.  
  2937. /**
  2938. * Fetches a valid sessionhash value, not necessarily the one tied to this session.
  2939. *
  2940. * @return string 32-character sessionhash
  2941. */
  2942. function fetch_sessionhash()
  2943. {
  2944. return md5(uniqid(microtime(), true));
  2945. }
  2946.  
  2947. /**
  2948. * Returns the IP address with the specified number of octets removed
  2949. *
  2950. * @param string IP address
  2951. *
  2952. * @return string truncated IP address
  2953. */
  2954. function fetch_substr_ip($ip, $length = null)
  2955. {
  2956. if ($length === null OR $length > 3)
  2957. {
  2958. $length = $this->registry->options['ipcheck'];
  2959. }
  2960. return implode('.', array_slice(explode('.', $ip), 0, 4 - $length));
  2961. }
  2962.  
  2963. /**
  2964. * Fetches a default session. Used when creating a new session.
  2965. *
  2966. * @param integer User ID the session should be for
  2967. *
  2968. * @return array Array of session variables
  2969. */
  2970. function fetch_session($userid = 0)
  2971. {
  2972. $sessionhash = $this->fetch_sessionhash();
  2973. if (!defined('SKIP_SESSIONCREATE'))
  2974. {
  2975. vbsetcookie('sessionhash', $sessionhash, false, false, true);
  2976. }
  2977.  
  2978. return array(
  2979. 'sessionhash' => $sessionhash,
  2980. 'dbsessionhash' => $sessionhash,
  2981. 'userid' => intval($userid),
  2982. 'host' => SESSION_HOST,
  2983. 'idhash' => SESSION_IDHASH,
  2984. 'lastactivity' => TIMENOW,
  2985. 'location' => defined('LOCATION_BYPASS') ? '' : WOLPATH,
  2986. 'styleid' => 0,
  2987. 'languageid' => 0,
  2988. 'loggedin' => intval($userid) ? 1 : 0,
  2989. 'inforum' => 0,
  2990. 'inthread' => 0,
  2991. 'incalendar' => 0,
  2992. 'badlocation' => 0,
  2993. 'profileupdate' => 0,
  2994. 'useragent' => USER_AGENT,
  2995. 'bypass' => SESSION_BYPASS
  2996. );
  2997.  
  2998. }
  2999.  
  3000. /**
  3001. * Returns appropriate user info for the owner of this session.
  3002. *
  3003. * @return array Array of user information.
  3004. */
  3005. function &fetch_userinfo()
  3006. {
  3007. if ($this->userinfo)
  3008. {
  3009. // we already calculated this
  3010. return $this->userinfo;
  3011. }
  3012. else if ($this->vars['userid'] AND !defined('SKIP_USERINFO'))
  3013. {
  3014. // user is logged in
  3015. $useroptions = (defined('IN_CONTROL_PANEL') ? FETCH_USERINFO_ADMIN : 0) + (defined('AVATAR_ON_NAVBAR') ? FETCH_USERINFO_AVATAR : 0);
  3016. $this->userinfo = fetch_userinfo($this->vars['userid'], $useroptions, $this->vars['languageid']);
  3017. return $this->userinfo;
  3018. }
  3019. else
  3020. {
  3021. // guest setup
  3022. $this->userinfo = array(
  3023. 'userid' => 0,
  3024. 'usergroupid' => 1,
  3025. 'username' => (!empty($_REQUEST['username']) ? htmlspecialchars_uni($_REQUEST['username']) : ''),
  3026. 'password' => '',
  3027. 'email' => '',
  3028. 'styleid' => $this->vars['styleid'],
  3029. 'languageid' => $this->vars['languageid'],
  3030. 'lastactivity' => $this->vars['lastactivity'],
  3031. 'daysprune' => 0,
  3032. 'timezoneoffset' => $this->registry->options['timeoffset'],
  3033. 'dstonoff' => $this->registry->options['dstonoff'],
  3034. 'showsignatures' => 1,
  3035. 'showavatars' => 1,
  3036. 'showimages' => 1,
  3037. 'showusercss' => 1,
  3038. 'dstauto' => 0,
  3039. 'maxposts' => -1,
  3040. 'startofweek' => 1,
  3041. 'threadedmode' => $this->registry->options['threadedmode'],
  3042. 'securitytoken' => 'guest',
  3043. 'securitytoken_raw' => 'guest'
  3044. );
  3045.  
  3046. $this->userinfo['options'] =
  3047. $this->registry->bf_misc_useroptions['showsignatures'] | $this->registry->bf_misc_useroptions['showavatars'] |
  3048. $this->registry->bf_misc_useroptions['showimages'] | $this->registry->bf_misc_useroptions['dstauto'] |
  3049. $this->registry->bf_misc_useroptions['showusercss'];
  3050.  
  3051. if (!defined('SKIP_USERINFO'))
  3052. {
  3053. // get default language
  3054. $phraseinfo = $this->registry->db->query_first_slave("
  3055. SELECT languageid" . fetch_language_fields_sql(0) . "
  3056. FROM " . TABLE_PREFIX . "language
  3057. WHERE languageid = " . (!empty($this->vars['languageid']) ? $this->vars['languageid'] : intval($this->registry->options['languageid'])) . "
  3058. ");
  3059. if (empty($phraseinfo))
  3060. { // can't phrase this since we can't find the language
  3061. trigger_error('The requested language does not exist, reset via tools.php.', E_USER_ERROR);
  3062. }
  3063. foreach($phraseinfo AS $_arrykey => $_arryval)
  3064. {
  3065. $this->userinfo["$_arrykey"] = $_arryval;
  3066. }
  3067. unset($phraseinfo);
  3068. }
  3069.  
  3070. return $this->userinfo;
  3071. }
  3072. }
  3073.  
  3074. /**
  3075. * Updates the last visit and last activity times for guests and registered users (differently).
  3076. * Last visit is set to the last activity time (before it's updated) only when a certain
  3077. * time has lapsed. Last activity is always set to the specified time.
  3078. *
  3079. * @param integer Time stamp for last visit time (guest only)
  3080. * @param integer Time stamp for last activity time (guest only)
  3081. */
  3082. function do_lastvisit_update($lastvisit = 0, $lastactivity = 0)
  3083. {
  3084. // update last visit/activity stuff
  3085. if ($this->vars['userid'] == 0)
  3086. {
  3087. // guest -- emulate last visit/activity for registered users by cookies
  3088. if ($lastvisit)
  3089. {
  3090. // we've been here before
  3091. $this->userinfo['lastvisit'] = intval($lastvisit);
  3092. $this->userinfo['lastactivity'] = ($lastvisit ? intval($lastvisit) : TIMENOW);
  3093.  
  3094. // here's the emulation
  3095. if (TIMENOW - $this->userinfo['lastactivity'] > $this->registry->options['cookietimeout'])
  3096. {
  3097. $this->userinfo['lastvisit'] = $this->userinfo['lastactivity'];
  3098.  
  3099. vbsetcookie('lastvisit', $this->userinfo['lastactivity']);
  3100. }
  3101. }
  3102. else
  3103. {
  3104. // first visit!
  3105. $this->userinfo['lastactivity'] = TIMENOW;
  3106. $this->userinfo['lastvisit'] = TIMENOW;
  3107.  
  3108. vbsetcookie('lastvisit', TIMENOW);
  3109. }
  3110. vbsetcookie('lastactivity', $lastactivity);
  3111. }
  3112. else
  3113. {
  3114. // registered user
  3115. if (!SESSION_BYPASS)
  3116. {
  3117. if (TIMENOW - $this->userinfo['lastactivity'] > $this->registry->options['cookietimeout'])
  3118. {
  3119. // see if session has 'expired' and if new post indicators need resetting
  3120. $this->registry->db->shutdown_query("
  3121. UPDATE " . TABLE_PREFIX . "user
  3122. SET
  3123. lastvisit = lastactivity,
  3124. lastactivity = " . TIMENOW . "
  3125. WHERE userid = " . $this->userinfo['userid'] . "
  3126. ", 'lastvisit');
  3127.  
  3128. $this->userinfo['lastvisit'] = $this->userinfo['lastactivity'];
  3129. }
  3130. else
  3131. {
  3132. // if this line is removed (say to be replaced by a cron job, you will need to change all of the 'online'
  3133. // status indicators as they use $userinfo['lastactivity'] to determine if a user is online which relies
  3134. // on this to be updated in real time.
  3135. $this->registry->db->shutdown_query("
  3136. UPDATE " . TABLE_PREFIX . "user
  3137. SET lastactivity = " . TIMENOW . "
  3138. WHERE userid = " . $this->userinfo['userid'] . "
  3139. ", 'lastvisit');
  3140. }
  3141. }
  3142. }
  3143. }
  3144. }
  3145.  
  3146. /**
  3147. * Class to handle shutdown
  3148. *
  3149. * @package vBulletin
  3150. * @version $Revision: 43870 $
  3151. * @author Scott
  3152. * @date $Date: 2011-05-26 14:52:39 -0700 (Thu, 26 May 2011) $
  3153. */
  3154. class vB_Shutdown
  3155. {
  3156. var $shutdown = array();
  3157.  
  3158. /**
  3159. * Constructor. Empty.
  3160. */
  3161. function vB_Shutdown()
  3162. {
  3163. }
  3164.  
  3165. /**
  3166. * Singleton emulation - use this function to instantiate the class
  3167. *
  3168. * @return vB_Shutdown
  3169. */
  3170. function &init()
  3171. {
  3172. static $instance;
  3173.  
  3174. if (!$instance)
  3175. {
  3176. $instance = new vB_Shutdown();
  3177. // we register this but it might not be used
  3178. if (phpversion() < '5.0.5')
  3179. {
  3180. register_shutdown_function(array(&$instance, '__destruct'));
  3181. }
  3182. }
  3183.  
  3184. return $instance;
  3185. }
  3186.  
  3187. /**
  3188. * Add function to be executed at shutdown
  3189. *
  3190. * @param string Name of function to be executed on shutdown
  3191. */
  3192. function add($function)
  3193. {
  3194. $obj =& vB_Shutdown::init();
  3195. if (function_exists($function) AND !in_array($function, $obj->shutdown))
  3196. {
  3197. $obj->shutdown[] = $function;
  3198. }
  3199. }
  3200.  
  3201. // only called when an object is destroyed, so $this is appropriate
  3202. function __destruct()
  3203. {
  3204. if (!empty($this->shutdown))
  3205. {
  3206. foreach ($this->shutdown AS $key => $funcname)
  3207. {
  3208. $funcname();
  3209. unset($this->shutdown[$key]);
  3210. }
  3211. }
  3212. }
  3213.  
  3214. // called if unserialized
  3215. function __wakeup()
  3216. {
  3217. $this->shutdown = array();
  3218. }
  3219. }
  3220.  
  3221. // #############################################################################
  3222. // misc functions
  3223.  
  3224. // #############################################################################
  3225. /**
  3226. * Feeds database connection errors into the halt() method of the vB_Database class.
  3227. *
  3228. * @param integer Error number
  3229. * @param string PHP error text string
  3230. * @param strig File that contained the error
  3231. * @param integer Line in the file that contained the error
  3232. */
  3233. function catch_db_error($errno, $errstr, $errfile, $errline)
  3234. {
  3235. global $db;
  3236. static $failures;
  3237.  
  3238. if (strstr($errstr, 'Lost connection') AND $failures < 5)
  3239. {
  3240. $failures++;
  3241. return;
  3242. }
  3243.  
  3244. if (is_object($db))
  3245. {
  3246. $db->halt("$errstr\r\n$errfile on line $errline");
  3247. }
  3248. else
  3249. {
  3250. vb_error_handler($errno, $errstr, $errfile, $errline);
  3251. }
  3252. }
  3253.  
  3254. // #############################################################################
  3255. /**
  3256. * Removes the full path from being disclosed on any errors
  3257. *
  3258. * @param integer Error number
  3259. * @param string PHP error text string
  3260. * @param strig File that contained the error
  3261. * @param integer Line in the file that contained the error
  3262. */
  3263. function vb_error_handler($errno, $errstr, $errfile, $errline)
  3264. {
  3265. global $vbulletin;
  3266.  
  3267. switch ($errno)
  3268. {
  3269. case E_WARNING:
  3270. case E_USER_WARNING:
  3271. /* Don't log warnings due to to the false bug reports about valid warnings that we suppress, but still appear in the log
  3272. require_once(DIR . '/includes/functions_log_error.php');
  3273. $message = "Warning: $errstr in $errfile on line $errline";
  3274. log_vbulletin_error($message, 'php');
  3275. */
  3276.  
  3277. if (!error_reporting() OR !ini_get('display_errors'))
  3278. {
  3279. return;
  3280. }
  3281. $errfile = str_replace(DIR, '[path]', $errfile);
  3282. $errstr = str_replace(DIR, '[path]', $errstr);
  3283. echo "<br /><strong>Warning</strong>: $errstr in <strong>$errfile</strong> on line <strong>$errline</strong><br />";
  3284. break;
  3285.  
  3286. case E_USER_ERROR:
  3287. require_once(DIR . '/includes/functions_log_error.php');
  3288. $message = "Fatal error: $errstr in $errfile on line $errline";
  3289. log_vbulletin_error($message, 'php');
  3290.  
  3291. if (!headers_sent())
  3292. {
  3293. if (SAPI_NAME == 'cgi' OR SAPI_NAME == 'cgi-fcgi')
  3294. {
  3295. header('Status: 500 Internal Server Error');
  3296. }
  3297. else
  3298. {
  3299. header('HTTP/1.1 500 Internal Server Error');
  3300. }
  3301. }
  3302.  
  3303. if (error_reporting() OR ini_get('display_errors'))
  3304. {
  3305. $errfile = str_replace(DIR, '[path]', $errfile);
  3306. $errstr = str_replace(DIR, '[path]', $errstr);
  3307. echo "<br /><strong>Fatal error:</strong> $errstr in <strong>$errfile</strong> on line <strong>$errline</strong><br />";
  3308. if (function_exists('debug_print_backtrace') AND ($vbulletin->userinfo['usergroupid'] == 6 OR ($vbulletin->userinfo['permissions']['adminpermissions'] & $vbulletin->bf_ugp_adminpermissions)))
  3309. {
  3310. // This is needed so IE doesn't show the pretty error messages
  3311. echo str_repeat(' ', 512);
  3312. debug_print_backtrace();
  3313. }
  3314. }
  3315. exit;
  3316. break;
  3317. }
  3318. }
  3319.  
  3320. // #############################################################################
  3321. /**
  3322. * Unicode-safe version of htmlspecialchars()
  3323. *
  3324. * @param string Text to be made html-safe
  3325. *
  3326. * @return string
  3327. */
  3328. function htmlspecialchars_uni($text, $entities = true)
  3329. {
  3330. return str_replace(
  3331. // replace special html characters
  3332. array('<', '>', '"'),
  3333. array('&lt;', '&gt;', '&quot;'),
  3334. preg_replace(
  3335. // translates all non-unicode entities
  3336. '/&(?!' . ($entities ? '#[0-9]+|shy' : '(#[0-9]+|[a-z]+)') . ';)/si',
  3337. '&amp;',
  3338. $text
  3339. )
  3340. );
  3341. }
  3342.  
  3343. /*======================================================================*\
  3344. || ####################################################################
  3345. || # Downloaded: 04:08, Mon Jul 1st 2013
  3346. || # CVS: $RCSfile$ - $Revision: 43870 $
  3347. || ####################################################################
  3348. \*======================================================================*/
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement