QueryString.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525
  1. <?php
  2. /**
  3. * This file does a lot of important stuff. Mainly, this means it handles
  4. the query string, request variables, and session management.
  5. *
  6. * Simple Machines Forum (SMF)
  7. *
  8. * @package SMF
  9. * @author Simple Machines http://www.simplemachines.org
  10. * @copyright 2011 Simple Machines
  11. * @license http://www.simplemachines.org/about/smf/license.php BSD
  12. *
  13. * @version 2.0
  14. */
  15. if (!defined('SMF'))
  16. die('Hacking attempt...');
  17. /**
  18. * Clean the request variables - add html entities to GET and slashes if magic_quotes_gpc is Off.
  19. *
  20. * What it does:
  21. * - cleans the request variables (ENV, GET, POST, COOKIE, SERVER) and
  22. makes sure the query string was parsed correctly.
  23. - handles the URLs passed by the queryless URLs option.
  24. - makes sure, regardless of php.ini, everything has slashes.
  25. - sets up $board, $topic, and $scripturl and $_REQUEST['start'].
  26. - determines, or rather tries to determine, the client's IP.
  27. */
  28. function cleanRequest()
  29. {
  30. global $board, $topic, $boardurl, $scripturl, $modSettings, $smcFunc;
  31. // Makes it easier to refer to things this way.
  32. $scripturl = $boardurl . '/index.php';
  33. // What function to use to reverse magic quotes - if sybase is on we assume that the database sensibly has the right unescape function!
  34. $removeMagicQuoteFunction = @ini_get('magic_quotes_sybase') || strtolower(@ini_get('magic_quotes_sybase')) == 'on' ? 'unescapestring__recursive' : 'stripslashes__recursive';
  35. // Save some memory.. (since we don't use these anyway.)
  36. unset($GLOBALS['HTTP_POST_VARS'], $GLOBALS['HTTP_POST_VARS']);
  37. unset($GLOBALS['HTTP_POST_FILES'], $GLOBALS['HTTP_POST_FILES']);
  38. // These keys shouldn't be set...ever.
  39. if (isset($_REQUEST['GLOBALS']) || isset($_COOKIE['GLOBALS']))
  40. die('Invalid request variable.');
  41. // Same goes for numeric keys.
  42. foreach (array_merge(array_keys($_POST), array_keys($_GET), array_keys($_FILES)) as $key)
  43. if (is_numeric($key))
  44. die('Numeric request keys are invalid.');
  45. // Numeric keys in cookies are less of a problem. Just unset those.
  46. foreach ($_COOKIE as $key => $value)
  47. if (is_numeric($key))
  48. unset($_COOKIE[$key]);
  49. // Get the correct query string. It may be in an environment variable...
  50. if (!isset($_SERVER['QUERY_STRING']))
  51. $_SERVER['QUERY_STRING'] = getenv('QUERY_STRING');
  52. // It seems that sticking a URL after the query string is mighty common, well, it's evil - don't.
  53. if (strpos($_SERVER['QUERY_STRING'], 'http') === 0)
  54. {
  55. header('HTTP/1.1 400 Bad Request');
  56. die;
  57. }
  58. // Are we going to need to parse the ; out?
  59. if ((strpos(@ini_get('arg_separator.input'), ';') === false || @version_compare(PHP_VERSION, '4.2.0') == -1) && !empty($_SERVER['QUERY_STRING']))
  60. {
  61. // Get rid of the old one! You don't know where it's been!
  62. $_GET = array();
  63. // Was this redirected? If so, get the REDIRECT_QUERY_STRING.
  64. // Do not urldecode() the querystring, unless you so much wish to break OpenID implementation. :)
  65. $_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'], 0, 5) === 'url=/' ? $_SERVER['REDIRECT_QUERY_STRING'] : $_SERVER['QUERY_STRING'];
  66. // Replace ';' with '&' and '&something&' with '&something=&'. (this is done for compatibility...)
  67. // !!! smflib
  68. parse_str(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr($_SERVER['QUERY_STRING'], array(';?' => '&', ';' => '&', '%00' => '', "\0" => ''))), $_GET);
  69. // Magic quotes still applies with parse_str - so clean it up.
  70. if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes']))
  71. $_GET = $removeMagicQuoteFunction($_GET);
  72. }
  73. elseif (strpos(@ini_get('arg_separator.input'), ';') !== false)
  74. {
  75. if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes']))
  76. $_GET = $removeMagicQuoteFunction($_GET);
  77. // Search engines will send action=profile%3Bu=1, which confuses PHP.
  78. foreach ($_GET as $k => $v)
  79. {
  80. if (is_string($v) && strpos($k, ';') !== false)
  81. {
  82. $temp = explode(';', $v);
  83. $_GET[$k] = $temp[0];
  84. for ($i = 1, $n = count($temp); $i < $n; $i++)
  85. {
  86. @list ($key, $val) = @explode('=', $temp[$i], 2);
  87. if (!isset($_GET[$key]))
  88. $_GET[$key] = $val;
  89. }
  90. }
  91. // This helps a lot with integration!
  92. if (strpos($k, '?') === 0)
  93. {
  94. $_GET[substr($k, 1)] = $v;
  95. unset($_GET[$k]);
  96. }
  97. }
  98. }
  99. // There's no query string, but there is a URL... try to get the data from there.
  100. if (!empty($_SERVER['REQUEST_URI']))
  101. {
  102. // Remove the .html, assuming there is one.
  103. if (substr($_SERVER['REQUEST_URI'], strrpos($_SERVER['REQUEST_URI'], '.'), 4) == '.htm')
  104. $request = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '.'));
  105. else
  106. $request = $_SERVER['REQUEST_URI'];
  107. // !!! smflib.
  108. // Replace 'index.php/a,b,c/d/e,f' with 'a=b,c&d=&e=f' and parse it into $_GET.
  109. if (strpos($request, basename($scripturl) . '/') !== false)
  110. {
  111. parse_str(substr(preg_replace('/&(\w+)(?=&|$)/', '&$1=', strtr(preg_replace('~/([^,/]+),~', '/$1=', substr($request, strpos($request, basename($scripturl)) + strlen(basename($scripturl)))), '/', '&')), 1), $temp);
  112. if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0 && empty($modSettings['integrate_magic_quotes']))
  113. $temp = $removeMagicQuoteFunction($temp);
  114. $_GET += $temp;
  115. }
  116. }
  117. // If magic quotes is on we have some work...
  118. if (function_exists('get_magic_quotes_gpc') && @get_magic_quotes_gpc() != 0)
  119. {
  120. $_ENV = $removeMagicQuoteFunction($_ENV);
  121. $_POST = $removeMagicQuoteFunction($_POST);
  122. $_COOKIE = $removeMagicQuoteFunction($_COOKIE);
  123. foreach ($_FILES as $k => $dummy)
  124. if (isset($_FILES[$k]['name']))
  125. $_FILES[$k]['name'] = $removeMagicQuoteFunction($_FILES[$k]['name']);
  126. }
  127. // Add entities to GET. This is kinda like the slashes on everything else.
  128. $_GET = htmlspecialchars__recursive($_GET);
  129. // Let's not depend on the ini settings... why even have COOKIE in there, anyway?
  130. $_REQUEST = $_POST + $_GET;
  131. // Make sure $board and $topic are numbers.
  132. if (isset($_REQUEST['board']))
  133. {
  134. // Make sure its a string and not something else like an array
  135. $_REQUEST['board'] = (string) $_REQUEST['board'];
  136. // If there's a slash in it, we've got a start value! (old, compatible links.)
  137. if (strpos($_REQUEST['board'], '/') !== false)
  138. list ($_REQUEST['board'], $_REQUEST['start']) = explode('/', $_REQUEST['board']);
  139. // Same idea, but dots. This is the currently used format - ?board=1.0...
  140. elseif (strpos($_REQUEST['board'], '.') !== false)
  141. list ($_REQUEST['board'], $_REQUEST['start']) = explode('.', $_REQUEST['board']);
  142. // Now make absolutely sure it's a number.
  143. $board = (int) $_REQUEST['board'];
  144. $_REQUEST['start'] = isset($_REQUEST['start']) ? (int) $_REQUEST['start'] : 0;
  145. // This is for "Who's Online" because it might come via POST - and it should be an int here.
  146. $_GET['board'] = $board;
  147. }
  148. // Well, $board is going to be a number no matter what.
  149. else
  150. $board = 0;
  151. // If there's a threadid, it's probably an old YaBB SE link. Flow with it.
  152. if (isset($_REQUEST['threadid']) && !isset($_REQUEST['topic']))
  153. $_REQUEST['topic'] = $_REQUEST['threadid'];
  154. // We've got topic!
  155. if (isset($_REQUEST['topic']))
  156. {
  157. // Make sure its a string and not something else like an array
  158. $_REQUEST['topic'] = (string) $_REQUEST['topic'];
  159. // Slash means old, beta style, formatting. That's okay though, the link should still work.
  160. if (strpos($_REQUEST['topic'], '/') !== false)
  161. list ($_REQUEST['topic'], $_REQUEST['start']) = explode('/', $_REQUEST['topic']);
  162. // Dots are useful and fun ;). This is ?topic=1.15.
  163. elseif (strpos($_REQUEST['topic'], '.') !== false)
  164. list ($_REQUEST['topic'], $_REQUEST['start']) = explode('.', $_REQUEST['topic']);
  165. $topic = (int) $_REQUEST['topic'];
  166. // Now make sure the online log gets the right number.
  167. $_GET['topic'] = $topic;
  168. }
  169. else
  170. $topic = 0;
  171. // There should be a $_REQUEST['start'], some at least. If you need to default to other than 0, use $_GET['start'].
  172. if (empty($_REQUEST['start']) || $_REQUEST['start'] < 0 || (int) $_REQUEST['start'] > 2147473647)
  173. $_REQUEST['start'] = 0;
  174. // The action needs to be a string and not an array or anything else
  175. if (isset($_REQUEST['action']))
  176. $_REQUEST['action'] = (string) $_REQUEST['action'];
  177. if (isset($_GET['action']))
  178. $_GET['action'] = (string) $_GET['action'];
  179. // Make sure we have a valid REMOTE_ADDR.
  180. if (!isset($_SERVER['REMOTE_ADDR']))
  181. {
  182. $_SERVER['REMOTE_ADDR'] = '';
  183. // A new magic variable to indicate we think this is command line.
  184. $_SERVER['is_cli'] = true;
  185. }
  186. elseif (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 0)
  187. $_SERVER['REMOTE_ADDR'] = 'unknown';
  188. // Try to calculate their most likely IP for those people behind proxies (And the like).
  189. $_SERVER['BAN_CHECK_IP'] = $_SERVER['REMOTE_ADDR'];
  190. // Find the user's IP address. (but don't let it give you 'unknown'!)
  191. if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
  192. {
  193. // We have both forwarded for AND client IP... check the first forwarded for as the block - only switch if it's better that way.
  194. if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') && '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
  195. $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
  196. else
  197. $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP'];
  198. }
  199. if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
  200. {
  201. // Since they are in different blocks, it's probably reversed.
  202. if (strtok($_SERVER['REMOTE_ADDR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.'))
  203. $_SERVER['BAN_CHECK_IP'] = implode('.', array_reverse(explode('.', $_SERVER['HTTP_CLIENT_IP'])));
  204. else
  205. $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP'];
  206. }
  207. elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
  208. {
  209. // If there are commas, get the last one.. probably.
  210. if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false)
  211. {
  212. $ips = array_reverse(explode(', ', $_SERVER['HTTP_X_FORWARDED_FOR']));
  213. // Go through each IP...
  214. foreach ($ips as $i => $ip)
  215. {
  216. // Make sure it's in a valid range...
  217. if (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $ip) != 0 && preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) == 0)
  218. continue;
  219. // Otherwise, we've got an IP!
  220. $_SERVER['BAN_CHECK_IP'] = trim($ip);
  221. break;
  222. }
  223. }
  224. // Otherwise just use the only one.
  225. elseif (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)
  226. $_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
  227. }
  228. // Make sure we know the URL of the current request.
  229. if (empty($_SERVER['REQUEST_URI']))
  230. $_SERVER['REQUEST_URL'] = $scripturl . (!empty($_SERVER['QUERY_STRING']) ? '?' . $_SERVER['QUERY_STRING'] : '');
  231. elseif (preg_match('~^([^/]+//[^/]+)~', $scripturl, $match) == 1)
  232. $_SERVER['REQUEST_URL'] = $match[1] . $_SERVER['REQUEST_URI'];
  233. else
  234. $_SERVER['REQUEST_URL'] = $_SERVER['REQUEST_URI'];
  235. // And make sure HTTP_USER_AGENT is set.
  236. $_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? htmlspecialchars($smcFunc['db_unescape_string']($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : '';
  237. // Some final checking.
  238. if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['BAN_CHECK_IP']) === 0)
  239. $_SERVER['BAN_CHECK_IP'] = '';
  240. if ($_SERVER['REMOTE_ADDR'] == 'unknown')
  241. $_SERVER['REMOTE_ADDR'] = '';
  242. }
  243. /**
  244. * Adds slashes to the array/variable.
  245. * What it does:
  246. * - returns the var, as an array or string, with escapes as required.
  247. - importantly escapes all keys and values!
  248. - calls itself recursively if necessary.
  249. *
  250. * @param array|string $var
  251. * @return array|string
  252. */
  253. function escapestring__recursive($var)
  254. {
  255. global $smcFunc;
  256. if (!is_array($var))
  257. return $smcFunc['db_escape_string']($var);
  258. // Reindex the array with slashes.
  259. $new_var = array();
  260. // Add slashes to every element, even the indexes!
  261. foreach ($var as $k => $v)
  262. $new_var[$smcFunc['db_escape_string']($k)] = escapestring__recursive($v);
  263. return $new_var;
  264. }
  265. /**
  266. * Adds html entities to the array/variable. Uses two underscores to guard against overloading.
  267. * What it does:
  268. * - adds entities (&quot;, &lt;, &gt;) to the array or string var.
  269. - importantly, does not effect keys, only values.
  270. - calls itself recursively if necessary.
  271. * @param array|string $var
  272. * @param int $level = 0
  273. * @return array|string
  274. */
  275. function htmlspecialchars__recursive($var, $level = 0)
  276. {
  277. global $smcFunc;
  278. if (!is_array($var))
  279. return isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($var, ENT_QUOTES) : htmlspecialchars($var, ENT_QUOTES);
  280. // Add the htmlspecialchars to every element.
  281. foreach ($var as $k => $v)
  282. $var[$k] = $level > 25 ? null : htmlspecialchars__recursive($v, $level + 1);
  283. return $var;
  284. }
  285. /**
  286. * Removes url stuff from the array/variable. Uses two underscores to guard against overloading.
  287. * What it does:
  288. * - takes off url encoding (%20, etc.) from the array or string var.
  289. - importantly, does it to keys too!
  290. - calls itself recursively if there are any sub arrays.
  291. *
  292. * @param array|string $var
  293. * @param int $level = 0
  294. * @return array|string
  295. */
  296. function urldecode__recursive($var, $level = 0)
  297. {
  298. if (!is_array($var))
  299. return urldecode($var);
  300. // Reindex the array...
  301. $new_var = array();
  302. // Add the htmlspecialchars to every element.
  303. foreach ($var as $k => $v)
  304. $new_var[urldecode($k)] = $level > 25 ? null : urldecode__recursive($v, $level + 1);
  305. return $new_var;
  306. }
  307. /**
  308. * Unescapes any array or variable. Uses two underscores to guard against overloading.
  309. * What it does:
  310. * - unescapes, recursively, from the array or string var.
  311. - effects both keys and values of arrays.
  312. - calls itself recursively to handle arrays of arrays.
  313. *
  314. * @param array|string $var
  315. * @return array|string
  316. */
  317. function unescapestring__recursive($var)
  318. {
  319. global $smcFunc;
  320. if (!is_array($var))
  321. return $smcFunc['db_unescape_string']($var);
  322. // Reindex the array without slashes, this time.
  323. $new_var = array();
  324. // Strip the slashes from every element.
  325. foreach ($var as $k => $v)
  326. $new_var[$smcFunc['db_unescape_string']($k)] = unescapestring__recursive($v);
  327. return $new_var;
  328. }
  329. /**
  330. * Remove slashes recursively. Uses two underscores to guard against overloading.
  331. * What it does:
  332. * - removes slashes, recursively, from the array or string var.
  333. - effects both keys and values of arrays.
  334. - calls itself recursively to handle arrays of arrays.
  335. *
  336. * @param array|string $var
  337. * @param int $level = 0
  338. * @return array|string
  339. */
  340. function stripslashes__recursive($var, $level = 0)
  341. {
  342. if (!is_array($var))
  343. return stripslashes($var);
  344. // Reindex the array without slashes, this time.
  345. $new_var = array();
  346. // Strip the slashes from every element.
  347. foreach ($var as $k => $v)
  348. $new_var[stripslashes($k)] = $level > 25 ? null : stripslashes__recursive($v, $level + 1);
  349. return $new_var;
  350. }
  351. /**
  352. * Trim a string including the HTML space, character 160. Uses two underscores to guard against overloading.
  353. * What it does:
  354. * - trims a string or an the var array using html characters as well.
  355. - does not effect keys, only values.
  356. - may call itself recursively if needed.
  357. *
  358. * @param array|string $var
  359. * @param int $level = 0
  360. * @return array|string
  361. */
  362. function htmltrim__recursive($var, $level = 0)
  363. {
  364. global $smcFunc;
  365. // Remove spaces (32), tabs (9), returns (13, 10, and 11), nulls (0), and hard spaces. (160)
  366. if (!is_array($var))
  367. return isset($smcFunc) ? $smcFunc['htmltrim']($var) : trim($var, ' ' . "\t\n\r\x0B" . '\0' . "\xA0");
  368. // Go through all the elements and remove the whitespace.
  369. foreach ($var as $k => $v)
  370. $var[$k] = $level > 25 ? null : htmltrim__recursive($v, $level + 1);
  371. return $var;
  372. }
  373. /**
  374. * Clean up the XML to make sure it doesn't contain invalid characters.
  375. * What it does:
  376. * - removes invalid XML characters to assure the input string being
  377. parsed properly.
  378. *
  379. * @param string $string
  380. * @return string
  381. */
  382. function cleanXml($string)
  383. {
  384. global $context;
  385. // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char
  386. return preg_replace('~[\x00-\x08\x0B\x0C\x0E-\x19' . ($context['utf8'] ? (@version_compare(PHP_VERSION, '4.3.3') != -1 ? '\x{D800}-\x{DFFF}\x{FFFE}\x{FFFF}' : "\xED\xA0\x80-\xED\xBF\xBF\xEF\xBF\xBE\xEF\xBF\xBF") : '') . ']~' . ($context['utf8'] ? 'u' : ''), '', $string);
  387. }
  388. /**
  389. * @todo needs a description
  390. *
  391. * @param string $string
  392. * @return string
  393. */
  394. function JavaScriptEscape($string)
  395. {
  396. global $scripturl;
  397. return '\'' . strtr($string, array(
  398. "\r" => '',
  399. "\n" => '\\n',
  400. "\t" => '\\t',
  401. '\\' => '\\\\',
  402. '\'' => '\\\'',
  403. '</' => '<\' + \'/',
  404. 'script' => 'scri\'+\'pt',
  405. '<a href' => '<a hr\'+\'ef',
  406. $scripturl => $scripturl . '\'+\'',
  407. )) . '\'';
  408. }
  409. /**
  410. * Rewrite URLs to include the session ID.
  411. * What it does:
  412. * - rewrites the URLs outputted to have the session ID, if the user
  413. is not accepting cookies and is using a standard web browser.
  414. - handles rewriting URLs for the queryless URLs option.
  415. - can be turned off entirely by setting $scripturl to an empty
  416. string, ''. (it wouldn't work well like that anyway.)
  417. - because of bugs in certain builds of PHP, does not function in
  418. versions lower than 4.3.0 - please upgrade if this hurts you.
  419. *
  420. * @param string $buffer
  421. * @return string
  422. */
  423. function ob_sessrewrite($buffer)
  424. {
  425. global $scripturl, $modSettings, $user_info, $context;
  426. // If $scripturl is set to nothing, or the SID is not defined (SSI?) just quit.
  427. if ($scripturl == '' || !defined('SID'))
  428. return $buffer;
  429. // Do nothing if the session is cookied, or they are a crawler - guests are caught by redirectexit(). This doesn't work below PHP 4.3.0, because it makes the output buffer bigger.
  430. // !!! smflib
  431. if (empty($_COOKIE) && SID != '' && empty($context['browser']['possibly_robot']) && @version_compare(PHP_VERSION, '4.3.0') != -1)
  432. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', '"' . $scripturl . '?' . SID . '&amp;', $buffer);
  433. // Debugging templates, are we?
  434. elseif (isset($_GET['debug']))
  435. $buffer = preg_replace('/(?<!<link rel="canonical" href=)"' . preg_quote($scripturl, '/') . '\\??/', '"' . $scripturl . '?debug;', $buffer);
  436. // This should work even in 4.2.x, just not CGI without cgi.fix_pathinfo.
  437. if (!empty($modSettings['queryless_urls']) && (!$context['server']['is_cgi'] || @ini_get('cgi.fix_pathinfo') == 1 || @get_cfg_var('cgi.fix_pathinfo') == 1) && ($context['server']['is_apache'] || $context['server']['is_lighttpd']))
  438. {
  439. // Let's do something special for session ids!
  440. if (defined('SID') && SID != '')
  441. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '\?(?:' . SID . '(?:;|&|&amp;))((?:board|topic)=[^#"]+?)(#[^"]*?)?"/e', "'\"' . \$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html?' . SID . '\$2\"'", $buffer);
  442. else
  443. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+?)(#[^"]*?)?"/e', "'\"' . \$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html\$2\"'", $buffer);
  444. }
  445. // Return the changed buffer.
  446. return $buffer;
  447. }
  448. ?>