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