Move sqm_baseuri() to strings.php. A file for functions that manipulate strings...
[squirrelmail.git] / functions / strings.php
index 924f2f1bb9f5a10cc001b116f3d628dbf5baba5b..f4e765541117f9e015ab568316dda9ad67f61faf 100644 (file)
@@ -3,16 +3,18 @@
 /**
  * strings.php
  *
- * Copyright (c) 1999-2004 The SquirrelMail Project Team
- * Licensed under the GNU GPL. For full terms see the file COPYING.
- *
  * This code provides various string manipulation functions that are
- * used by the rest of the Squirrelmail code.
+ * used by the rest of the SquirrelMail code.
  *
+ * @copyright © 1999-2005 The SquirrelMail Project Team
+ * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  * @version $Id$
  * @package squirrelmail
  */
 
+/** @ignore */
+if (!defined('SM_PATH')) define('SM_PATH','../');
+
 /**
  * SquirrelMail version number -- DO NOT CHANGE
  */
@@ -31,17 +33,17 @@ $SQM_INTERNAL_VERSION = array(1,5,1);
  * referenced by the include of global.php, etc. before it's defined.
  * For that reason, bring in global.php AFTER we define the version strings.
  */
-require_once(SM_PATH . 'functions/global.php');
+include_once(SM_PATH . 'functions/global.php');
 
 /**
  * Appends citation markers to the string.
  * Also appends a trailing space.
  *
  * @author Justus Pendleton
- *
- * @param string str The string to append to
- * @param int citeLevel the number of markers to append
+ * @param string $str The string to append to
+ * @param int $citeLevel the number of markers to append
  * @return null
+ * @since 1.5.1
  */
 function sqMakeCite (&$str, $citeLevel) {
     for ($i = 0; $i < $citeLevel; $i++) {
@@ -57,11 +59,11 @@ function sqMakeCite (&$str, $citeLevel) {
  * markers to the newline as necessary.
  *
  * @author Justus Pendleton
- *
- * @param string str the string to make a newline in
- * @param int citeLevel the citation level the newline is at
- * @param int column starting column of the newline
+ * @param string $str the string to make a newline in
+ * @param int $citeLevel the citation level the newline is at
+ * @param int $column starting column of the newline
  * @return null
+ * @since 1.5.1
  */
 function sqMakeNewLine (&$str, $citeLevel, &$column) {
     $str .= "\n";
@@ -77,8 +79,6 @@ function sqMakeNewLine (&$str, $citeLevel, &$column) {
 /**
  * Checks for spaces in strings - only used if PHP doesn't have native ctype support
  *
- * @author Tomas Kuliavas
- *
  * You might be able to rewrite the function by adding short evaluation form.
  *
  * possible problems:
@@ -94,13 +94,14 @@ function sqMakeNewLine (&$str, $citeLevel, &$column) {
  *
  * @param string $string tested string
  * @return bool true when only whitespace symbols are present in test string
+ * @since 1.5.1
  */
 function sm_ctype_space($string) {
-  if ( preg_match('/^[\x09-\x0D]|^\x20/', $string) || $string=='') {
-    return true;
-  } else {
-    return false;
-  }
+    if ( preg_match('/^[\x09-\x0D]|^\x20/', $string) || $string=='') {
+        return true;
+    } else {
+        return false;
+    }
 }
 
 /**
@@ -110,10 +111,10 @@ function sm_ctype_space($string) {
  * bit smarter and when and how to wrap.
  *
  * @author Justus Pendleton
- *
- * @param string body the entire body of text
- * @param int wrap the maximum line length
+ * @param string $body the entire body of text
+ * @param int $wrap the maximum line length
  * @return string the wrapped text
+ * @since 1.5.1
  */
 function &sqBodyWrap (&$body, $wrap) {
     //check for ctype support, and fake it if it doesn't exist
@@ -127,7 +128,7 @@ function &sqBodyWrap (&$body, $wrap) {
     $outString = '';
     // current column since the last newline in the outstring
     $outStringCol = 0;
-    $length = strlen($body);
+    $length = sq_strlen($body);
     // where we are in the original string
     $pos = 0;
     // the number of >>> citation markers we are currently at
@@ -139,12 +140,12 @@ function &sqBodyWrap (&$body, $wrap) {
        // we're at the beginning of a line, get the new cite level
        $newCiteLevel = 0;
 
-       while (($pos < $length) && ($body{$pos} == '>')) {
+       while (($pos < $length) && (sq_substr($body,$pos,1) == '>')) {
            $newCiteLevel++;
            $pos++;
 
            // skip over any spaces interleaved among the cite markers
-           while (($pos < $length) && ($body{$pos} == ' ')) {
+           while (($pos < $length) && (sq_substr($body,$pos,1) == ' ')) {
 
                $pos++;
 
@@ -157,8 +158,8 @@ function &sqBodyWrap (&$body, $wrap) {
        // special case: if this is a blank line then maintain it
        // (i.e. try to preserve original paragraph breaks)
        // unless they occur at the very beginning of the text
-       if (($body{$pos} == "\n" ) && (strlen($outString) != 0)) {
-           $outStringLast = $outString{strlen($outString) - 1};
+       if ((sq_substr($body,$pos,1) == "\n" ) && (sq_strlen($outString) != 0)) {
+           $outStringLast = $outString{sq_strlen($outString) - 1};
            if ($outStringLast != "\n") {
                $outString .= "\n";
            }
@@ -192,7 +193,7 @@ function &sqBodyWrap (&$body, $wrap) {
        }
 
        // find the next newline -- we don't want to go further than that
-       $nextNewline = strpos ($body, "\n", $pos);
+       $nextNewline = sq_strpos ($body, "\n", $pos);
        if ($nextNewline === FALSE) {
            $nextNewline = $length;
        }
@@ -201,7 +202,7 @@ function &sqBodyWrap (&$body, $wrap) {
        // will work fine for this.  Maybe revisit this later though
        // (for completeness more than anything else, I think)
        if ($citeLevel == 0) {
-           $outString .= substr ($body, $pos, ($nextNewline - $pos));
+           $outString .= sq_substr ($body, $pos, ($nextNewline - $pos));
            $outStringCol = $nextNewline - $pos;
            if ($nextNewline != $length) {
                sqMakeNewLine ($outString, 0, $outStringCol);
@@ -213,41 +214,36 @@ function &sqBodyWrap (&$body, $wrap) {
         * Set this to false to stop appending short strings to previous lines
         */
        $smartwrap = true;
-
        // inner loop, (obviously) handles wrapping up to
        // the next newline
        while ($pos < $nextNewline) {
            // skip over initial spaces
-           while (($pos < $nextNewline) && (ctype_space ($body{$pos}))) {
+           while (($pos < $nextNewline) && (ctype_space (sq_substr($body,$pos,1)))) {
                $pos++;
            }
-
-
            // if this is a short line then just append it and continue outer loop
            if (($outStringCol + $nextNewline - $pos) <= ($wrap - $citeLevel - 1) ) {
                // if this is the final line in the input string then include
                // any trailing newlines
                //      echo substr($body,$pos,$wrap). "<br />";
-               if (($nextNewline + 1 == $length) && ($body{$nextNewline} == "\n")) {
+               if (($nextNewline + 1 == $length) && (sq_substr($body,$nextNewline,1) == "\n")) {
                    $nextNewline++;
                }
 
                // trim trailing spaces
                $lastRealChar = $nextNewline;
-               while (($lastRealChar > $pos && $lastRealChar < $length) && (ctype_space ($body{$lastRealChar}))) {
+               while (($lastRealChar > $pos && $lastRealChar < $length) && (ctype_space (sq_substr($body,$lastRealChar,1)))) {
                    $lastRealChar--;
                }
-
                // decide if appending the short string is what we want
-               if (($nextNewline < $length && $body{$nextNewline} == "\n") &&
+               if (($nextNewline < $length && sq_substr($body,$nextNewline,1) == "\n") &&
                      isset($lastRealChar)) {
-
-                     //check the first word:
-                   $mypos = $nextNewline+1;
-                   while (($mypos < $length) && ($body{$mypos} == '>')) {
+                   $mypos = $pos;
+                   //check the first word:
+                   while (($mypos < $length) && (sq_substr($body,$mypos,1) == '>')) {
                        $mypos++;
                        // skip over any spaces interleaved among the cite markers
-                       while (($mypos < $length) && ($body{$mypos} == ' ')) {
+                       while (($mypos < $length) && (sq_substr($body,$mypos,1) == ' ')) {
                            $mypos++;
                        }
                    }
@@ -260,16 +256,15 @@ function &sqBodyWrap (&$body, $wrap) {
                      }
 */
 
-                   $firstword = substr($body,$mypos,strpos($body,' ',$mypos) - $mypos);
-
+                   $firstword = sq_substr($body,$mypos,sq_strpos($body,' ',$mypos) - $mypos);
                    //if ($dowrap || $ldnspacecnt > 1 || ($firstword && (
                    if (!$smartwrap || $firstword && (
                                         $firstword{0} == '-' ||
                                         $firstword{0} == '+' ||
                                         $firstword{0} == '*' ||
-                                        $firstword{0} == strtoupper($firstword{0}) ||
+                                        sq_substr($firstword,0,1) == sq_strtoupper(sq_substr($firstword,0,1)) ||
                                         strpos($firstword,':'))) {
-                        $outString .= substr($body,$pos,($lastRealChar - $pos+1));
+                        $outString .= sq_substr($body,$pos,($lastRealChar - $pos+1));
                         $outStringCol += ($lastRealChar - $pos);
                         sqMakeNewLine($outString,$citeLevel,$outStringCol);
                         $nextNewline++;
@@ -280,7 +275,7 @@ function &sqBodyWrap (&$body, $wrap) {
 
                }
 
-               $outString .= substr ($body, $pos, ($lastRealChar - $pos + 1));
+               $outString .= sq_substr ($body, $pos, ($lastRealChar - $pos + 1));
                $outStringCol += ($lastRealChar - $pos);
                $pos = $nextNewline + 1;
                continue;
@@ -299,7 +294,7 @@ function &sqBodyWrap (&$body, $wrap) {
 
            // start looking backwards for whitespace to break at.
            $breakPoint = $eol;
-           while (($breakPoint > $pos) && (! ctype_space ($body{$breakPoint}))) {
+           while (($breakPoint > $pos) && (! ctype_space (sq_substr($body,$breakPoint,1)))) {
                $breakPoint--;
            }
 
@@ -332,13 +327,13 @@ function &sqBodyWrap (&$body, $wrap) {
            }
 
            // skip newlines or whitespace at the beginning of the string
-           $substring = substr ($body, $pos, ($breakPoint - $pos));
+           $substring = sq_substr ($body, $pos, ($breakPoint - $pos));
            $substring = rtrim ($substring); // do rtrim and ctype_space have the same ideas about whitespace?
            $outString .= $substring;
-           $outStringCol += strlen ($substring);
+           $outStringCol += sq_strlen ($substring);
            // advance past the whitespace which caused the wrap
            $pos = $breakPoint;
-           while (($pos < $length) && (ctype_space ($body{$pos}))) {
+           while (($pos < $length) && (ctype_space (sq_substr($body,$pos,1)))) {
                $pos++;
            }
            if ($pos < $length) {
@@ -356,20 +351,23 @@ function &sqBodyWrap (&$body, $wrap) {
  * Has a problem with special HTML characters, so call this before
  * you do character translation.
  *
- * Specifically, &#039 comes up as 5 characters instead of 1.
+ * Specifically, &amp;#039; comes up as 5 characters instead of 1.
  * This should not add newlines to the end of lines.
  *
- * @param string line the line of text to wrap, by ref
- * @param int wrap the maximum line lenth
+ * @param string $line the line of text to wrap, by ref
+ * @param int $wrap the maximum line lenth
+ * @param string $charset name of charset used in $line string. Available since v.1.5.1.
  * @return void
+ * @since 1.0
  */
-function sqWordWrap(&$line, $wrap) {
+function sqWordWrap(&$line, $wrap, $charset='') {
     global $languages, $squirrelmail_language;
 
+    // Use custom wrapping function, if translation provides it
     if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
-        function_exists($languages[$squirrelmail_language]['XTRA_CODE'])) {
+        function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_wordwrap')) {
         if (mb_detect_encoding($line) != 'ASCII') {
-            $line = $languages[$squirrelmail_language]['XTRA_CODE']('wordwrap', $line, $wrap);
+            $line = call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_wordwrap', $line, $wrap);
             return;
         }
     }
@@ -388,9 +386,9 @@ function sqWordWrap(&$line, $wrap) {
     while ($i < count($words)) {
         /* Force one word to be on a line (minimum) */
         $line .= $words[$i];
-        $line_len = strlen($beginning_spaces) + strlen($words[$i]) + 2;
+        $line_len = strlen($beginning_spaces) + sq_strlen($words[$i],$charset) + 2;
         if (isset($words[$i + 1]))
-            $line_len += strlen($words[$i + 1]);
+            $line_len += sq_strlen($words[$i + 1],$charset);
         $i ++;
 
         /* Add more words (as long as they fit) */
@@ -398,7 +396,7 @@ function sqWordWrap(&$line, $wrap) {
             $line .= ' ' . $words[$i];
             $i++;
             if (isset($words[$i]))
-                $line_len += strlen($words[$i]) + 1;
+                $line_len += sq_strlen($words[$i],$charset) + 1;
             else
                 $line_len += 1;
         }
@@ -417,8 +415,9 @@ function sqWordWrap(&$line, $wrap) {
 
 /**
  * Does the opposite of sqWordWrap()
- * @param string body the text to un-wordwrap
+ * @param string $body the text to un-wordwrap
  * @return void
+ * @since 1.0
  */
 function sqUnWordWrap(&$body) {
     global $squirrelmail_language;
@@ -462,6 +461,7 @@ function sqUnWordWrap(&$body) {
  * @param string haystack full mailbox name to search
  * @param string needle the mailbox separator character
  * @return string the last part of the mailbox name
+ * @since 1.0
  */
 function readShortMailboxName($haystack, $needle) {
 
@@ -478,33 +478,28 @@ function readShortMailboxName($haystack, $needle) {
 }
 
 /**
- * php_self
+ * Find out where SquirrelMail lives and try to be smart about it.
+ * The only problem would be when SquirrelMail lives in directories
+ * called "src", "functions", or "plugins", but people who do that need
+ * to be beaten with a steel pipe anyway.
  *
- * Creates an URL for the page calling this function, using either the PHP global
- * REQUEST_URI, or the PHP global PHP_SELF with QUERY_STRING added.
- *
- * @return string the complete url for this page
+ * @return string the base uri of SquirrelMail installation.
+ * @since 1.2.6
  */
-function php_self () {
-    if ( sqgetGlobalVar('REQUEST_URI', $req_uri, SQ_SERVER) && !empty($req_uri) ) {
-      return $req_uri;
-    }
-
-    if ( sqgetGlobalVar('PHP_SELF', $php_self, SQ_SERVER) && !empty($php_self) ) {
-
-      // need to add query string to end of PHP_SELF to match REQUEST_URI
-      //
-      if ( sqgetGlobalVar('QUERY_STRING', $query_string, SQ_SERVER) && !empty($query_string) ) {
-         $php_self .= '?' . $query_string;
-      }
-
-      return $php_self;
+function sqm_baseuri(){
+    global $base_uri, $PHP_SELF;
+    /**
+     * If it is in the session, just return it.
+     */
+    if (isset($base_uri)){
+        return $base_uri;
     }
-
-    return '';
+    $dirs = array('|src/.*|', '|plugins/.*|', '|functions/.*|');
+    $repl = array('', '', '');
+    $base_uri = preg_replace($dirs, $repl, $PHP_SELF);
+    return $base_uri;
 }
 
-
 /**
  * get_location
  *
@@ -517,6 +512,7 @@ function php_self () {
  *   http://www.example.com/squirrelmail/
  *
  * @return string the base url for this SquirrelMail installation
+ * @since 1.0
  */
 function get_location () {
 
@@ -588,12 +584,23 @@ function get_location () {
  * stored in a cookie. The encryption key is generated by
  * OneTimePadCreate();
  *
- * @param string string the (password)string to encrypt
- * @param string epad the encryption key
+ * @param string $string the (password)string to encrypt
+ * @param string $epad the encryption key
  * @return string the base64-encoded encrypted password
+ * @since 1.0
  */
 function OneTimePadEncrypt ($string, $epad) {
     $pad = base64_decode($epad);
+
+    if (strlen($pad)>0) {
+        // make sure that pad is longer than string
+        while (strlen($string)>strlen($pad)) {
+            $pad.=$pad;
+        }
+    } else {
+        // FIXME: what should we do when $epad is not base64 encoded or empty.
+    }
+
     $encrypted = '';
     for ($i = 0; $i < strlen ($string); $i++) {
         $encrypted .= chr (ord($string[$i]) ^ ord($pad[$i]));
@@ -608,12 +615,23 @@ function OneTimePadEncrypt ($string, $epad) {
  * Decrypts a password from the cookie, encrypted by OneTimePadEncrypt.
  * This uses the encryption key that is stored in the session.
  *
- * @param string string the string to decrypt
- * @param string epad the encryption key from the session
+ * @param string $string the string to decrypt
+ * @param string $epad the encryption key from the session
  * @return string the decrypted password
+ * @since 1.0
  */
 function OneTimePadDecrypt ($string, $epad) {
     $pad = base64_decode($epad);
+
+    if (strlen($pad)>0) {
+        // make sure that pad is longer than string
+        while (strlen($string)>strlen($pad)) {
+            $pad.=$pad;
+        }
+    } else {
+        // FIXME: what should we do when $epad is not base64 encoded or empty.
+    }
+
     $encrypted = base64_decode ($string);
     $decrypted = '';
     for ($i = 0; $i < strlen ($encrypted); $i++) {
@@ -631,8 +649,9 @@ function OneTimePadDecrypt ($string, $epad) {
  * 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
+ * @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,
@@ -647,7 +666,7 @@ function sq_mt_seed($Val) {
         $Val *= -1;
     }
 
-    if ($Val = 0) {
+    if ($Val == 0) {
         return;
     }
 
@@ -663,6 +682,7 @@ function sq_mt_seed($Val) {
  * the same 'random' numbers twice in one session.
  *
  * @return void
+ * @since 1.0
  */
 function sq_mt_randomize() {
     static $randomized;
@@ -704,8 +724,10 @@ function sq_mt_randomize() {
  * Creates an encryption key for encrypting the password stored in the cookie.
  * The encryption key itself is stored in the session.
  *
- * @param int length optional, length of the string to generate
+ * Pad must be longer or equal to encoded string length in 1.4.4/1.5.0 and older.
+ * @param int $length optional, length of the string to generate
  * @return string the encryption key
+ * @since 1.0
  */
 function OneTimePadCreate ($length=100) {
     sq_mt_randomize();
@@ -721,8 +743,9 @@ function OneTimePadCreate ($length=100) {
 /**
  * Returns a string showing the size of the message/attachment.
  *
- * @param int bytes the filesize in bytes
+ * @param int $bytes the filesize in bytes
  * @return string the filesize in human readable format
+ * @since 1.0
  */
 function show_readable_size($bytes) {
     $bytes /= 1024;
@@ -741,20 +764,21 @@ function show_readable_size($bytes) {
         settype($bytes, 'integer');
     }
 
-    return $bytes . '<small>&nbsp;' . $type . '</small>';
+    return $bytes . '&nbsp;' . $type;
 }
 
 /**
- * Generates a random string from the caracter set you pass in
+ * Generates a random string from the character set you pass in
  *
- * @param int size the size of the string to generate
- * @param string chars a string containing the characters to use
- * @param int flags a flag to add a specific set to the characters to use:
+ * @param int $size the length of the string to generate
+ * @param string $chars a string containing the characters to use
+ * @param int $flags a flag to add a specific set to the characters to use:
  *     Flags:
  *       1 = add lowercase a-z to $chars
  *       2 = add uppercase A-Z to $chars
  *       4 = add numbers 0-9 to $chars
  * @return string the random string
+ * @since 1.0
  */
 function GenerateRandomString($size, $chars, $flags = 0) {
     if ($flags & 0x1) {
@@ -785,8 +809,9 @@ function GenerateRandomString($size, $chars, $flags = 0) {
 /**
  * Escapes special characters for use in IMAP commands.
  *
- * @param string the string to escape
+ * @param string $str the string to escape
  * @return string the escaped string
+ * @since 1.0.3
  */
 function quoteimap($str) {
     return preg_replace("/([\"\\\\])/", "\\\\$1", $str);
@@ -796,7 +821,8 @@ function quoteimap($str) {
  * Trims array
  *
  * Trims every element in the array, ie. remove the first char of each element
- * @param array array the array to trim
+ * @param array $array the array to trim
+ * @since 1.2.0
  */
 function TrimArray(&$array) {
     foreach ($array as $k => $v) {
@@ -819,39 +845,37 @@ function TrimArray(&$array) {
  *
  * 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 $url the URL to the compose page
+ * @param string $text the link text, default "Compose"
+ * @param string $target (since 1.4.3) url target
  * @return string a link to the compose page
+ * @since 1.4.2
  */
-function makeComposeLink($url, $text = null, $target='')
-{
-    global $compose_new_win,$javascript_on;
+function makeComposeLink($url, $text = null, $target='') {
+    global $compose_new_win,$javascript_on, $compose_width, $compose_height;
 
     if(!$text) {
         $text = _("Compose");
     }
 
-
     // 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);
     }
 
-
     // build the compose in new window link...
 
 
-    // if javascript is on, use onClick event to handle it
+    // if javascript is on, use onclick event to handle it
     if($javascript_on) {
         sqgetGlobalVar('base_uri', $base_uri, SQ_SESSION);
-        return '<a href="javascript:void(0)" onclick="comp_in_new(\''.$base_uri.$url.'\')">'. $text.'</a>';
+        $compuri = $base_uri.$url;
+        return "<a href=\"javascript:void(0)\" onclick=\"comp_in_new('$compuri','$compose_width','$compose_height')\">$text</a>";
     }
 
-
     // otherwise, just open new window using regular HTML
     return makeInternalLink($url, $text, '_blank');
-
 }
 
 /**
@@ -863,7 +887,8 @@ function makeComposeLink($url, $text = null, $target='')
  * characters are converted to htmlentities first.  This will allow
  * values like <some@email.address> to be displayed.
  * The output is wrapped in <<pre>> and <</pre>> tags.
- *
+ * Since 1.4.2 accepts unlimited number of arguments.
+ * @since 1.4.1
  * @return void
  */
 function sm_print_r() {
@@ -871,16 +896,33 @@ function sm_print_r() {
     foreach(func_get_args() as $var) {
         print_r($var);
         echo "\n";
+        // php has get_class_methods function that can print class methods
+        if (is_object($var)) {
+            // get class methods if $var is object
+            $aMethods=get_class_methods(get_class($var));
+            // make sure that $aMethods is array and array is not empty
+            if (is_array($aMethods) && $aMethods!=array()) {
+                echo "Object methods:\n";
+                foreach($aMethods as $method) {
+                    echo '* ' . $method . "\n";
+                }
+            }
+            echo "\n";
+        }
     }
     $buffer = ob_get_contents(); // Grab the print_r output
     ob_end_clean();  // Silently discard the output & stop buffering
-    print '<pre>';
+    print '<div align="left"><pre>';
     print htmlentities($buffer);
-    print '</pre>';
+    print '</pre></div>';
 }
 
 /**
  * version of fwrite which checks for failure
+ * @param resource $fp
+ * @param string $string
+ * @return number of written bytes. false on failure
+ * @since 1.4.3
  */
 function sq_fwrite($fp, $string) {
         // write to file
@@ -911,6 +953,7 @@ function sq_fwrite($fp, $string) {
  *              </ul>
  * @param string $charset charset used for encoding. default to us-ascii, 'auto' uses $default_charset global value.
  * @return array html translation array
+ * @since 1.5.1
  */
 function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='us-ascii') {
   global $default_charset;
@@ -960,7 +1003,10 @@ function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='
  * sq_htmlentities
  *
  * Convert all applicable characters to HTML entities.
- * Minimal php requirement - v.4.0.5
+ * Minimal php requirement - v.4.0.5.
+ *
+ * Function is designed for people that want to use full power of htmlentities() in
+ * i18n environment.
  *
  * @param string $string string that has to be sanitized
  * @param integer $quote_style quote encoding style. Possible values (without quotes):
@@ -971,6 +1017,7 @@ function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='
  *              </ul>
  * @param string $charset charset used for encoding. defaults to 'us-ascii', 'auto' uses $default_charset global value.
  * @return string sanitized string
+ * @since 1.5.1
  */
 function sq_htmlentities($string,$quote_style=ENT_COMPAT,$charset='us-ascii') {
   // get translation table
@@ -979,5 +1026,299 @@ function sq_htmlentities($string,$quote_style=ENT_COMPAT,$charset='us-ascii') {
   return str_replace(array_keys($sq_html_ent_table),array_values($sq_html_ent_table),$string);
 }
 
-$PHP_SELF = php_self();
-?>
\ No newline at end of file
+/**
+ * Tests if string contains 8bit symbols.
+ *
+ * If charset is not set, function defaults to default_charset.
+ * $default_charset global must be set correctly if $charset is
+ * not used.
+ * @param string $string tested string
+ * @param string $charset charset used in a string
+ * @return bool true if 8bit symbols are detected
+ * @since 1.5.1 and 1.4.4
+ */
+function sq_is8bit($string,$charset='') {
+    global $default_charset;
+
+    if ($charset=='') $charset=$default_charset;
+
+    /**
+     * Don't use \240 in ranges. Sometimes RH 7.2 doesn't like it.
+     * Don't use \200-\237 for iso-8859-x charsets. This range
+     * stores control symbols in those charsets.
+     * Use preg_match instead of ereg in order to avoid problems
+     * with mbstring overloading
+     */
+    if (preg_match("/^iso-8859/i",$charset)) {
+        $needle='/\240|[\241-\377]/';
+    } else {
+        $needle='/[\200-\237]|\240|[\241-\377]/';
+    }
+    return preg_match("$needle",$string);
+}
+
+/**
+ * Replacement of mb_list_encodings function
+ *
+ * This function provides replacement for function that is available only
+ * in php 5.x. Function does not test all mbstring encodings. Only the ones
+ * that might be used in SM translations.
+ *
+ * Supported strings are stored in session in order to reduce number of
+ * mb_internal_encoding function calls.
+ *
+ * If you want to test all mbstring encodings - fill $list_of_encodings
+ * array.
+ * @return array list of encodings supported by php mbstring extension
+ * @since 1.5.1 and 1.4.6
+ */
+function sq_mb_list_encodings() {
+    if (! function_exists('mb_internal_encoding'))
+        return array();
+
+    // php 5+ function
+    if (function_exists('mb_list_encodings')) {
+        $ret = mb_list_encodings();
+        array_walk($ret,'sq_lowercase_array_vals');
+        return $ret;
+    }
+
+    // don't try to test encodings, if they are already stored in session
+    if (sqgetGlobalVar('mb_supported_encodings',$mb_supported_encodings,SQ_SESSION))
+        return $mb_supported_encodings;
+
+    // save original encoding
+    $orig_encoding=mb_internal_encoding();
+
+    $list_of_encoding=array(
+        'pass',
+        'auto',
+        'ascii',
+        'jis',
+        'utf-8',
+        'sjis',
+        'euc-jp',
+        'iso-8859-1',
+        'iso-8859-2',
+        'iso-8859-7',
+        'iso-8859-9',
+        'iso-8859-15',
+        'koi8-r',
+        'koi8-u',
+        'big5',
+        'gb2312',
+        'gb18030',
+        'windows-1251',
+        'windows-1255',
+        'windows-1256',
+        'tis-620',
+        'iso-2022-jp',
+        'euc-cn',
+        'euc-kr',
+        'euc-tw',
+        'uhc',
+        'utf7-imap');
+
+    $supported_encodings=array();
+
+    foreach ($list_of_encoding as $encoding) {
+        // try setting encodings. suppress warning messages
+        if (@mb_internal_encoding($encoding))
+            $supported_encodings[]=$encoding;
+    }
+
+    // restore original encoding
+    mb_internal_encoding($orig_encoding);
+
+    // register list in session
+    sqsession_register($supported_encodings,'mb_supported_encodings');
+
+    return $supported_encodings;
+}
+
+/**
+ * Callback function used to lowercase array values.
+ * @param string $val array value
+ * @param mixed $key array key
+ * @since 1.5.1 and 1.4.6
+ */
+function sq_lowercase_array_vals(&$val,$key) {
+    $val = strtolower($val);
+}
+
+
+/**
+ * Function returns number of characters in string.
+ *
+ * Returned number might be different from number of bytes in string,
+ * if $charset is multibyte charset. Detection depends on mbstring
+ * functions. If mbstring does not support tested multibyte charset,
+ * vanilla string length function is used.
+ * @param string $str string
+ * @param string $charset charset
+ * @since 1.5.1 and 1.4.6
+ * @return integer number of characters in string
+ */
+function sq_strlen($str, $charset=null){
+    // default option
+    if (is_null($charset)) return strlen($str);
+
+    // lowercase charset name
+    $charset=strtolower($charset);
+
+    // use automatic charset detection, if function call asks for it
+    if ($charset=='auto') {
+        global $default_charset;
+        set_my_charset();
+        $charset=$default_charset;
+    }
+
+    // Use mbstring only with listed charsets
+    $aList_of_mb_charsets=array('utf-8','big5','gb2312','gb18030','euc-jp','euc-cn','euc-tw','euc-kr');
+
+    // calculate string length according to charset
+    if (in_array($charset,$aList_of_mb_charsets) && in_array($charset,sq_mb_list_encodings())) {
+        $real_length = mb_strlen($str,$charset);
+    } else {
+        // own strlen detection code is removed because missing strpos,
+        // strtoupper and substr implementations break string wrapping.
+        $real_length=strlen($str);
+    }
+    return $real_length;
+}
+
+/**
+ * string padding with multibyte support
+ *
+ * @link http://www.php.net/str_pad
+ * @param string $string original string
+ * @param integer $width padded string width
+ * @param string $pad padding symbols
+ * @param integer $padtype padding type
+ *  (internal php defines, see str_pad() description)
+ * @param string $charset charset used in original string
+ * @return string padded string
+ */
+function sq_str_pad($string, $width, $pad, $padtype, $charset='') {
+
+    $charset = strtolower($charset);
+    $padded_string = '';
+
+    switch ($charset) {
+    case 'utf-8':
+    case 'big5':
+    case 'gb2312':
+    case 'euc-kr':
+        /*
+         * all multibyte charsets try to increase width value by
+         * adding difference between number of bytes and real length
+         */
+        $width = $width - sq_strlen($string,$charset) + strlen($string);
+    default:
+        $padded_string=str_pad($string,$width,$pad,$padtype);
+    }
+    return $padded_string;
+}
+
+/**
+ * Wrapper that is used to switch between vanilla and multibyte substr
+ * functions.
+ * @param string $string
+ * @param integer $start
+ * @param integer $length
+ * @param string $charset
+ * @return string
+ * @since 1.5.1
+ * @link http://www.php.net/substr
+ * @link http://www.php.net/mb_substr
+ */
+function sq_substr($string,$start,$length,$charset='auto') {
+    // use automatic charset detection, if function call asks for it
+    if ($charset=='auto') {
+        global $default_charset;
+        set_my_charset();
+        $charset=$default_charset;
+    }
+    $charset = strtolower($charset);
+    if (function_exists('mb_internal_encoding') &&
+        in_array($charset,sq_mb_list_encodings())) {
+        return mb_substr($string,$start,$length,$charset);
+    }
+    // TODO: add mbstring independent code
+
+    // use vanilla string functions as last option
+    return substr($string,$start,$length);
+}
+
+/**
+ * Wrapper that is used to switch between vanilla and multibyte strpos
+ * functions.
+ * @param string $haystack
+ * @param mixed $needle
+ * @param integer $offset
+ * @param string $charset
+ * @return string
+ * @since 1.5.1
+ * @link http://www.php.net/strpos
+ * @link http://www.php.net/mb_strpos
+ */
+function sq_strpos($haystack,$needle,$offset,$charset='auto') {
+    // use automatic charset detection, if function call asks for it
+    if ($charset=='auto') {
+        global $default_charset;
+        set_my_charset();
+        $charset=$default_charset;
+    }
+    $charset = strtolower($charset);
+    if (function_exists('mb_internal_encoding') &&
+        in_array($charset,sq_mb_list_encodings())) {
+        return mb_strpos($haystack,$needle,$offset,$charset);
+    }
+    // TODO: add mbstring independent code
+
+    // use vanilla string functions as last option
+    return strpos($haystack,$needle,$offset);
+}
+
+/**
+ * Wrapper that is used to switch between vanilla and multibyte strtoupper
+ * functions.
+ * @param string $string
+ * @param string $charset
+ * @return string
+ * @since 1.5.1
+ * @link http://www.php.net/strtoupper
+ * @link http://www.php.net/mb_strtoupper
+ */
+function sq_strtoupper($string,$charset='auto') {
+    // use automatic charset detection, if function call asks for it
+    if ($charset=='auto') {
+        global $default_charset;
+        set_my_charset();
+        $charset=$default_charset;
+    }
+    $charset = strtolower($charset);
+    if (function_exists('mb_strtoupper') &&
+        in_array($charset,sq_mb_list_encodings())) {
+        return mb_strtoupper($string,$charset);
+    }
+    // TODO: add mbstring independent code
+
+    // use vanilla string functions as last option
+    return strtoupper($string);
+}
+
+/**
+ * Counts 8bit bytes in string
+ * @param string $string tested string
+ * @return integer number of 8bit bytes
+ */
+function sq_count8bit($string) {
+    $count=0;
+    for ($i=0; $i<strlen($string); $i++) {
+        if (ord($string[$i]) > 127) $count++;
+    }
+    return $count;
+}
+
+?>