QueryString.php 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526
  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. *
  272. * @param array|string $var
  273. * @param int $level = 0
  274. * @return array|string
  275. */
  276. function htmlspecialchars__recursive($var, $level = 0)
  277. {
  278. global $smcFunc;
  279. if (!is_array($var))
  280. return isset($smcFunc['htmlspecialchars']) ? $smcFunc['htmlspecialchars']($var, ENT_QUOTES) : htmlspecialchars($var, ENT_QUOTES);
  281. // Add the htmlspecialchars to every element.
  282. foreach ($var as $k => $v)
  283. $var[$k] = $level > 25 ? null : htmlspecialchars__recursive($v, $level + 1);
  284. return $var;
  285. }
  286. /**
  287. * Removes url stuff from the array/variable. Uses two underscores to guard against overloading.
  288. * What it does:
  289. * - takes off url encoding (%20, etc.) from the array or string var.
  290. * - importantly, does it to keys too!
  291. * - calls itself recursively if there are any sub arrays.
  292. *
  293. * @param array|string $var
  294. * @param int $level = 0
  295. * @return array|string
  296. */
  297. function urldecode__recursive($var, $level = 0)
  298. {
  299. if (!is_array($var))
  300. return urldecode($var);
  301. // Reindex the array...
  302. $new_var = array();
  303. // Add the htmlspecialchars to every element.
  304. foreach ($var as $k => $v)
  305. $new_var[urldecode($k)] = $level > 25 ? null : urldecode__recursive($v, $level + 1);
  306. return $new_var;
  307. }
  308. /**
  309. * Unescapes any array or variable. Uses two underscores to guard against overloading.
  310. * What it does:
  311. * - unescapes, recursively, from the array or string var.
  312. * - effects both keys and values of arrays.
  313. * - calls itself recursively to handle arrays of arrays.
  314. *
  315. * @param array|string $var
  316. * @return array|string
  317. */
  318. function unescapestring__recursive($var)
  319. {
  320. global $smcFunc;
  321. if (!is_array($var))
  322. return $smcFunc['db_unescape_string']($var);
  323. // Reindex the array without slashes, this time.
  324. $new_var = array();
  325. // Strip the slashes from every element.
  326. foreach ($var as $k => $v)
  327. $new_var[$smcFunc['db_unescape_string']($k)] = unescapestring__recursive($v);
  328. return $new_var;
  329. }
  330. /**
  331. * Remove slashes recursively. Uses two underscores to guard against overloading.
  332. * What it does:
  333. * - removes slashes, recursively, from the array or string var.
  334. * - effects both keys and values of arrays.
  335. * - calls itself recursively to handle arrays of arrays.
  336. *
  337. * @param array|string $var
  338. * @param int $level = 0
  339. * @return array|string
  340. */
  341. function stripslashes__recursive($var, $level = 0)
  342. {
  343. if (!is_array($var))
  344. return stripslashes($var);
  345. // Reindex the array without slashes, this time.
  346. $new_var = array();
  347. // Strip the slashes from every element.
  348. foreach ($var as $k => $v)
  349. $new_var[stripslashes($k)] = $level > 25 ? null : stripslashes__recursive($v, $level + 1);
  350. return $new_var;
  351. }
  352. /**
  353. * Trim a string including the HTML space, character 160. Uses two underscores to guard against overloading.
  354. * What it does:
  355. * - trims a string or an the var array using html characters as well.
  356. * - does not effect keys, only values.
  357. * - may call itself recursively if needed.
  358. *
  359. * @param array|string $var
  360. * @param int $level = 0
  361. * @return array|string
  362. */
  363. function htmltrim__recursive($var, $level = 0)
  364. {
  365. global $smcFunc;
  366. // Remove spaces (32), tabs (9), returns (13, 10, and 11), nulls (0), and hard spaces. (160)
  367. if (!is_array($var))
  368. return isset($smcFunc) ? $smcFunc['htmltrim']($var) : trim($var, ' ' . "\t\n\r\x0B" . '\0' . "\xA0");
  369. // Go through all the elements and remove the whitespace.
  370. foreach ($var as $k => $v)
  371. $var[$k] = $level > 25 ? null : htmltrim__recursive($v, $level + 1);
  372. return $var;
  373. }
  374. /**
  375. * Clean up the XML to make sure it doesn't contain invalid characters.
  376. * What it does:
  377. * - removes invalid XML characters to assure the input string being
  378. * - parsed properly.
  379. *
  380. * @param string $string
  381. * @return string
  382. */
  383. function cleanXml($string)
  384. {
  385. global $context;
  386. // http://www.w3.org/TR/2000/REC-xml-20001006#NT-Char
  387. 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);
  388. }
  389. /**
  390. * @todo needs a description
  391. *
  392. * @param string $string
  393. * @return string
  394. */
  395. function JavaScriptEscape($string)
  396. {
  397. global $scripturl;
  398. return '\'' . strtr($string, array(
  399. "\r" => '',
  400. "\n" => '\\n',
  401. "\t" => '\\t',
  402. '\\' => '\\\\',
  403. '\'' => '\\\'',
  404. '</' => '<\' + \'/',
  405. 'script' => 'scri\'+\'pt',
  406. '<a href' => '<a hr\'+\'ef',
  407. $scripturl => $scripturl . '\'+\'',
  408. )) . '\'';
  409. }
  410. /**
  411. * Rewrite URLs to include the session ID.
  412. * What it does:
  413. * - rewrites the URLs outputted to have the session ID, if the user
  414. * is not accepting cookies and is using a standard web browser.
  415. * - handles rewriting URLs for the queryless URLs option.
  416. * - can be turned off entirely by setting $scripturl to an empty
  417. * string, ''. (it wouldn't work well like that anyway.)
  418. * - because of bugs in certain builds of PHP, does not function in
  419. * versions lower than 4.3.0 - please upgrade if this hurts you.
  420. *
  421. * @param string $buffer
  422. * @return string
  423. */
  424. function ob_sessrewrite($buffer)
  425. {
  426. global $scripturl, $modSettings, $user_info, $context;
  427. // If $scripturl is set to nothing, or the SID is not defined (SSI?) just quit.
  428. if ($scripturl == '' || !defined('SID'))
  429. return $buffer;
  430. // 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.
  431. // !!! smflib
  432. if (empty($_COOKIE) && SID != '' && empty($context['browser']['possibly_robot']) && @version_compare(PHP_VERSION, '4.3.0') != -1)
  433. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')\\??/', '"' . $scripturl . '?' . SID . '&amp;', $buffer);
  434. // Debugging templates, are we?
  435. elseif (isset($_GET['debug']))
  436. $buffer = preg_replace('/(?<!<link rel="canonical" href=)"' . preg_quote($scripturl, '/') . '\\??/', '"' . $scripturl . '?debug;', $buffer);
  437. // This should work even in 4.2.x, just not CGI without cgi.fix_pathinfo.
  438. 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']))
  439. {
  440. // Let's do something special for session ids!
  441. if (defined('SID') && SID != '')
  442. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '\?(?:' . SID . '(?:;|&|&amp;))((?:board|topic)=[^#"]+?)(#[^"]*?)?"/e', "'\"' . \$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html?' . SID . '\$2\"'", $buffer);
  443. else
  444. $buffer = preg_replace('/"' . preg_quote($scripturl, '/') . '\?((?:board|topic)=[^#"]+?)(#[^"]*?)?"/e', "'\"' . \$scripturl . '/' . strtr('\$1', '&;=', '//,') . '.html\$2\"'", $buffer);
  445. }
  446. // Return the changed buffer.
  447. return $buffer;
  448. }
  449. ?>