global.php 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965
  1. <?php
  2. /**
  3. * global.php
  4. *
  5. * This includes code to update < 4.1.0 globals to the newer format
  6. * It also has some session register functions that work across various
  7. * php versions.
  8. *
  9. * @copyright 1999-2025 The SquirrelMail Project Team
  10. * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  11. * @version $Id$
  12. * @package squirrelmail
  13. */
  14. /**
  15. * These constants are used in the function sqgetGlobalVar(). See
  16. * sqgetGlobalVar() for a description of what they mean.
  17. *
  18. * @since 1.4.0
  19. */
  20. define('SQ_INORDER',0);
  21. define('SQ_GET',1);
  22. define('SQ_POST',2);
  23. define('SQ_SESSION',3);
  24. define('SQ_COOKIE',4);
  25. define('SQ_SERVER',5);
  26. define('SQ_FORM',6);
  27. /**
  28. * returns true if current php version is at mimimum a.b.c
  29. *
  30. * Called: check_php_version(4,1)
  31. * @param int a major version number
  32. * @param int b minor version number
  33. * @param int c release number
  34. * @return bool
  35. */
  36. function check_php_version ($a = '0', $b = '0', $c = '0')
  37. {
  38. return version_compare ( PHP_VERSION, "$a.$b.$c", 'ge' );
  39. }
  40. /**
  41. * returns true if the current internal SM version is at minimum a.b.c
  42. * These are plain integer comparisons, as our internal version is
  43. * constructed by us, as an array of 3 ints.
  44. *
  45. * Called: check_sm_version(1,3,3)
  46. * @param int a major version number
  47. * @param int b minor version number
  48. * @param int c release number
  49. * @return bool
  50. */
  51. function check_sm_version($a = 0, $b = 0, $c = 0)
  52. {
  53. global $SQM_INTERNAL_VERSION;
  54. if ( !isset($SQM_INTERNAL_VERSION) ||
  55. $SQM_INTERNAL_VERSION[0] < $a ||
  56. ( $SQM_INTERNAL_VERSION[0] == $a &&
  57. $SQM_INTERNAL_VERSION[1] < $b) ||
  58. ( $SQM_INTERNAL_VERSION[0] == $a &&
  59. $SQM_INTERNAL_VERSION[1] == $b &&
  60. $SQM_INTERNAL_VERSION[2] < $c ) ) {
  61. return FALSE;
  62. }
  63. return TRUE;
  64. }
  65. /**
  66. * Recursively strip slashes from the values of an array.
  67. * @param array array the array to strip, passed by reference
  68. * @return void
  69. */
  70. function sqstripslashes(&$array) {
  71. if(count($array) > 0) {
  72. foreach ($array as $index=>$value) {
  73. if (is_array($array[$index])) {
  74. sqstripslashes($array[$index]);
  75. }
  76. else {
  77. $array[$index] = stripslashes($value);
  78. }
  79. }
  80. }
  81. }
  82. /**
  83. * Squelch error output to screen (only) for the given function.
  84. * If the SquirrelMail debug mode SM_DEBUG_MODE_ADVANCED is not
  85. * enabled, error output will not go to the log, either.
  86. *
  87. * This provides an alternative to the @ error-suppression
  88. * operator where errors will not be shown in the interface
  89. * but will show up in the server log file (assuming the
  90. * administrator has configured PHP logging).
  91. *
  92. * @since 1.4.12 and 1.5.2
  93. *
  94. * @param string $function The function to be executed
  95. * @param array $args The arguments to be passed to the function
  96. * (OPTIONAL; default no arguments)
  97. * NOTE: The caller must take extra action if
  98. * the function being called is supposed
  99. * to use any of the parameters by
  100. * reference. In the following example,
  101. * $x is passed by reference and $y is
  102. * passed by value to the "my_func"
  103. * function.
  104. * sq_call_function_suppress_errors('my_func', array(&$x, $y));
  105. *
  106. * @return mixed The return value, if any, of the function being
  107. * executed will be returned.
  108. *
  109. */
  110. function sq_call_function_suppress_errors($function, $args=array()) {
  111. global $sm_debug_mode;
  112. $display_errors = ini_get('display_errors');
  113. ini_set('display_errors', '0');
  114. // if advanced debug mode isn't enabled, don't log the error, either
  115. //
  116. if (!($sm_debug_mode & SM_DEBUG_MODE_ADVANCED))
  117. $error_reporting = error_reporting(0);
  118. $ret = call_user_func_array($function, $args);
  119. if (!($sm_debug_mode & SM_DEBUG_MODE_ADVANCED))
  120. error_reporting($error_reporting);
  121. ini_set('display_errors', $display_errors);
  122. return $ret;
  123. }
  124. /**
  125. * Add a variable to the session.
  126. * @param mixed $var the variable to register
  127. * @param string $name the name to refer to this variable
  128. * @return void
  129. */
  130. function sqsession_register ($var, $name) {
  131. sqsession_is_active();
  132. $_SESSION[$name] = $var;
  133. }
  134. /**
  135. * Delete a variable from the session.
  136. * @param string $name the name of the var to delete
  137. * @return void
  138. */
  139. function sqsession_unregister ($name) {
  140. sqsession_is_active();
  141. unset($_SESSION[$name]);
  142. // starts throwing warnings in PHP 5.3.0 and is
  143. // removed in PHP 6 and is redundant anyway
  144. //session_unregister("$name");
  145. }
  146. /**
  147. * Checks to see if a variable has already been registered
  148. * in the session.
  149. * @param string $name the name of the var to check
  150. * @return bool whether the var has been registered
  151. */
  152. function sqsession_is_registered ($name) {
  153. $test_name = &$name;
  154. $result = false;
  155. if (isset($_SESSION[$test_name])) {
  156. $result = true;
  157. }
  158. return $result;
  159. }
  160. /**
  161. * Retrieves a form variable, from a set of possible similarly named
  162. * form variables, based on finding a different, single field. This
  163. * is intended to allow more than one same-named inputs in a single
  164. * <form>, where the submit button that is clicked tells us which
  165. * input we should retrieve. An example is if we have:
  166. * <select name="startMessage_1">
  167. * <select name="startMessage_2">
  168. * <input type="submit" name="form_submit_1" />
  169. * <input type="submit" name="form_submit_2" />
  170. * and we want to know which one of the select inputs should be
  171. * returned as $startMessage (without the suffix!), this function
  172. * decides by looking for either "form_submit_1" or "form_submit_2"
  173. * (both should not appear). In this example, $name should be
  174. * "startMessage" and $indicator_field should be "form_submit".
  175. *
  176. * NOTE that form widgets must be named with the suffix "_1", "_2", "_3"
  177. * and so on, or this function will not work.
  178. *
  179. * If more than one of the indicator fields is found, the first one
  180. * (numerically) will win.
  181. *
  182. * If an indicator field is found without a matching input ($name)
  183. * field, FALSE is returned.
  184. *
  185. * If no indicator fields are found, a field of $name *without* any
  186. * suffix is searched for (but only if $fallback_no_suffix is TRUE),
  187. * and if not found, FALSE is ultimately returned.
  188. *
  189. * It should also be possible to use the same string for both
  190. * $name and $indicator_field to look for the first possible
  191. * widget with a suffix that can be found (and possibly fallback
  192. * to a widget without a suffix).
  193. *
  194. * @param string name the name of the var to search
  195. * @param mixed value the variable to return
  196. * @param string indicator_field the name of the field upon which to base
  197. * our decision upon (see above)
  198. * @param int search constant defining where to look
  199. * @param bool fallback_no_suffix whether or not to look for $name with
  200. * no suffix when nothing else is found
  201. * @param mixed default the value to assign to $value when nothing is found
  202. * @param int typecast force variable to be cast to given type (please
  203. * use SQ_TYPE_XXX constants or set to FALSE (default)
  204. * to leave variable type unmolested)
  205. *
  206. * @return bool whether variable is found.
  207. */
  208. function sqGetGlobalVarMultiple($name, &$value, $indicator_field,
  209. $search = SQ_INORDER,
  210. $fallback_no_suffix=TRUE, $default=NULL,
  211. $typecast=FALSE) {
  212. // Set arbitrary max limit -- should be much lower except on the
  213. // search results page, if there are many (50 or more?) mailboxes
  214. // shown, this may not be high enough. Is there some way we should
  215. // automate this value?
  216. //
  217. $max_form_search = 100;
  218. for ($i = 1; $i <= $max_form_search; $i++) {
  219. if (sqGetGlobalVar($indicator_field . '_' . $i, $temp, $search)) {
  220. return sqGetGlobalVar($name . '_' . $i, $value, $search, $default, $typecast);
  221. }
  222. }
  223. // no indicator field found; just try without suffix if allowed
  224. //
  225. if ($fallback_no_suffix) {
  226. return sqGetGlobalVar($name, $value, $search, $default, $typecast);
  227. }
  228. // no dice, set default and return FALSE
  229. //
  230. if (!is_null($default)) {
  231. $value = $default;
  232. }
  233. return FALSE;
  234. }
  235. /**
  236. * Search for the variable $name in one or more of the global variables
  237. * $_SESSION, $_POST, $_GET, $_COOKIE, and $_SERVER, and set the value of it in
  238. * the variable $vaule.
  239. *
  240. * $search must be one of the defined constants below. The default is
  241. * SQ_INORDER. Both SQ_INORDER and SQ_FORM stops on the first match.
  242. *
  243. * SQ_INORDER searches $_SESSION, then $_POST, and then $_GET.
  244. * SQ_FORM searches $_POST and then $_GET.
  245. * SQ_COOKIE searches $_COOKIE only.
  246. * SQ_GET searches $_GET only.
  247. * SQ_POST searches $_POST only.
  248. * SQ_SERVER searches $_SERVER only.
  249. * SQ_SESSION searches $_SESSION only.
  250. *
  251. * Example:
  252. * sqgetGlobalVar('username', $username, SQ_SESSION);
  253. * // No quotes around the last parameter, it's a constant - not a string!
  254. *
  255. * @param string name the name of the var to search
  256. * @param mixed value the variable to return
  257. * @param int search constant defining where to look
  258. * @param mixed default the value to assign to $value when nothing is found
  259. * @param int typecast force variable to be cast to given type (please
  260. * use SQ_TYPE_XXX constants or set to FALSE (default)
  261. * to leave variable type unmolested)
  262. *
  263. * @return bool whether variable is found.
  264. */
  265. function sqgetGlobalVar($name, &$value, $search = SQ_INORDER, $default = NULL, $typecast = FALSE) {
  266. // The return value defaults to FALSE, i.e. the variable wasn't found.
  267. $result = FALSE;
  268. // Search the global variables to find a match.
  269. switch ($search) {
  270. default:
  271. // The default needs to be first here so SQ_INORDER will be used if
  272. // $search isn't a valid constant.
  273. case SQ_INORDER:
  274. // Search $_SESSION, then $_POST, and then $_GET. Stop on the first
  275. // match.
  276. case SQ_SESSION:
  277. if (isset($_SESSION[$name])) {
  278. // If a match is found, set the specified variable to the found
  279. // value, indicate a match, and stop the search.
  280. $value = $_SESSION[$name];
  281. $result = TRUE;
  282. break;
  283. } elseif ($search == SQ_SESSION) {
  284. // Only stop the search if SQ_SESSION is set. SQ_INORDER will
  285. // continue with the next clause.
  286. break;
  287. }
  288. case SQ_FORM:
  289. // Search $_POST and then $_GET. Stop on the first match.
  290. case SQ_POST:
  291. if (isset($_POST[$name])) {
  292. // If a match is found, set the specified variable to the found
  293. // value, indicate a match, and stop the search.
  294. $value = $_POST[$name];
  295. $result = TRUE;
  296. break;
  297. } elseif ($search == SQ_POST) {
  298. // Only stop the search if SQ_POST is set. SQ_INORDER and
  299. // SQ_FORM will continue with the next clause.
  300. break;
  301. }
  302. case SQ_GET:
  303. if (isset($_GET[$name])) {
  304. // If a match is found, set the specified variable to the found
  305. // value, indicate a match, and stop the search.
  306. $value = $_GET[$name];
  307. $result = TRUE;
  308. break;
  309. }
  310. // Stop the search regardless of if SQ_INORDER, SQ_FORM, or SQ_GET
  311. // is set. All three of them ends here.
  312. break;
  313. case SQ_COOKIE:
  314. if (isset($_COOKIE[$name])) {
  315. // If a match is found, set the specified variable to the found
  316. // value, indicate a match, and stop the search.
  317. $value = $_COOKIE[$name];
  318. $result = TRUE;
  319. break;
  320. }
  321. // Stop the search.
  322. break;
  323. case SQ_SERVER:
  324. if (isset($_SERVER[$name])) {
  325. // If a match is found, set the specified variable to the found
  326. // value, indicate a match, and stop the search.
  327. $value = $_SERVER[$name];
  328. $result = TRUE;
  329. break;
  330. }
  331. // Stop the search.
  332. break;
  333. }
  334. if ($result && $typecast) {
  335. // Only typecast if it's requested and a match is found. The default is
  336. // not to typecast, which will happen if a valid constant isn't
  337. // specified.
  338. switch ($typecast) {
  339. case SQ_TYPE_INT:
  340. // Typecast the value and stop.
  341. $value = (int) $value;
  342. break;
  343. case SQ_TYPE_STRING:
  344. // Typecast the value and stop.
  345. $value = (string) $value;
  346. break;
  347. case SQ_TYPE_BOOL:
  348. // Typecast the value and stop.
  349. $value = (bool) $value;
  350. break;
  351. case SQ_TYPE_BIGINT:
  352. // Typecast the value and stop.
  353. $value = (preg_match('/^[0-9]+$/', $value) ? $value : '0');
  354. break;
  355. default:
  356. // The default is to do nothing.
  357. break;
  358. }
  359. } else if (!$result && !is_null($default)) {
  360. // If no match is found and a default value is specified, set it.
  361. $value = $default;
  362. }
  363. // Return if a match was found or not.
  364. return $result;
  365. }
  366. /**
  367. * Get an immutable copy of a configuration variable if SquirrelMail
  368. * is in "secured configuration" mode. This guarantees the caller
  369. * gets a copy of the requested value as it is set in the main
  370. * application configuration (including config_local overrides), and
  371. * not what it might be after possibly having been modified by some
  372. * other code (usually a plugin overriding configuration values for
  373. * one reason or another).
  374. *
  375. * WARNING: Please use this function as little as possible, because
  376. * every time it is called, it forcibly reloads the main configuration
  377. * file(s).
  378. *
  379. * Caller beware that this function will do nothing if SquirrelMail
  380. * is not in "secured configuration" mode per the $secured_config
  381. * setting.
  382. *
  383. * @param string $var_name The name of the desired variable
  384. *
  385. * @return mixed The desired value
  386. *
  387. * @since 1.5.2
  388. *
  389. */
  390. function get_secured_config_value($var_name) {
  391. static $return_values = array();
  392. // if we can avoid it, return values that have
  393. // already been retrieved (so we don't have to
  394. // include the config file yet again)
  395. //
  396. if (isset($return_values[$var_name])) {
  397. return $return_values[$var_name];
  398. }
  399. // load site configuration
  400. //
  401. require(SM_PATH . 'config/config.php');
  402. // load local configuration overrides
  403. //
  404. if (file_exists(SM_PATH . 'config/config_local.php')) {
  405. require(SM_PATH . 'config/config_local.php');
  406. }
  407. // if SM isn't in "secured configuration" mode,
  408. // just return the desired value from the global scope
  409. //
  410. if (!$secured_config) {
  411. global $$var_name;
  412. $return_values[$var_name] = $$var_name;
  413. return $$var_name;
  414. }
  415. // else we return what we got from the config file
  416. //
  417. $return_values[$var_name] = $$var_name;
  418. return $$var_name;
  419. }
  420. /**
  421. * Deletes an existing session, more advanced than the standard PHP
  422. * session_destroy(), it explicitly deletes the cookies and global vars.
  423. *
  424. * WARNING: Older PHP versions have some issues with session management.
  425. * See http://bugs.php.net/11643 (warning, spammed bug tracker) and
  426. * http://bugs.php.net/13834. SID constant is not destroyed in PHP 4.1.2,
  427. * 4.2.3 and maybe other versions. If you restart session after session
  428. * is destroyed, affected PHP versions produce PHP notice. Bug should
  429. * be fixed only in 4.3.0
  430. */
  431. function sqsession_destroy() {
  432. /*
  433. * php.net says we can kill the cookie by setting just the name:
  434. * http://www.php.net/manual/en/function.setcookie.php
  435. * maybe this will help fix the session merging again.
  436. *
  437. * Changed the theory on this to kill the cookies first starting
  438. * a new session will provide a new session for all instances of
  439. * the browser, we don't want that, as that is what is causing the
  440. * merging of sessions.
  441. */
  442. global $base_uri, $_COOKIE, $_SESSION;
  443. if (isset($_COOKIE[session_name()]) && session_name()) {
  444. // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri);
  445. sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri);
  446. /*
  447. * Make sure to kill /src and /src/ cookies, just in case there are
  448. * some left-over or malicious ones set in user's browser.
  449. * NB: Note that an attacker could try to plant a cookie for one
  450. * of the /plugins/* directories. Such cookies can block
  451. * access to certain plugin pages, but they do not influence
  452. * or fixate the $base_uri cookie, so we don't worry about
  453. * trying to delete all of them here.
  454. */
  455. // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri . 'src');
  456. // sqsetcookie(session_name(), $_COOKIE[session_name()], 1, $base_uri . 'src/');
  457. sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri . 'src');
  458. sqsetcookie(session_name(), 'SQMTRASH', 1, $base_uri . 'src/');
  459. }
  460. if (isset($_COOKIE['key']) && $_COOKIE['key']) sqsetcookie('key','SQMTRASH',1,$base_uri);
  461. /* Make sure new session id is generated on subsequent session_start() */
  462. unset($_COOKIE[session_name()]);
  463. unset($_GET[session_name()]);
  464. unset($_POST[session_name()]);
  465. $sessid = session_id();
  466. if (!empty( $sessid )) {
  467. $_SESSION = array();
  468. @session_destroy();
  469. }
  470. }
  471. /**
  472. * Function to verify a session has been started. If it hasn't
  473. * start a session up. php.net doesn't tell you that $_SESSION
  474. * (even though autoglobal), is not created unless a session is
  475. * started, unlike $_POST, $_GET and such
  476. * Update: (see #1685031) the session ID is left over after the
  477. * session is closed in some PHP setups; this function just becomes
  478. * a passthru to sqsession_start(), but leaving old code in for
  479. * edification.
  480. */
  481. function sqsession_is_active() {
  482. //$sessid = session_id();
  483. //if ( empty( $sessid ) ) {
  484. sqsession_start();
  485. //}
  486. }
  487. /**
  488. * Function to start the session and store the cookie with the session_id as
  489. * HttpOnly cookie which means that the cookie isn't accessible by javascript
  490. * (IE6 only)
  491. * Note that as sqsession_is_active() no longer discriminates as to when
  492. * it calls this function, session_start() has to have E_NOTICE suppression
  493. * (thus the @ sign).
  494. */
  495. function sqsession_start() {
  496. global $base_uri;
  497. sq_call_function_suppress_errors('session_start');
  498. // was: @session_start();
  499. $session_id = session_id();
  500. // session_starts sets the sessionid cookie but without the httponly var
  501. // setting the cookie again sets the httponly cookie attribute
  502. //
  503. // need to check if headers have been sent, since sqsession_is_active()
  504. // has become just a passthru to this function, so the sqsetcookie()
  505. // below is called every time, even after headers have already been sent
  506. //
  507. if (!headers_sent())
  508. sqsetcookie(session_name(),$session_id,false,$base_uri);
  509. }
  510. /**
  511. * Set a cookie
  512. *
  513. * @param string $sName The name of the cookie.
  514. * @param string $sValue The value of the cookie.
  515. * @param int $iExpire The time the cookie expires. This is a Unix
  516. * timestamp so is in number of seconds since
  517. * the epoch.
  518. * @param string $sPath The path on the server in which the cookie
  519. * will be available on.
  520. * @param string $sDomain The domain that the cookie is available.
  521. * @param boolean $bSecure Indicates that the cookie should only be
  522. * transmitted over a secure HTTPS connection.
  523. * @param boolean $bHttpOnly Disallow JS to access the cookie (IE6 only)
  524. * @param boolean $bReplace Replace previous cookies with same name?
  525. * @param string $sSameSite Optional override of the default SameSite
  526. * cookie policy detemined from the global
  527. * configuration item $same_site_cookies
  528. * (which can be set in config/config_local.php)
  529. * (should be NULL to accept the configured global
  530. * default or one of "Lax" "Strict" or "None"
  531. * but "None" will not work if $bSecure is FALSE.
  532. * Can also be set set to an empty string in order
  533. * to NOT specify the SameSite cookie attribute at
  534. * all and accept whatever the browser default is)
  535. *
  536. * @return void
  537. *
  538. * @since 1.4.16 and 1.5.1
  539. *
  540. */
  541. function sqsetcookie($sName, $sValue='deleted', $iExpire=0, $sPath="", $sDomain="",
  542. $bSecure=false, $bHttpOnly=true, $bReplace=false, $sSameSite=NULL) {
  543. // some environments can get overwhelmed by an excessive
  544. // setting of the same cookie over and over (e.g., many
  545. // calls to this function via sqsession_is_active() result
  546. // in repeated setting of the session cookie when $bReplace
  547. // is FALSE, but something odd happens (during login only)
  548. // if we change that to default TRUE) ... so we keep our own
  549. // naive per-request name/value cache and only set the cookie
  550. // if its value is changing (or never seen before)
  551. static $cookies = array();
  552. if (isset($cookies[$sName]) && $cookies[$sName] === $sValue)
  553. return;
  554. else
  555. $cookies[$sName] = $sValue;
  556. // if we have a secure connection then limit the cookies to https only.
  557. global $is_secure_connection;
  558. if ($sName && $is_secure_connection)
  559. $bSecure = true;
  560. // admin config can override the restriction of secure-only cookies
  561. global $only_secure_cookies;
  562. if (!$only_secure_cookies)
  563. $bSecure = false;
  564. // use global SameSite setting, but allow override
  565. // The global $same_site_cookies (for which an override value
  566. // can be specified in config/config_local.php) defaults to
  567. // "Strict" when it is NULL (when not given in the config file),
  568. // or can be manually set to "Lax" "Strict" or "None" if desired
  569. // or can be set to an empty string in order to not specify
  570. // SameSite at all and use the browser default
  571. if (is_null($sSameSite)) {
  572. global $same_site_cookies;
  573. if (is_null($same_site_cookies))
  574. $sSameSite = 'Strict';
  575. else
  576. $sSameSite = $same_site_cookies;
  577. }
  578. if (false && check_php_version(5,2)) {
  579. // php 5 supports the httponly attribute in setcookie, but because setcookie seems a bit
  580. // broken we use the header function for php 5.2 as well. We might change that later.
  581. //setcookie($sName,$sValue,(int) $iExpire,$sPath,$sDomain,$bSecure,$bHttpOnly);
  582. } else {
  583. if (!empty($sDomain)) {
  584. // Fix the domain to accept domains with and without 'www.'.
  585. if (strtolower(substr($sDomain, 0, 4)) == 'www.') $sDomain = substr($sDomain, 4);
  586. $sDomain = '.' . $sDomain;
  587. // Remove port information.
  588. $Port = strpos($sDomain, ':');
  589. if ($Port !== false) $sDomain = substr($sDomain, 0, $Port);
  590. }
  591. if (!$sValue) $sValue = 'deleted';
  592. header('Set-Cookie: ' . rawurlencode($sName) . '=' . rawurlencode($sValue)
  593. . (empty($iExpire) ? '' : '; expires=' . gmdate('D, d-M-Y H:i:s', $iExpire) . ' GMT')
  594. . (empty($sPath) ? '' : '; path=' . $sPath)
  595. . (empty($sDomain) ? '' : '; domain=' . $sDomain)
  596. . (!$bSecure ? '' : '; secure')
  597. . (!$bHttpOnly ? '' : '; HttpOnly')
  598. . (empty($sSameSite) ? '' : '; SameSite=' . $sSameSite), $bReplace);
  599. }
  600. }
  601. /**
  602. * session_regenerate_id replacement for PHP < 4.3.2
  603. *
  604. * This code is borrowed from Gallery, session.php version 1.53.2.1
  605. FIXME: I saw this code on php.net (in the manual); that's where it comes from originally, but I don't think we need it - it's just redundant to all the hard work we already did seeding the random number generator IMO. I think we can just call to GenerateRandomString() and dump the rest.
  606. */
  607. if (!function_exists('session_regenerate_id')) {
  608. function php_combined_lcg() {
  609. $tv = gettimeofday();
  610. $lcg['s1'] = $tv['sec'] ^ (~$tv['usec']);
  611. $lcg['s2'] = random_int();
  612. $q = (int) ($lcg['s1'] / 53668);
  613. $lcg['s1'] = (int) (40014 * ($lcg['s1'] - 53668 * $q) - 12211 * $q);
  614. if ($lcg['s1'] < 0) {
  615. $lcg['s1'] += 2147483563;
  616. }
  617. $q = (int) ($lcg['s2'] / 52774);
  618. $lcg['s2'] = (int) (40692 * ($lcg['s2'] - 52774 * $q) - 3791 * $q);
  619. if ($lcg['s2'] < 0) {
  620. $lcg['s2'] += 2147483399;
  621. }
  622. $z = (int) ($lcg['s1'] - $lcg['s2']);
  623. if ($z < 1) {
  624. $z += 2147483562;
  625. }
  626. return $z * 4.656613e-10;
  627. }
  628. function session_regenerate_id() {
  629. global $base_uri;
  630. $tv = gettimeofday();
  631. sqgetGlobalVar('REMOTE_ADDR',$remote_addr,SQ_SERVER);
  632. $buf = sprintf("%.15s%ld%ld%0.8f", $remote_addr, $tv['sec'], $tv['usec'], php_combined_lcg() * 10);
  633. session_id(md5($buf));
  634. if (ini_get('session.use_cookies')) {
  635. sqsetcookie(session_name(), session_id(), 0, $base_uri);
  636. }
  637. return TRUE;
  638. }
  639. }
  640. /**
  641. * php_self
  642. *
  643. * Attempts to determine the path and filename and any arguments
  644. * for the currently executing script. This is usually found in
  645. * $_SERVER['REQUEST_URI'], but some environments may differ, so
  646. * this function tries to standardize this value.
  647. *
  648. * Note that before SquirrelMail version 1.5.1, this function was
  649. * stored in function/strings.php.
  650. *
  651. * @since 1.2.3
  652. * @return string The path, filename and any arguments for the
  653. * current script
  654. */
  655. function php_self($with_query_string=TRUE) {
  656. static $request_uri = '';
  657. if (!empty($request_uri))
  658. return ($with_query_string ? $request_uri : (strpos($request_uri, '?') !== FALSE ? substr($request_uri, 0, strpos($request_uri, '?')) : $request_uri));
  659. // first try $_SERVER['PHP_SELF'], which seems most reliable
  660. // (albeit it usually won't include the query string)
  661. //
  662. $request_uri = '';
  663. if (!sqgetGlobalVar('PHP_SELF', $request_uri, SQ_SERVER)
  664. || empty($request_uri)) {
  665. // well, then let's try $_SERVER['REQUEST_URI']
  666. //
  667. $request_uri = '';
  668. if (!sqgetGlobalVar('REQUEST_URI', $request_uri, SQ_SERVER)
  669. || empty($request_uri)) {
  670. // TODO: anyone have any other ideas? maybe $_SERVER['SCRIPT_NAME']???
  671. //
  672. return '';
  673. }
  674. }
  675. // we may or may not have any query arguments, depending on
  676. // which environment variable was used above, and the PHP
  677. // version, etc., so let's check for it now
  678. //
  679. $query_string = '';
  680. if (strpos($request_uri, '?') === FALSE
  681. && sqgetGlobalVar('QUERY_STRING', $query_string, SQ_SERVER)
  682. && !empty($query_string)) {
  683. $request_uri .= '?' . $query_string;
  684. }
  685. global $php_self_pattern, $php_self_replacement;
  686. if (!empty($php_self_pattern))
  687. $request_uri = preg_replace($php_self_pattern, $php_self_replacement, $request_uri);
  688. return ($with_query_string ? $request_uri : (strpos($request_uri, '?') !== FALSE ? substr($request_uri, 0, strpos($request_uri, '?')) : $request_uri));
  689. }
  690. /**
  691. * Print variable
  692. *
  693. * sm_print_r($some_variable, [$some_other_variable [, ...]]);
  694. *
  695. * Debugging function - does the same as print_r, but makes sure special
  696. * characters are converted to htmlentities first. This will allow
  697. * values like <some@email.address> to be displayed.
  698. * The output is wrapped in <<pre>> and <</pre>> tags.
  699. * Since 1.4.2 accepts unlimited number of arguments.
  700. * @since 1.4.1
  701. * @return void
  702. */
  703. function sm_print_r() {
  704. ob_start(); // Buffer output
  705. foreach(func_get_args() as $var) {
  706. print_r($var);
  707. echo "\n";
  708. // php has get_class_methods function that can print class methods
  709. if (is_object($var)) {
  710. // get class methods if $var is object
  711. $aMethods=get_class_methods(get_class($var));
  712. // make sure that $aMethods is array and array is not empty
  713. if (is_array($aMethods) && $aMethods!=array()) {
  714. echo "Object methods:\n";
  715. foreach($aMethods as $method) {
  716. echo '* ' . $method . "\n";
  717. }
  718. }
  719. echo "\n";
  720. }
  721. }
  722. $buffer = ob_get_contents(); // Grab the print_r output
  723. ob_end_clean(); // Silently discard the output & stop buffering
  724. print '<div align="left"><pre>';
  725. print htmlentities($buffer);
  726. print '</pre></div>';
  727. }
  728. /**
  729. * Sanitize a value using sm_encode_html_special_chars() or similar, but also
  730. * recursively run sm_encode_html_special_chars() (or similar) on array keys
  731. * and values.
  732. *
  733. * If $value is not a string or an array with strings in it,
  734. * the value is returned as is.
  735. *
  736. * @param mixed $value The value to be sanitized.
  737. * @param mixed $quote_style Either boolean or an integer. If it
  738. * is an integer, it must be the PHP
  739. * constant indicating if/how to escape
  740. * quotes: ENT_QUOTES, ENT_COMPAT, or
  741. * ENT_NOQUOTES. If it is a boolean value,
  742. * it must be TRUE and thus indicates
  743. * that the only sanitizing to be done
  744. * herein is to replace single and double
  745. * quotes with &#039; and &quot;, no other
  746. * changes are made to $value. If it is
  747. * boolean and FALSE, behavior reverts
  748. * to same as if the value was ENT_QUOTES
  749. * (OPTIONAL; default is ENT_QUOTES).
  750. *
  751. * @return mixed The sanitized value.
  752. *
  753. * @since 1.5.2
  754. *
  755. **/
  756. function sq_htmlspecialchars($value, $quote_style=ENT_QUOTES) {
  757. if ($quote_style === FALSE) $quote_style = ENT_QUOTES;
  758. // array? go recursive...
  759. //
  760. if (is_array($value)) {
  761. $return_array = array();
  762. foreach ($value as $key => $val) {
  763. $return_array[sq_htmlspecialchars($key, $quote_style)]
  764. = sq_htmlspecialchars($val, $quote_style);
  765. }
  766. return $return_array;
  767. // sanitize strings only
  768. //
  769. } else if (is_string($value)) {
  770. if ($quote_style === TRUE)
  771. return str_replace(array('\'', '"'), array('&#039;', '&quot;'), $value);
  772. else
  773. return sm_encode_html_special_chars($value, $quote_style);
  774. }
  775. // anything else gets returned with no changes
  776. //
  777. return $value;
  778. }
  779. /**
  780. * Detect whether or not we have a SSL secured (HTTPS) connection
  781. * connection to the browser
  782. *
  783. * It is thought to be so if you have 'SSLOptions +StdEnvVars'
  784. * in your Apache configuration,
  785. * OR if you have HTTPS set to a non-empty value (except "off")
  786. * in your HTTP_SERVER_VARS,
  787. * OR if you have HTTP_X_FORWARDED_PROTO=https in your HTTP_SERVER_VARS,
  788. * OR if you are on port 443.
  789. *
  790. * Note: HTTP_X_FORWARDED_PROTO could be sent from the client and
  791. * therefore possibly spoofed/hackable. Thus, SquirrelMail
  792. * ignores such headers by default. The administrator
  793. * can tell SM to use such header values by setting
  794. * $sq_ignore_http_x_forwarded_headers to boolean FALSE
  795. * in config/config.php or by using config/conf.pl.
  796. *
  797. * Note: It is possible to run SSL on a port other than 443, and
  798. * if that is the case, the administrator should set
  799. * $sq_https_port in config/config.php or by using config/conf.pl.
  800. *
  801. * @return boolean TRUE if the current connection is SSL-encrypted;
  802. * FALSE otherwise.
  803. *
  804. * @since 1.4.17 and 1.5.2
  805. *
  806. */
  807. function is_ssl_secured_connection()
  808. {
  809. global $sq_ignore_http_x_forwarded_headers, $sq_https_port;
  810. $https_env_var = getenv('HTTPS');
  811. if ($sq_ignore_http_x_forwarded_headers
  812. || !sqgetGlobalVar('HTTP_X_FORWARDED_PROTO', $forwarded_proto, SQ_SERVER))
  813. $forwarded_proto = '';
  814. if (empty($sq_https_port)) // won't work with port 0 (zero)
  815. $sq_https_port = 443;
  816. if ((isset($https_env_var) && strcasecmp($https_env_var, 'on') === 0)
  817. || (sqgetGlobalVar('HTTPS', $https, SQ_SERVER) && !empty($https)
  818. && strcasecmp($https, 'off') !== 0)
  819. || (strcasecmp($forwarded_proto, 'https') === 0)
  820. || (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER)
  821. && $server_port == $sq_https_port))
  822. return TRUE;
  823. return FALSE;
  824. }
  825. /**
  826. * Endeavor to detect what user and group PHP is currently
  827. * running as. Probably only works in non-Windows environments.
  828. *
  829. * @return mixed Boolean FALSE is returned if something went wrong,
  830. * otherwise an array is returned with the following
  831. * elements:
  832. * uid The current process' UID (integer)
  833. * euid The current process' effective UID (integer)
  834. * gid The current process' GID (integer)
  835. * egid The current process' effective GID (integer)
  836. * name The current process' name/handle (string)
  837. * ename The current process' effective name/handle (string)
  838. * group The current process' group name (string)
  839. * egroup The current process' effective group name (string)
  840. * Note that some of these elements may have empty
  841. * values, especially if they could not be determined.
  842. *
  843. * @since 1.5.2
  844. *
  845. */
  846. function get_process_owner_info()
  847. {
  848. if (!function_exists('posix_getuid'))
  849. return FALSE;
  850. $process_info['uid'] = posix_getuid();
  851. $process_info['euid'] = posix_geteuid();
  852. $process_info['gid'] = posix_getgid();
  853. $process_info['egid'] = posix_getegid();
  854. $user_info = posix_getpwuid($process_info['uid']);
  855. $euser_info = posix_getpwuid($process_info['euid']);
  856. $group_info = posix_getgrgid($process_info['gid']);
  857. $egroup_info = posix_getgrgid($process_info['egid']);
  858. $process_info['name'] = $user_info['name'];
  859. $process_info['ename'] = $euser_info['name'];
  860. $process_info['group'] = $user_info['name'];
  861. $process_info['egroup'] = $euser_info['name'];
  862. return $process_info;
  863. }