Fix wrong doc
[squirrelmail.git] / functions / strings.php
index 757ebdb2ff67d2285e27d7abfd975465488cb389..b67eed3d6685072585cd77e908919b1994ce18b7 100644 (file)
@@ -6,7 +6,7 @@
  * This code provides various string manipulation functions that are
  * used by the rest of the SquirrelMail code.
  *
- * @copyright © 1999-2006 The SquirrelMail Project Team
+ * @copyright 1999-2009 The SquirrelMail Project Team
  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
@@ -349,12 +349,12 @@ function sqWordWrap(&$line, $wrap, $charset='') {
         }
     }
 
-    ereg("^([\t >]*)([^\t >].*)?$", $line, $regs);
+    preg_match('/^([\t >]*)([^\t >].*)?$/', $line, $regs);
     $beginning_spaces = $regs[1];
     if (isset($regs[2])) {
         $words = explode(' ', $regs[2]);
     } else {
-        $words = '';
+        $words = array();
     }
 
     $i = 0;
@@ -470,7 +470,8 @@ function readShortMailboxName($haystack, $needle) {
  */
 function get_location () {
 
-    global $imap_server_type, $config_location_base;
+    global $imap_server_type, $config_location_base,
+           $is_secure_connection, $sq_ignore_http_x_forwarded_headers;
 
     /* Get the path, handle virtual directories */
     if(strpos(php_self(), '?')) {
@@ -492,21 +493,13 @@ function get_location () {
 
     /* Check if this is a HTTPS or regular HTTP request. */
     $proto = 'http://';
-
-    /*
-     * If you have 'SSLOptions +StdEnvVars' in your apache config
-     *     OR if you have HTTPS=on in your HTTP_SERVER_VARS
-     *     OR if you are on port 443
-     */
-    $getEnvVar = getenv('HTTPS');
-    if ((isset($getEnvVar) && strcasecmp($getEnvVar, 'on') === 0) ||
-        (sqgetGlobalVar('HTTPS', $https_on, SQ_SERVER) && strcasecmp($https_on, 'on') === 0) ||
-        (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER) &&  $server_port == 443)) {
+    if ($is_secure_connection)
         $proto = 'https://';
-    }
 
     /* Get the hostname from the Host header or server config. */
-    if ( !sqgetGlobalVar('HTTP_X_FORWARDED_HOST', $host, SQ_SERVER) || empty($host) ) {
+    if ($sq_ignore_http_x_forwarded_headers
+     || !sqgetGlobalVar('HTTP_X_FORWARDED_HOST', $host, SQ_SERVER)
+     || empty($host)) {
         if ( !sqgetGlobalVar('HTTP_HOST', $host, SQ_SERVER) || empty($host) ) {
             if ( !sqgetGlobalVar('SERVER_NAME', $host, SQ_SERVER) || empty($host) ) {
                 $host = '';
@@ -516,9 +509,20 @@ function get_location () {
 
     $port = '';
     if (! strstr($host, ':')) {
+        // Note: HTTP_X_FORWARDED_PROTO could be sent from the client and
+        //       therefore possibly spoofed/hackable.  Thus, SquirrelMail
+        //       ignores such headers by default.  The administrator
+        //       can tell SM to use such header values by setting
+        //       $sq_ignore_http_x_forwarded_headers to boolean FALSE
+        //       in config/config.php or by using config/conf.pl.
+        global $sq_ignore_http_x_forwarded_headers;
+        if ($sq_ignore_http_x_forwarded_headers
+         || !sqgetGlobalVar('HTTP_X_FORWARDED_PROTO', $forwarded_proto, SQ_SERVER))
+            $forwarded_proto = '';
         if (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER)) {
             if (($server_port != 80 && $proto == 'http://') ||
-                ($server_port != 443 && $proto == 'https://')) {
+                ($server_port != 443 && $proto == 'https://' &&
+                 strcasecmp($forwarded_proto, 'https') !== 0)) {
                 $port = sprintf(':%d', $server_port);
             }
         }
@@ -540,6 +544,32 @@ function get_location () {
 }
 
 
+/**
+ * Get Message List URI
+ *
+ * @param string $mailbox      Current mailbox name (unencoded/raw)
+ * @param string $startMessage The mailbox page offset
+ * @param string $what         Any current search parameters (OPTIONAL; 
+ *                             default empty string)
+ *
+ * @return string The message list URI
+ *
+ * @since 1.5.2
+ *
+ */
+function get_message_list_uri($mailbox, $startMessage, $what='') {
+
+    global $base_uri;
+
+    $urlMailbox = urlencode($mailbox);
+
+    $list_xtra = "?where=read_body.php&what=$what&mailbox=" . $urlMailbox.
+                 "&startMessage=$startMessage";
+
+    return $base_uri .'src/right_main.php'. $list_xtra;
+}
+
+
 /**
  * Encrypts password
  *
@@ -604,83 +634,6 @@ function OneTimePadDecrypt ($string, $epad) {
     return $decrypted;
 }
 
-
-/**
- * Randomizes the mt_rand() function.
- *
- * Toss this in strings or integers and it will seed the generator
- * appropriately. With strings, it is better to get them long.
- * Use md5() to lengthen smaller strings.
- *
- * @param mixed $val a value to seed the random number generator. mixed = integer or string.
- * @return void
- * @since 1.0
- */
-function sq_mt_seed($Val) {
-    /* if mt_getrandmax() does not return a 2^n - 1 number,
-       this might not work well.  This uses $Max as a bitmask. */
-    $Max = mt_getrandmax();
-
-    if (! is_int($Val)) {
-            $Val = crc32($Val);
-    }
-
-    if ($Val < 0) {
-        $Val *= -1;
-    }
-
-    if ($Val == 0) {
-        return;
-    }
-
-    mt_srand(($Val ^ mt_rand(0, $Max)) & $Max);
-}
-
-
-/**
- * Init random number generator
- *
- * This function initializes the random number generator fairly well.
- * It also only initializes it once, so you don't accidentally get
- * the same 'random' numbers twice in one session.
- *
- * @return void
- * @since 1.0
- */
-function sq_mt_randomize() {
-    static $randomized;
-
-    if ($randomized) {
-        return;
-    }
-
-    /* Global. */
-    sqgetGlobalVar('REMOTE_PORT', $remote_port, SQ_SERVER);
-    sqgetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER);
-    sq_mt_seed((int)((double) microtime() * 1000000));
-    sq_mt_seed(md5($remote_port . $remote_addr . getmypid()));
-
-    /* getrusage */
-    if (function_exists('getrusage')) {
-        /* Avoid warnings with Win32 */
-        $dat = @getrusage();
-        if (isset($dat) && is_array($dat)) {
-            $Str = '';
-            foreach ($dat as $k => $v)
-                {
-                    $Str .= $k . $v;
-                }
-            sq_mt_seed(md5($Str));
-        }
-    }
-
-    if(sqgetGlobalVar('UNIQUE_ID', $unique_id, SQ_SERVER)) {
-        sq_mt_seed(md5($unique_id));
-    }
-
-    $randomized = 1;
-}
-
 /**
  * Creates encryption key
  *
@@ -693,8 +646,6 @@ function sq_mt_randomize() {
  * @since 1.0
  */
 function OneTimePadCreate ($length=100) {
-    sq_mt_randomize();
-
     $pad = '';
     for ($i = 0; $i < $length; $i++) {
         $pad .= chr(mt_rand(0,255));
@@ -704,19 +655,23 @@ function OneTimePadCreate ($length=100) {
 }
 
 /**
- * Returns a string showing the size of the message/attachment.
- *
- * @param int $bytes the filesize in bytes
- * @return string the filesize in human readable format
- * @since 1.0
- */
+  * Returns a string showing a byte size figure in
+  * a more easily digested (readable) format
+  *
+  * @param int $bytes the size in bytes
+  *
+  * @return string The size in human readable format
+  *
+  * @since 1.0
+  *
+  */
 function show_readable_size($bytes) {
     $bytes /= 1024;
-    $type = 'KiB';
+    $type = _("KiB");
 
     if ($bytes / 1024 > 1) {
         $bytes /= 1024;
-        $type = 'MiB';
+        $type = _("MiB");
     }
 
     if ($bytes < 10) {
@@ -727,7 +682,8 @@ function show_readable_size($bytes) {
         settype($bytes, 'integer');
     }
 
-    return $bytes . '&nbsp;' . $type;
+    global $nbsp;
+    return $bytes . $nbsp . $type;
 }
 
 /**
@@ -758,8 +714,6 @@ function GenerateRandomString($size, $chars, $flags = 0) {
         return '';
     }
 
-    sq_mt_randomize(); /* Initialize the random number generator */
-
     $String = '';
     $j = strlen( $chars ) - 1;
     while (strlen($String) < $size) {
@@ -785,14 +739,18 @@ function quoteimap($str) {
  *
  * Returns a link to the compose-page, taking in consideration
  * the compose_in_new and javascript settings.
- * @param string $url the URL to the compose page
- * @param string $text the link text, default "Compose"
- * @param string $target (since 1.4.3) url target
+ *
+ * @param string $url       The URL to the compose page
+ * @param string $text      The link text, default "Compose"
+ * @param string $target    URL target, if any (since 1.4.3)
+ * @param string $accesskey The access key to be used, if any
+ *
  * @return string a link to the compose page
+ *
  * @since 1.4.2
  */
-function makeComposeLink($url, $text = null, $target='') {
-    global $compose_new_win, $javascript_on, $compose_width, 
+function makeComposeLink($url, $text = null, $target='', $accesskey='NONE') {
+    global $compose_new_win, $compose_width, 
            $compose_height, $oTemplate;
 
     if(!$text) {
@@ -802,26 +760,27 @@ function makeComposeLink($url, $text = null, $target='') {
     // if not using "compose in new window", make
     // regular link and be done with it
     if($compose_new_win != '1') {
-        return makeInternalLink($url, $text, $target);
+        return makeInternalLink($url, $text, $target, $accesskey);
     }
 
     // build the compose in new window link...
 
 
     // if javascript is on, use onclick event to handle it
-    if($javascript_on) {
+    if(checkForJavascript()) {
         sqgetGlobalVar('base_uri', $base_uri, SQ_SESSION);
         $compuri = SM_BASE_URI.$url;
-        $oTemplate->assign('uri', 'javascript:void(0)');
-        $oTemplate->assign('text', $text);
-        $oTemplate->assign('onclick', "comp_in_new('$compuri','$compose_width','$compose_height')");
-        $oTemplate->assign('class', '');
-        $oTemplate->assign('id', '');
-        return $oTemplate->fetch('hyperlink.tpl');
+
+        return create_hyperlink('javascript:void(0)', $text, '',
+                                "comp_in_new('$compuri','$compose_width','$compose_height')",
+                                '', '', '',
+                                ($accesskey == 'NONE'
+                                ? array()
+                                : array('accesskey' => $accesskey)));
     }
 
     // otherwise, just open new window using regular HTML
-    return makeInternalLink($url, $text, '_blank');
+    return makeInternalLink($url, $text, '_blank', $accesskey);
 }
 
 /**
@@ -1291,3 +1250,186 @@ function sq_count8bit($string) {
 function sq_trim_value ( &$value ) {
     $value = trim($value);
 }
+
+/**
+  * Gathers the list of secuirty tokens currently
+  * stored in the user's preferences and optionally
+  * purges old ones from the list.
+  *
+  * @param boolean $purge_old Indicates if old tokens
+  *                           should be purged from the
+  *                           list ("old" is 30 days or
+  *                           older unless the administrator
+  *                           overrides that value using
+  *                           $max_security_token_age in
+  *                           config/config_local.php)
+  *                           (OPTIONAL; default is to always
+  *                           purge old tokens)
+  *
+  * @return array The list of tokens
+  *
+  * @since 1.4.19 and 1.5.2
+  *
+  */
+function sm_get_user_security_tokens($purge_old=TRUE)
+{
+
+   global $data_dir, $username, $max_token_age_days;
+
+   $tokens = getPref($data_dir, $username, 'security_tokens', '');
+   if (($tokens = unserialize($tokens)) === FALSE || !is_array($tokens))
+      $tokens = array();
+
+   // purge old tokens if necessary
+   //
+   if ($purge_old)
+   {
+      if (empty($max_token_age_days)) $max_token_age_days = 30;
+      $now = time();
+      $discard_token_date = $now - ($max_token_age_days * 86400);
+      $cleaned_tokens = array();
+      foreach ($tokens as $token => $timestamp)
+         if ($timestamp >= $discard_token_date)
+            $cleaned_tokens[$token] = $timestamp;
+      $tokens = $cleaned_tokens;
+   }
+
+   return $tokens;
+
+}
+
+/**
+  * Generates a security token that is then stored in
+  * the user's preferences with a timestamp for later
+  * verification/use.
+  *
+  * WARNING: If the administrator has turned the token system
+  *          off by setting $disable_security_tokens to TRUE in
+  *          config/config.php or the configuration tool, this
+  *          function will not store tokens in the user
+  *          preferences (but it will still generate and return
+  *          a random string).
+  *
+  * @return string A security token
+  *
+  * @since 1.4.19 and 1.5.2
+  *
+  */
+function sm_generate_security_token()
+{
+
+   global $data_dir, $username, $disable_security_tokens;
+   $max_generation_tries = 1000;
+
+   $tokens = sm_get_user_security_tokens();
+
+   $new_token = GenerateRandomString(12, '', 7);
+   $count = 0;
+   while (isset($tokens[$new_token]))
+   {
+      $new_token = GenerateRandomString(12, '', 7);
+      if (++$count > $max_generation_tries)
+      {
+         logout_error(_("Fatal token generation error; please contact your system administrator or the SquirrelMail Team"));
+         exit;
+      }
+   }
+
+   // is the token system enabled?  CAREFUL!
+   //
+   if (!$disable_security_tokens)
+   {
+      $tokens[$new_token] = time();
+      setPref($data_dir, $username, 'security_tokens', serialize($tokens));
+   }
+
+   return $new_token;
+
+}
+
+/**
+  * Validates a given security token and optionally remove it
+  * from the user's preferences if it was valid.  If the token
+  * is too old but otherwise valid, it will still be rejected.
+  *
+  * "Too old" is 30 days or older unless the administrator
+  * overrides that value using $max_security_token_age in
+  * config/config_local.php
+  *
+  * WARNING: If the administrator has turned the token system
+  *          off by setting $disable_security_tokens to TRUE in
+  *          config/config.php or the configuration tool, this
+  *          function will always return TRUE.
+  *
+  * @param string  $token           The token to validate
+  * @param int     $validity_period The number of seconds tokens are valid
+  *                                 for (set to zero to remove valid tokens
+  *                                 after only one use; use 3600 to allow
+  *                                 tokens to be reused for an hour)
+  *                                 (OPTIONAL; default is to only allow tokens
+  *                                 to be used once)
+  * @param boolean $show_error      Indicates that if the token is not
+  *                                 valid, this function should display
+  *                                 a generic error, log the user out
+  *                                 and exit - this function will never
+  *                                 return in that case.
+  *                                 (OPTIONAL; default FALSE)
+  *
+  * @return boolean TRUE if the token validated; FALSE otherwise
+  *
+  * @since 1.4.19 and 1.5.2
+  *
+  */
+function sm_validate_security_token($token, $validity_period=0, $show_error=FALSE)
+{
+
+   global $data_dir, $username, $max_token_age_days,
+          $disable_security_tokens;
+
+   // bypass token validation?  CAREFUL!
+   //
+   if ($disable_security_tokens) return TRUE;
+
+   // don't purge old tokens here because we already
+   // do it when generating tokens
+   //
+   $tokens = sm_get_user_security_tokens(FALSE);
+
+   // token not found?
+   //
+   if (empty($tokens[$token]))
+   {
+      if (!$show_error) return FALSE;
+      logout_error(_("This page request could not be verified and appears to have expired."));
+      exit;
+   }
+
+   $now = time();
+   $timestamp = $tokens[$token];
+
+   // whether valid or not, we want to remove it from
+   // user prefs if it's old enough
+   //
+   if ($timestamp < $now - $validity_period)
+   {
+      unset($tokens[$token]);
+      setPref($data_dir, $username, 'security_tokens', serialize($tokens));
+   }
+
+   // reject tokens that are too old
+   //
+   if (empty($max_token_age_days)) $max_token_age_days = 30;
+   $old_token_date = $now - ($max_token_age_days * 86400);
+   if ($timestamp < $old_token_date)
+   {
+      if (!$show_error) return FALSE;
+      logout_error(_("The current page request appears to have originated from an untrusted source."));
+      exit;
+   }
+
+   // token OK!
+   //
+   return TRUE;
+
+}
+