Source for file strings.php

Documentation is available at strings.php

  1. <?php
  2.  
  3. /**
  4.  * strings.php
  5.  *
  6.  * This code provides various string manipulation functions that are
  7.  * used by the rest of the SquirrelMail code.
  8.  *
  9.  * @copyright &copy; 1999-2006 The SquirrelMail Project Team
  10.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  11.  * @version $Id: strings.php,v 1.255 2006/09/30 16:24:00 stekkel Exp $
  12.  * @package squirrelmail
  13.  */
  14.  
  15. /**
  16.  * Appends citation markers to the string.
  17.  * Also appends a trailing space.
  18.  *
  19.  * @author Justus Pendleton
  20.  * @param string $str The string to append to
  21.  * @param int $citeLevel the number of markers to append
  22.  * @return null 
  23.  * @since 1.5.1
  24.  */
  25. function sqMakeCite (&$str, $citeLevel) {
  26.     for ($i = 0; $i < $citeLevel; $i++) {
  27.         $str .= '>';
  28.     }
  29.     if ($citeLevel != 0) {
  30.         $str .= ' ';
  31.     }
  32. }
  33.  
  34. /**
  35.  * Create a newline in the string, adding citation
  36.  * markers to the newline as necessary.
  37.  *
  38.  * @author Justus Pendleton
  39.  * @param string $str the string to make a newline in
  40.  * @param int $citeLevel the citation level the newline is at
  41.  * @param int $column starting column of the newline
  42.  * @return null 
  43.  * @since 1.5.1
  44.  */
  45. function sqMakeNewLine (&$str, $citeLevel, &$column) {
  46.     $str .= "\n";
  47.     $column = 0;
  48.     if ($citeLevel > 0) {
  49.         sqMakeCite ($str, $citeLevel);
  50.         $column = $citeLevel + 1;
  51.     } else {
  52.         $column = 0;
  53.     }
  54. }
  55.  
  56. /**
  57.  * Checks for spaces in strings - only used if PHP doesn't have native ctype support
  58.  *
  59.  * You might be able to rewrite the function by adding short evaluation form.
  60.  *
  61.  * possible problems:
  62.  *  - iso-2022-xx charsets  - hex 20 might be part of other symbol. I might
  63.  * be wrong. 0x20 is not used in iso-2022-jp. I haven't checked iso-2022-kr
  64.  * and iso-2022-cn mappings.
  65.  *
  66.  *  - no-break space (&nbsp;) - it is 8bit symbol, that depends on charset.
  67.  * there are at least three different charset groups that have nbsp in
  68.  * different places.
  69.  *
  70.  * I don't see any charset/nbsp options in php ctype either.
  71.  *
  72.  * @param string $string tested string
  73.  * @return bool true when only whitespace symbols are present in test string
  74.  * @since 1.5.1
  75.  */
  76. function sm_ctype_space($string) {
  77.     if ( preg_match('/^[\x09-\x0D]|^\x20/', $string) || $string=='') {
  78.         return true;
  79.     } else {
  80.         return false;
  81.     }
  82. }
  83.  
  84. /**
  85.  * Wraps text at $wrap characters.  While sqWordWrap takes
  86.  * a single line of text and wraps it, this function works
  87.  * on the entire corpus at once, this allows it to be a little
  88.  * bit smarter and when and how to wrap.
  89.  *
  90.  * @author Justus Pendleton
  91.  * @param string $body the entire body of text
  92.  * @param int $wrap the maximum line length
  93.  * @return string the wrapped text
  94.  * @since 1.5.1
  95.  */
  96. function &sqBodyWrap (&$body, $wrap) {
  97.     //check for ctype support, and fake it if it doesn't exist
  98.     if (!function_exists('ctype_space')) {
  99.         function ctype_space ($string) {
  100.             return sm_ctype_space($string);
  101.         }
  102.     }
  103.  
  104.     // the newly wrapped text
  105.     $outString = '';
  106.     // current column since the last newline in the outstring
  107.     $outStringCol = 0;
  108.     $length = sq_strlen($body);
  109.     // where we are in the original string
  110.     $pos = 0;
  111.     // the number of >>> citation markers we are currently at
  112.     $citeLevel = 0;
  113.  
  114.     // the main loop, whenever we start a newline of input text
  115.     // we start from here
  116.     while ($pos < $length) {
  117.        // we're at the beginning of a line, get the new cite level
  118.        $newCiteLevel = 0;
  119.  
  120.        while (($pos < $length) && (sq_substr($body,$pos,1) == '>')) {
  121.            $newCiteLevel++;
  122.            $pos++;
  123.  
  124.            // skip over any spaces interleaved among the cite markers
  125.            while (($pos < $length) && (sq_substr($body,$pos,1) == ' ')) {
  126.  
  127.                $pos++;
  128.  
  129.            }
  130.            if ($pos >= $length) {
  131.                break;
  132.            }
  133.        }
  134.  
  135.        // special case: if this is a blank line then maintain it
  136.        // (i.e. try to preserve original paragraph breaks)
  137.        // unless they occur at the very beginning of the text
  138.        if ((sq_substr($body,$pos,1) == "\n" ) && (sq_strlen($outString) != 0)) {
  139.            $outStringLast = $outString{sq_strlen($outString) - 1};
  140.            if ($outStringLast != "\n") {
  141.                $outString .= "\n";
  142.            }
  143.            sqMakeCite ($outString, $newCiteLevel);
  144.            $outString .= "\n";
  145.            $pos++;
  146.            $outStringCol = 0;
  147.            continue;
  148.        }
  149.  
  150.        // if the cite level has changed, then start a new line
  151.        // with the new cite level.
  152.        if (($citeLevel != $newCiteLevel) && ($pos > ($newCiteLevel + 1)) && ($outStringCol != 0)) {
  153.            sqMakeNewLine ($outString, 0, $outStringCol);
  154.        }
  155.  
  156.        $citeLevel = $newCiteLevel;
  157.  
  158.        // prepend the quote level if necessary
  159.        if ($outStringCol == 0) {
  160.            sqMakeCite ($outString, $citeLevel);
  161.            // if we added a citation then move the column
  162.            // out by citelevel + 1 (the cite markers + the space)
  163.            $outStringCol = $citeLevel + ($citeLevel ? 1 : 0);
  164.        } else if ($outStringCol > $citeLevel) {
  165.            // not a cite and we're not at the beginning of a line
  166.            // in the output.  add a space to separate the new text
  167.            // from previous text.
  168.            $outString .= ' ';
  169.            $outStringCol++;
  170.        }
  171.  
  172.        // find the next newline -- we don't want to go further than that
  173.        $nextNewline = sq_strpos ($body, "\n", $pos);
  174.        if ($nextNewline === FALSE) {
  175.            $nextNewline = $length;
  176.        }
  177.  
  178.        // Don't wrap unquoted lines at all.  For now the textarea
  179.        // will work fine for this.  Maybe revisit this later though
  180.        // (for completeness more than anything else, I think)
  181.        if ($citeLevel == 0) {
  182.            $outString .= sq_substr ($body, $pos, ($nextNewline - $pos));
  183.            $outStringCol = $nextNewline - $pos;
  184.            if ($nextNewline != $length) {
  185.                sqMakeNewLine ($outString, 0, $outStringCol);
  186.            }
  187.            $pos = $nextNewline + 1;
  188.            continue;
  189.        }
  190.        /**
  191.         * Set this to false to stop appending short strings to previous lines
  192.         */
  193.        $smartwrap = true;
  194.        // inner loop, (obviously) handles wrapping up to
  195.        // the next newline
  196.        while ($pos < $nextNewline) {
  197.            // skip over initial spaces
  198.            while (($pos < $nextNewline) && (ctype_space (sq_substr($body,$pos,1)))) {
  199.                $pos++;
  200.            }
  201.            // if this is a short line then just append it and continue outer loop
  202.            if (($outStringCol + $nextNewline - $pos) <= ($wrap - $citeLevel - 1) ) {
  203.                // if this is the final line in the input string then include
  204.                // any trailing newlines
  205.                //      echo substr($body,$pos,$wrap). "<br />";
  206.                if (($nextNewline + 1 == $length) && (sq_substr($body,$nextNewline,1) == "\n")) {
  207.                    $nextNewline++;
  208.                }
  209.  
  210.                // trim trailing spaces
  211.                $lastRealChar = $nextNewline;
  212.                while (($lastRealChar > $pos && $lastRealChar < $length) && (ctype_space (sq_substr($body,$lastRealChar,1)))) {
  213.                    $lastRealChar--;
  214.                }
  215.                // decide if appending the short string is what we want
  216.                if (($nextNewline < $length && sq_substr($body,$nextNewline,1) == "\n") &&
  217.                      isset($lastRealChar)) {
  218.                    $mypos = $pos;
  219.                    //check the first word:
  220.                    while (($mypos < $length) && (sq_substr($body,$mypos,1) == '>')) {
  221.                        $mypos++;
  222.                        // skip over any spaces interleaved among the cite markers
  223.                        while (($mypos < $length) && (sq_substr($body,$mypos,1) == ' ')) {
  224.                            $mypos++;
  225.                        }
  226.                    }
  227. /*
  228.                      $ldnspacecnt = 0;
  229.                      if ($mypos == $nextNewline+1) {
  230.                         while (($mypos < $length) && ($body{$mypos} == ' ')) {
  231.                          $ldnspacecnt++;
  232.                         }
  233.                      }
  234. */
  235.  
  236.                    $firstword = sq_substr($body,$mypos,sq_strpos($body,' ',$mypos) - $mypos);
  237.                    //if ($dowrap || $ldnspacecnt > 1 || ($firstword && (
  238.                    if (!$smartwrap || $firstword && (
  239.                                         $firstword{0} == '-' ||
  240.                                         $firstword{0} == '+' ||
  241.                                         $firstword{0} == '*' ||
  242.                                         sq_substr($firstword,0,1) == sq_strtoupper(sq_substr($firstword,0,1)) ||
  243.                                         strpos($firstword,':'))) {
  244.                         $outString .= sq_substr($body,$pos,($lastRealChar - $pos+1));
  245.                         $outStringCol += ($lastRealChar - $pos);
  246.                         sqMakeNewLine($outString,$citeLevel,$outStringCol);
  247.                         $nextNewline++;
  248.                         $pos = $nextNewline;
  249.                         $outStringCol--;
  250.                         continue;
  251.                    }
  252.  
  253.                }
  254.  
  255.                $outString .= sq_substr ($body, $pos, ($lastRealChar - $pos + 1));
  256.                $outStringCol += ($lastRealChar - $pos);
  257.                $pos = $nextNewline + 1;
  258.                continue;
  259.            }
  260.  
  261.            $eol = $pos + $wrap - $citeLevel - $outStringCol;
  262.            // eol is the tentative end of line.
  263.            // look backwards for there for a whitespace to break at.
  264.            // if it's already less than our current position then
  265.            // our current line is already too long, break immediately
  266.            // and restart outer loop
  267.            if ($eol <= $pos) {
  268.                sqMakeNewLine ($outString, $citeLevel, $outStringCol);
  269.                continue;
  270.            }
  271.  
  272.            // start looking backwards for whitespace to break at.
  273.            $breakPoint = $eol;
  274.            while (($breakPoint > $pos) && (! ctype_space (sq_substr($body,$breakPoint,1)))) {
  275.                $breakPoint--;
  276.            }
  277.  
  278.            // if we didn't find a breakpoint by looking backward then we
  279.            // need to figure out what to do about that
  280.            if ($breakPoint == $pos) {
  281.                // if we are not at the beginning then end this line
  282.                // and start a new loop
  283.                if ($outStringCol > ($citeLevel + 1)) {
  284.                    sqMakeNewLine ($outString, $citeLevel, $outStringCol);
  285.                    continue;
  286.                } else {
  287.                    // just hard break here.  most likely we are breaking
  288.                    // a really long URL.  could also try searching
  289.                    // forward for a break point, which is what Mozilla
  290.                    // does.  don't bother for now.
  291.                    $breakPoint = $eol;
  292.                }
  293.            }
  294.  
  295.            // special case: maybe we should have wrapped last
  296.            // time.  if the first breakpoint here makes the
  297.            // current line too long and there is already text on
  298.            // the current line, break and loop again if at
  299.            // beginning of current line, don't force break
  300.            $SLOP = 6;
  301.            if ((($outStringCol + ($breakPoint - $pos)) > ($wrap + $SLOP)) && ($outStringCol > ($citeLevel + 1))) {
  302.                sqMakeNewLine ($outString, $citeLevel, $outStringCol);
  303.                continue;
  304.            }
  305.  
  306.            // skip newlines or whitespace at the beginning of the string
  307.            $substring = sq_substr ($body, $pos, ($breakPoint - $pos));
  308.            $substring = rtrim ($substring); // do rtrim and ctype_space have the same ideas about whitespace?
  309.            $outString .= $substring;
  310.            $outStringCol += sq_strlen ($substring);
  311.            // advance past the whitespace which caused the wrap
  312.            $pos = $breakPoint;
  313.            while (($pos < $length) && (ctype_space (sq_substr($body,$pos,1)))) {
  314.                $pos++;
  315.            }
  316.            if ($pos < $length) {
  317.                sqMakeNewLine ($outString, $citeLevel, $outStringCol);
  318.            }
  319.        }
  320.     }
  321.  
  322.     return $outString;
  323. }
  324.  
  325. /**
  326.  * Wraps text at $wrap characters
  327.  *
  328.  * Has a problem with special HTML characters, so call this before
  329.  * you do character translation.
  330.  *
  331.  * Specifically, &amp;#039; comes up as 5 characters instead of 1.
  332.  * This should not add newlines to the end of lines.
  333.  *
  334.  * @param string $line the line of text to wrap, by ref
  335.  * @param int $wrap the maximum line lenth
  336.  * @param string $charset name of charset used in $line string. Available since v.1.5.1.
  337.  * @return void 
  338.  * @since 1.0
  339.  */
  340. function sqWordWrap(&$line, $wrap, $charset='') {
  341.     global $languages, $squirrelmail_language;
  342.  
  343.     // Use custom wrapping function, if translation provides it
  344.     if (isset($languages[$squirrelmail_language]['XTRA_CODE']) &&
  345.         function_exists($languages[$squirrelmail_language]['XTRA_CODE'] . '_wordwrap')) {
  346.         if (mb_detect_encoding($line) != 'ASCII') {
  347.             $line = call_user_func($languages[$squirrelmail_language]['XTRA_CODE'] . '_wordwrap', $line, $wrap);
  348.             return;
  349.         }
  350.     }
  351.  
  352.     ereg("^([\t >]*)([^\t >].*)?$", $line, $regs);
  353.     $beginning_spaces = $regs[1];
  354.     if (isset($regs[2])) {
  355.         $words = explode(' ', $regs[2]);
  356.     } else {
  357.         $words = '';
  358.     }
  359.  
  360.     $i = 0;
  361.     $line = $beginning_spaces;
  362.  
  363.     while ($i < count($words)) {
  364.         /* Force one word to be on a line (minimum) */
  365.         $line .= $words[$i];
  366.         $line_len = strlen($beginning_spaces) + sq_strlen($words[$i],$charset) + 2;
  367.         if (isset($words[$i + 1]))
  368.             $line_len += sq_strlen($words[$i + 1],$charset);
  369.         $i ++;
  370.  
  371.         /* Add more words (as long as they fit) */
  372.         while ($line_len < $wrap && $i < count($words)) {
  373.             $line .= ' ' . $words[$i];
  374.             $i++;
  375.             if (isset($words[$i]))
  376.                 $line_len += sq_strlen($words[$i],$charset) + 1;
  377.             else
  378.                 $line_len += 1;
  379.         }
  380.  
  381.         /* Skip spaces if they are the first thing on a continued line */
  382.         while (!isset($words[$i]) && $i < count($words)) {
  383.             $i ++;
  384.         }
  385.  
  386.         /* Go to the next line if we have more to process */
  387.         if ($i < count($words)) {
  388.             $line .= "\n";
  389.         }
  390.     }
  391. }
  392.  
  393. /**
  394.  * Does the opposite of sqWordWrap()
  395.  * @param string $body the text to un-wordwrap
  396.  * @return void 
  397.  * @since 1.0
  398.  */
  399. function sqUnWordWrap(&$body) {
  400.     global $squirrelmail_language;
  401.  
  402.     if ($squirrelmail_language == 'ja_JP') {
  403.         return;
  404.     }
  405.  
  406.     $lines = explode("\n", $body);
  407.     $body = '';
  408.     $PreviousSpaces = '';
  409.     $cnt = count($lines);
  410.     for ($i = 0; $i < $cnt; $i ++) {
  411.         preg_match("/^([\t >]*)([^\t >].*)?$/", $lines[$i], $regs);
  412.         $CurrentSpaces = $regs[1];
  413.         if (isset($regs[2])) {
  414.             $CurrentRest = $regs[2];
  415.         } else {
  416.             $CurrentRest = '';
  417.         }
  418.  
  419.         if ($i == 0) {
  420.             $PreviousSpaces = $CurrentSpaces;
  421.             $body = $lines[$i];
  422.         } else if (($PreviousSpaces == $CurrentSpaces) /* Do the beginnings match */
  423.                    && (strlen($lines[$i - 1]) > 65)    /* Over 65 characters long */
  424.                    && strlen($CurrentRest)) {          /* and there's a line to continue with */
  425.             $body .= ' ' . $CurrentRest;
  426.         } else {
  427.             $body .= "\n" . $lines[$i];
  428.             $PreviousSpaces = $CurrentSpaces;
  429.         }
  430.     }
  431.     $body .= "\n";
  432. }
  433.  
  434. /**
  435.  * If $haystack is a full mailbox name and $needle is the mailbox
  436.  * separator character, returns the last part of the mailbox name.
  437.  *
  438.  * @param string haystack full mailbox name to search
  439.  * @param string needle the mailbox separator character
  440.  * @return string the last part of the mailbox name
  441.  * @since 1.0
  442.  */
  443. function readShortMailboxName($haystack, $needle) {
  444.  
  445.     if ($needle == '') {
  446.         $elem = $haystack;
  447.     } else {
  448.         $parts = explode($needle, $haystack);
  449.         $elem = array_pop($parts);
  450.         while ($elem == '' && count($parts)) {
  451.             $elem = array_pop($parts);
  452.         }
  453.     }
  454.     return( $elem );
  455. }
  456.  
  457.  
  458. /**
  459.  * get_location
  460.  *
  461.  * Determines the location to forward to, relative to your server.
  462.  * This is used in HTTP Location: redirects.
  463.  *
  464.  * If set, it uses $config_location_base as the first part of the URL,
  465.  * specifically, the protocol, hostname and port parts. The path is
  466.  * always autodetected.
  467.  *
  468.  * @return string the base url for this SquirrelMail installation
  469.  * @since 1.0
  470.  */
  471. function get_location () {
  472.  
  473.  
  474.     /* Get the path, handle virtual directories */
  475.     if(strpos(php_self(), '?')) {
  476.         $path = substr(php_self(), 0, strpos(php_self(), '?'));
  477.     } else {
  478.         $path = php_self();
  479.     }
  480.     $path = substr($path, 0, strrpos($path, '/'));
  481.  
  482.     // proto+host+port are already set in config:
  483.     if ( !empty($config_location_base) ) {
  484.         return $config_location_base . $path ;
  485.     }
  486.     // we computed it before, get it from the session:
  487.     if ( sqgetGlobalVar('sq_base_url', $full_url, SQ_SESSION) ) {
  488.       return $full_url . $path;
  489.     }
  490.     // else: autodetect
  491.  
  492.     /* Check if this is a HTTPS or regular HTTP request. */
  493.     $proto = 'http://';
  494.  
  495.     /*
  496.      * If you have 'SSLOptions +StdEnvVars' in your apache config
  497.      *     OR if you have HTTPS=on in your HTTP_SERVER_VARS
  498.      *     OR if you are on port 443
  499.      */
  500.     $getEnvVar = getenv('HTTPS');
  501.     if ((isset($getEnvVar) && strcasecmp($getEnvVar, 'on') === 0) ||
  502.         (sqgetGlobalVar('HTTPS', $https_on, SQ_SERVER) && strcasecmp($https_on, 'on') === 0) ||
  503.         (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER) &&  $server_port == 443)) {
  504.         $proto = 'https://';
  505.     }
  506.  
  507.     /* Get the hostname from the Host header or server config. */
  508.     if ( !sqgetGlobalVar('HTTP_X_FORWARDED_HOST', $host, SQ_SERVER) || empty($host) ) {
  509.         if ( !sqgetGlobalVar('HTTP_HOST', $host, SQ_SERVER) || empty($host) ) {
  510.             if ( !sqgetGlobalVar('SERVER_NAME', $host, SQ_SERVER) || empty($host) ) {
  511.                 $host = '';
  512.             }
  513.         }
  514.     }
  515.  
  516.     $port = '';
  517.     if (! strstr($host, ':')) {
  518.         if (sqgetGlobalVar('SERVER_PORT', $server_port, SQ_SERVER)) {
  519.             if (($server_port != 80 && $proto == 'http://') ||
  520.                 ($server_port != 443 && $proto == 'https://')) {
  521.                 $port = sprintf(':%d', $server_port);
  522.             }
  523.         }
  524.     }
  525.  
  526.     /* this is a workaround for the weird macosx caching that
  527.      * causes Apache to return 16080 as the port number, which causes
  528.      * SM to bail */
  529.  
  530.     if ($imap_server_type == 'macosx' && $port == ':16080') {
  531.         $port = '';
  532.     }
  533.  
  534.     /* Fallback is to omit the server name and use a relative */
  535.     /* URI, although this is not RFC 2616 compliant.          */
  536.     $full_url = ($host ? $proto . $host . $port : '');
  537.     sqsession_register($full_url, 'sq_base_url');
  538.     return $full_url . $path;
  539. }
  540.  
  541.  
  542. /**
  543.  * Encrypts password
  544.  *
  545.  * These functions are used to encrypt the password before it is
  546.  * stored in a cookie. The encryption key is generated by
  547.  * OneTimePadCreate();
  548.  *
  549.  * @param string $string the (password)string to encrypt
  550.  * @param string $epad the encryption key
  551.  * @return string the base64-encoded encrypted password
  552.  * @since 1.0
  553.  */
  554. function OneTimePadEncrypt ($string, $epad) {
  555.     $pad = base64_decode($epad);
  556.  
  557.     if (strlen($pad)>0) {
  558.         // make sure that pad is longer than string
  559.         while (strlen($string)>strlen($pad)) {
  560.             $pad.=$pad;
  561.         }
  562.     } else {
  563.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  564.     }
  565.  
  566.     $encrypted = '';
  567.     for ($i = 0; $i < strlen ($string); $i++) {
  568.         $encrypted .= chr (ord($string[$i]) ^ ord($pad[$i]));
  569.     }
  570.  
  571.     return base64_encode($encrypted);
  572. }
  573.  
  574. /**
  575.  * Decrypts a password from the cookie
  576.  *
  577.  * Decrypts a password from the cookie, encrypted by OneTimePadEncrypt.
  578.  * This uses the encryption key that is stored in the session.
  579.  *
  580.  * @param string $string the string to decrypt
  581.  * @param string $epad the encryption key from the session
  582.  * @return string the decrypted password
  583.  * @since 1.0
  584.  */
  585. function OneTimePadDecrypt ($string, $epad) {
  586.     $pad = base64_decode($epad);
  587.  
  588.     if (strlen($pad)>0) {
  589.         // make sure that pad is longer than string
  590.         while (strlen($string)>strlen($pad)) {
  591.             $pad.=$pad;
  592.         }
  593.     } else {
  594.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  595.     }
  596.  
  597.     $encrypted = base64_decode ($string);
  598.     $decrypted = '';
  599.     for ($i = 0; $i < strlen ($encrypted); $i++) {
  600.         $decrypted .= chr (ord($encrypted[$i]) ^ ord($pad[$i]));
  601.     }
  602.  
  603.     return $decrypted;
  604. }
  605.  
  606.  
  607. /**
  608.  * Randomizes the mt_rand() function.
  609.  *
  610.  * Toss this in strings or integers and it will seed the generator
  611.  * appropriately. With strings, it is better to get them long.
  612.  * Use md5() to lengthen smaller strings.
  613.  *
  614.  * @param mixed $val a value to seed the random number generator. mixed = integer or string.
  615.  * @return void 
  616.  * @since 1.0
  617.  */
  618. function sq_mt_seed($Val) {
  619.     /* if mt_getrandmax() does not return a 2^n - 1 number,
  620.        this might not work well.  This uses $Max as a bitmask. */
  621.     $Max = mt_getrandmax();
  622.  
  623.     if (! is_int($Val)) {
  624.             $Val = crc32($Val);
  625.     }
  626.  
  627.     if ($Val < 0) {
  628.         $Val *= -1;
  629.     }
  630.  
  631.     if ($Val == 0) {
  632.         return;
  633.     }
  634.  
  635.     mt_srand(($Val ^ mt_rand(0, $Max)) & $Max);
  636. }
  637.  
  638.  
  639. /**
  640.  * Init random number generator
  641.  *
  642.  * This function initializes the random number generator fairly well.
  643.  * It also only initializes it once, so you don't accidentally get
  644.  * the same 'random' numbers twice in one session.
  645.  *
  646.  * @return void 
  647.  * @since 1.0
  648.  */
  649. function sq_mt_randomize() {
  650.     static $randomized;
  651.  
  652.     if ($randomized) {
  653.         return;
  654.     }
  655.  
  656.     /* Global. */
  657.     sqgetGlobalVar('REMOTE_PORT', $remote_port, SQ_SERVER);
  658.     sqgetGlobalVar('REMOTE_ADDR', $remote_addr, SQ_SERVER);
  659.     sq_mt_seed((int)((double) microtime() * 1000000));
  660.     sq_mt_seed(md5($remote_port . $remote_addr . getmypid()));
  661.  
  662.     /* getrusage */
  663.     if (function_exists('getrusage')) {
  664.         /* Avoid warnings with Win32 */
  665.         $dat = @getrusage();
  666.         if (isset($dat) && is_array($dat)) {
  667.             $Str = '';
  668.             foreach ($dat as $k => $v)
  669.                 {
  670.                     $Str .= $k . $v;
  671.                 }
  672.             sq_mt_seed(md5($Str));
  673.         }
  674.     }
  675.  
  676.     if(sqgetGlobalVar('UNIQUE_ID', $unique_id, SQ_SERVER)) {
  677.         sq_mt_seed(md5($unique_id));
  678.     }
  679.  
  680.     $randomized = 1;
  681. }
  682.  
  683. /**
  684.  * Creates encryption key
  685.  *
  686.  * Creates an encryption key for encrypting the password stored in the cookie.
  687.  * The encryption key itself is stored in the session.
  688.  *
  689.  * Pad must be longer or equal to encoded string length in 1.4.4/1.5.0 and older.
  690.  * @param int $length optional, length of the string to generate
  691.  * @return string the encryption key
  692.  * @since 1.0
  693.  */
  694. function OneTimePadCreate ($length=100) {
  695.     sq_mt_randomize();
  696.  
  697.     $pad = '';
  698.     for ($i = 0; $i < $length; $i++) {
  699.         $pad .= chr(mt_rand(0,255));
  700.     }
  701.  
  702.     return base64_encode($pad);
  703. }
  704.  
  705. /**
  706.  * Returns a string showing the size of the message/attachment.
  707.  *
  708.  * @param int $bytes the filesize in bytes
  709.  * @return string the filesize in human readable format
  710.  * @since 1.0
  711.  */
  712. function show_readable_size($bytes) {
  713.     $bytes /= 1024;
  714.     $type = 'KiB';
  715.  
  716.     if ($bytes / 1024 > 1) {
  717.         $bytes /= 1024;
  718.         $type = 'MiB';
  719.     }
  720.  
  721.     if ($bytes < 10) {
  722.         $bytes *= 10;
  723.         settype($bytes, 'integer');
  724.         $bytes /= 10;
  725.     } else {
  726.         settype($bytes, 'integer');
  727.     }
  728.  
  729.     return $bytes . '&nbsp;' . $type;
  730. }
  731.  
  732. /**
  733.  * Generates a random string from the character set you pass in
  734.  *
  735.  * @param int $size the length of the string to generate
  736.  * @param string $chars a string containing the characters to use
  737.  * @param int $flags a flag to add a specific set to the characters to use:
  738.  *      Flags:
  739.  *        1 = add lowercase a-z to $chars
  740.  *        2 = add uppercase A-Z to $chars
  741.  *        4 = add numbers 0-9 to $chars
  742.  * @return string the random string
  743.  * @since 1.0
  744.  */
  745. function GenerateRandomString($size, $chars, $flags = 0) {
  746.     if ($flags & 0x1) {
  747.         $chars .= 'abcdefghijklmnopqrstuvwxyz';
  748.     }
  749.     if ($flags & 0x2) {
  750.         $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  751.     }
  752.     if ($flags & 0x4) {
  753.         $chars .= '0123456789';
  754.     }
  755.  
  756.     if (($size < 1) || (strlen($chars) < 1)) {
  757.         return '';
  758.     }
  759.  
  760.     sq_mt_randomize(); /* Initialize the random number generator */
  761.  
  762.     $String = '';
  763.     $j = strlen( $chars ) - 1;
  764.     while (strlen($String) < $size) {
  765.         $String .= $chars{mt_rand(0, $j)};
  766.     }
  767.  
  768.     return $String;
  769. }
  770.  
  771. /**
  772.  * Escapes special characters for use in IMAP commands.
  773.  *
  774.  * @param string $str the string to escape
  775.  * @return string the escaped string
  776.  * @since 1.0.3
  777.  */
  778. function quoteimap($str) {
  779.     return preg_replace("/([\"\\\\])/", "\\\\$1", $str);
  780. }
  781.  
  782. /**
  783.  * Create compose link
  784.  *
  785.  * Returns a link to the compose-page, taking in consideration
  786.  * the compose_in_new and javascript settings.
  787.  * @param string $url the URL to the compose page
  788.  * @param string $text the link text, default "Compose"
  789.  * @param string $target (since 1.4.3) url target
  790.  * @return string a link to the compose page
  791.  * @since 1.4.2
  792.  */
  793. function makeComposeLink($url, $text = null, $target='') {
  794.     global $compose_new_win,$javascript_on, $compose_width, $compose_height;
  795.  
  796.     if(!$text) {
  797.         $text = _("Compose");
  798.     }
  799.  
  800.     // if not using "compose in new window", make
  801.     // regular link and be done with it
  802.     if($compose_new_win != '1') {
  803.         return makeInternalLink($url, $text, $target);
  804.     }
  805.  
  806.     // build the compose in new window link...
  807.  
  808.  
  809.     // if javascript is on, use onclick event to handle it
  810.     if($javascript_on) {
  811.         sqgetGlobalVar('base_uri', $base_uri, SQ_SESSION);
  812.         $compuri = SM_BASE_URI.$url;
  813.         return "<a href=\"javascript:void(0)\" onclick=\"comp_in_new('$compuri','$compose_width','$compose_height')\">$text</a>";
  814.     }
  815.  
  816.     // otherwise, just open new window using regular HTML
  817.     return makeInternalLink($url, $text, '_blank');
  818. }
  819.  
  820. /**
  821.  * version of fwrite which checks for failure
  822.  * @param resource $fp 
  823.  * @param string $string 
  824.  * @return number of written bytes. false on failure
  825.  * @since 1.4.3
  826.  */
  827. function sq_fwrite($fp, $string) {
  828.         // write to file
  829.         $count = @fwrite($fp,$string);
  830.         // the number of bytes written should be the length of the string
  831.         if($count != strlen($string)) {
  832.                 return FALSE;
  833.         }
  834.  
  835.         return $count;
  836. }
  837.  
  838. /**
  839.  * sq_get_html_translation_table
  840.  *
  841.  * Returns the translation table used by sq_htmlentities()
  842.  *
  843.  * @param integer $table html translation table. Possible values (without quotes):
  844.  *              <ul>
  845.  *                 <li>HTML_ENTITIES - full html entities table defined by charset</li>
  846.  *                 <li>HTML_SPECIALCHARS - html special characters table</li>
  847.  *              </ul>
  848.  * @param integer $quote_style quote encoding style. Possible values (without quotes):
  849.  *               <ul>
  850.  *                 <li>ENT_COMPAT - (default) encode double quotes</li>
  851.  *                 <li>ENT_NOQUOTES -  don't encode double or single quotes</li>
  852.  *                 <li>ENT_QUOTES - encode double and single quotes</li>
  853.  *               </ul>
  854.  * @param string $charset charset used for encoding. default to us-ascii, 'auto' uses $default_charset global value.
  855.  * @return array html translation array
  856.  * @since 1.5.1
  857.  */
  858. function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='us-ascii') {
  859.   global $default_charset;
  860.  
  861.   if ($table == HTML_SPECIALCHARS) $charset='us-ascii';
  862.  
  863.   // Start array with ampersand
  864.   $sq_html_ent_table = array( "&" => '&amp;' );
  865.  
  866.   // < and >
  867.   $sq_html_ent_table = array_merge($sq_html_ent_table,
  868.                         array("<" => '&lt;',
  869.                               ">" => '&gt;')
  870.                         );
  871.   // double quotes
  872.   if ($quote_style == ENT_COMPAT)
  873.      $sq_html_ent_table = array_merge($sq_html_ent_table,
  874.                             array("\"" => '&quot;')
  875.                             );
  876.  
  877.   // double and single quotes
  878.   if ($quote_style == ENT_QUOTES)
  879.      $sq_html_ent_table = array_merge($sq_html_ent_table,
  880.                             array("\"" => '&quot;',
  881.                               "'" => '&#39;')
  882.                             );
  883.  
  884.   if ($charset=='auto') $charset=$default_charset;
  885.  
  886.   // add entities that depend on charset
  887.   switch($charset){
  888.   case 'iso-8859-1':
  889.     include_once(SM_PATH . 'functions/htmlentities/iso-8859-1.php');
  890.     break;
  891.   case 'utf-8':
  892.     include_once(SM_PATH . 'functions/htmlentities/utf-8.php');
  893.     break;
  894.   case 'us-ascii':
  895.   default:
  896.     break;
  897.   }
  898.   // return table
  899.   return $sq_html_ent_table;
  900. }
  901.  
  902. /**
  903.  * sq_htmlentities
  904.  *
  905.  * Convert all applicable characters to HTML entities.
  906.  * Minimal php requirement - v.4.0.5.
  907.  *
  908.  * Function is designed for people that want to use full power of htmlentities() in
  909.  * i18n environment.
  910.  *
  911.  * @param string $string string that has to be sanitized
  912.  * @param integer $quote_style quote encoding style. Possible values (without quotes):
  913.  *               <ul>
  914.  *                 <li>ENT_COMPAT - (default) encode double quotes</li>
  915.  *                 <li>ENT_NOQUOTES - don't encode double or single quotes</li>
  916.  *                 <li>ENT_QUOTES - encode double and single quotes</li>
  917.  *               </ul>
  918.  * @param string $charset charset used for encoding. defaults to 'us-ascii', 'auto' uses $default_charset global value.
  919.  * @return string sanitized string
  920.  * @since 1.5.1
  921.  */
  922. function sq_htmlentities($string,$quote_style=ENT_COMPAT,$charset='us-ascii') {
  923.   // get translation table
  924.   $sq_html_ent_table=sq_get_html_translation_table(HTML_ENTITIES,$quote_style,$charset);
  925.   // convert characters
  926.   return str_replace(array_keys($sq_html_ent_table),array_values($sq_html_ent_table),$string);
  927. }
  928.  
  929. /**
  930.  * Tests if string contains 8bit symbols.
  931.  *
  932.  * If charset is not set, function defaults to default_charset.
  933.  * $default_charset global must be set correctly if $charset is
  934.  * not used.
  935.  * @param string $string tested string
  936.  * @param string $charset charset used in a string
  937.  * @return bool true if 8bit symbols are detected
  938.  * @since 1.5.1 and 1.4.4
  939.  */
  940. function sq_is8bit($string,$charset='') {
  941.     global $default_charset;
  942.  
  943.     if ($charset=='') $charset=$default_charset;
  944.  
  945.     /**
  946.      * Don't use \240 in ranges. Sometimes RH 7.2 doesn't like it.
  947.      * Don't use \200-\237 for iso-8859-x charsets. This range
  948.      * stores control symbols in those charsets.
  949.      * Use preg_match instead of ereg in order to avoid problems
  950.      * with mbstring overloading
  951.      */
  952.     if (preg_match("/^iso-8859/i",$charset)) {
  953.         $needle='/\240|[\241-\377]/';
  954.     } else {
  955.         $needle='/[\200-\237]|\240|[\241-\377]/';
  956.     }
  957.     return preg_match("$needle",$string);
  958. }
  959.  
  960. /**
  961.  * Replacement of mb_list_encodings function
  962.  *
  963.  * This function provides replacement for function that is available only
  964.  * in php 5.x. Function does not test all mbstring encodings. Only the ones
  965.  * that might be used in SM translations.
  966.  *
  967.  * Supported strings are stored in session in order to reduce number of
  968.  * mb_internal_encoding function calls.
  969.  *
  970.  * If you want to test all mbstring encodings - fill $list_of_encodings
  971.  * array.
  972.  * @return array list of encodings supported by php mbstring extension
  973.  * @since 1.5.1 and 1.4.6
  974.  */
  975. function sq_mb_list_encodings() {
  976.     if (! function_exists('mb_internal_encoding'))
  977.         return array();
  978.  
  979.     // php 5+ function
  980.     if (function_exists('mb_list_encodings')) {
  981.         $ret = mb_list_encodings();
  982.         array_walk($ret,'sq_lowercase_array_vals');
  983.         return $ret;
  984.     }
  985.  
  986.     // don't try to test encodings, if they are already stored in session
  987.     if (sqgetGlobalVar('mb_supported_encodings',$mb_supported_encodings,SQ_SESSION))
  988.         return $mb_supported_encodings;
  989.  
  990.     // save original encoding
  991.     $orig_encoding=mb_internal_encoding();
  992.  
  993.     $list_of_encoding=array(
  994.         'pass',
  995.         'auto',
  996.         'ascii',
  997.         'jis',
  998.         'utf-8',
  999.         'sjis',
  1000.         'euc-jp',
  1001.         'iso-8859-1',
  1002.         'iso-8859-2',
  1003.         'iso-8859-7',
  1004.         'iso-8859-9',
  1005.         'iso-8859-15',
  1006.         'koi8-r',
  1007.         'koi8-u',
  1008.         'big5',
  1009.         'gb2312',
  1010.         'gb18030',
  1011.         'windows-1251',
  1012.         'windows-1255',
  1013.         'windows-1256',
  1014.         'tis-620',
  1015.         'iso-2022-jp',
  1016.         'euc-cn',
  1017.         'euc-kr',
  1018.         'euc-tw',
  1019.         'uhc',
  1020.         'utf7-imap');
  1021.  
  1022.     $supported_encodings=array();
  1023.  
  1024.     foreach ($list_of_encoding as $encoding) {
  1025.         // try setting encodings. suppress warning messages
  1026.         if (@mb_internal_encoding($encoding))
  1027.             $supported_encodings[]=$encoding;
  1028.     }
  1029.  
  1030.     // restore original encoding
  1031.     mb_internal_encoding($orig_encoding);
  1032.  
  1033.     // register list in session
  1034.     sqsession_register($supported_encodings,'mb_supported_encodings');
  1035.  
  1036.     return $supported_encodings;
  1037. }
  1038.  
  1039. /**
  1040.  * Callback function used to lowercase array values.
  1041.  * @param string $val array value
  1042.  * @param mixed $key array key
  1043.  * @since 1.5.1 and 1.4.6
  1044.  */
  1045. function sq_lowercase_array_vals(&$val,$key) {
  1046.     $val = strtolower($val);
  1047. }
  1048.  
  1049.  
  1050. /**
  1051.  * Function returns number of characters in string.
  1052.  *
  1053.  * Returned number might be different from number of bytes in string,
  1054.  * if $charset is multibyte charset. Detection depends on mbstring
  1055.  * functions. If mbstring does not support tested multibyte charset,
  1056.  * vanilla string length function is used.
  1057.  * @param string $str string
  1058.  * @param string $charset charset
  1059.  * @since 1.5.1 and 1.4.6
  1060.  * @return integer number of characters in string
  1061.  */
  1062. function sq_strlen($str, $charset=null){
  1063.     // default option
  1064.     if (is_null($charset)) return strlen($str);
  1065.  
  1066.     // lowercase charset name
  1067.     $charset=strtolower($charset);
  1068.  
  1069.     // use automatic charset detection, if function call asks for it
  1070.     if ($charset=='auto') {
  1071.         global $default_charset, $squirrelmail_language;
  1072.         set_my_charset();
  1073.         $charset=$default_charset;
  1074.         if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
  1075.     }
  1076.  
  1077.     // Use mbstring only with listed charsets
  1078.     $aList_of_mb_charsets=array('utf-8','big5','gb2312','gb18030','euc-jp','euc-cn','euc-tw','euc-kr');
  1079.  
  1080.     // calculate string length according to charset
  1081.     if (in_array($charset,$aList_of_mb_charsets) && in_array($charset,sq_mb_list_encodings())) {
  1082.         $real_length = mb_strlen($str,$charset);
  1083.     } else {
  1084.         // own strlen detection code is removed because missing strpos,
  1085.         // strtoupper and substr implementations break string wrapping.
  1086.         $real_length=strlen($str);
  1087.     }
  1088.     return $real_length;
  1089. }
  1090.  
  1091. /**
  1092.  * string padding with multibyte support
  1093.  *
  1094.  * @link http://www.php.net/str_pad
  1095.  * @param string $string original string
  1096.  * @param integer $width padded string width
  1097.  * @param string $pad padding symbols
  1098.  * @param integer $padtype padding type
  1099.  *   (internal php defines, see str_pad() description)
  1100.  * @param string $charset charset used in original string
  1101.  * @return string padded string
  1102.  */
  1103. function sq_str_pad($string, $width, $pad, $padtype, $charset='') {
  1104.  
  1105.     $charset = strtolower($charset);
  1106.     $padded_string = '';
  1107.  
  1108.     switch ($charset) {
  1109.     case 'utf-8':
  1110.     case 'big5':
  1111.     case 'gb2312':
  1112.     case 'euc-kr':
  1113.         /*
  1114.          * all multibyte charsets try to increase width value by
  1115.          * adding difference between number of bytes and real length
  1116.          */
  1117.         $width = $width - sq_strlen($string,$charset) + strlen($string);
  1118.     default:
  1119.         $padded_string=str_pad($string,$width,$pad,$padtype);
  1120.     }
  1121.     return $padded_string;
  1122. }
  1123.  
  1124. /**
  1125.  * Wrapper that is used to switch between vanilla and multibyte substr
  1126.  * functions.
  1127.  * @param string $string 
  1128.  * @param integer $start 
  1129.  * @param integer $length 
  1130.  * @param string $charset 
  1131.  * @return string 
  1132.  * @since 1.5.1
  1133.  * @link http://www.php.net/substr
  1134.  * @link http://www.php.net/mb_substr
  1135.  */
  1136. function sq_substr($string,$start,$length,$charset='auto') {
  1137.     // use automatic charset detection, if function call asks for it
  1138.     static $charset_auto, $bUse_mb;
  1139.  
  1140.     if ($charset=='auto') {
  1141.         if (!isset($charset_auto)) {
  1142.             global $default_charset, $squirrelmail_language;
  1143.             set_my_charset();
  1144.             $charset=$default_charset;
  1145.             if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
  1146.             $charset_auto = $charset;
  1147.         } else {
  1148.             $charset = $charset_auto;
  1149.         }
  1150.     }
  1151.     $charset = strtolower($charset);
  1152.  
  1153.     // in_array call is expensive => do it once and use a static var for
  1154.     // storing the results
  1155.     if (!isset($bUse_mb)) {
  1156.         if (in_array($charset,sq_mb_list_encodings())) {
  1157.             $bUse_mb = true;
  1158.         } else {
  1159.             $bUse_mb = false;
  1160.         }
  1161.     }
  1162.  
  1163.     if ($bUse_mb) {
  1164.         return mb_substr($string,$start,$length,$charset);
  1165.     }
  1166.     // TODO: add mbstring independent code
  1167.  
  1168.     // use vanilla string functions as last option
  1169.     return substr($string,$start,$length);
  1170. }
  1171.  
  1172. /**
  1173.  * Wrapper that is used to switch between vanilla and multibyte strpos
  1174.  * functions.
  1175.  * @param string $haystack 
  1176.  * @param mixed $needle 
  1177.  * @param integer $offset 
  1178.  * @param string $charset 
  1179.  * @return string 
  1180.  * @since 1.5.1
  1181.  * @link http://www.php.net/strpos
  1182.  * @link http://www.php.net/mb_strpos
  1183.  */
  1184. function sq_strpos($haystack,$needle,$offset,$charset='auto') {
  1185.     // use automatic charset detection, if function call asks for it
  1186.     static $charset_auto, $bUse_mb;
  1187.  
  1188.     if ($charset=='auto') {
  1189.         if (!isset($charset_auto)) {
  1190.             global $default_charset, $squirrelmail_language;
  1191.             set_my_charset();
  1192.             $charset=$default_charset;
  1193.             if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
  1194.             $charset_auto = $charset;
  1195.         } else {
  1196.             $charset = $charset_auto;
  1197.         }
  1198.     }
  1199.     $charset = strtolower($charset);
  1200.  
  1201.     // in_array call is expensive => do it once and use a static var for
  1202.     // storing the results
  1203.     if (!isset($bUse_mb)) {
  1204.         if (in_array($charset,sq_mb_list_encodings())) {
  1205.             $bUse_mb = true;
  1206.         } else {
  1207.             $bUse_mb = false;
  1208.         }
  1209.     }
  1210.     if ($bUse_mb) {
  1211.         return mb_strpos($haystack,$needle,$offset,$charset);
  1212.     }
  1213.     // TODO: add mbstring independent code
  1214.  
  1215.     // use vanilla string functions as last option
  1216.     return strpos($haystack,$needle,$offset);
  1217. }
  1218.  
  1219. /**
  1220.  * Wrapper that is used to switch between vanilla and multibyte strtoupper
  1221.  * functions.
  1222.  * @param string $string 
  1223.  * @param string $charset 
  1224.  * @return string 
  1225.  * @since 1.5.1
  1226.  * @link http://www.php.net/strtoupper
  1227.  * @link http://www.php.net/mb_strtoupper
  1228.  */
  1229. function sq_strtoupper($string,$charset='auto') {
  1230.     // use automatic charset detection, if function call asks for it
  1231.     static $charset_auto, $bUse_mb;
  1232.  
  1233.     if ($charset=='auto') {
  1234.         if (!isset($charset_auto)) {
  1235.             global $default_charset, $squirrelmail_language;
  1236.             set_my_charset();
  1237.             $charset=$default_charset;
  1238.             if ($squirrelmail_language=='ja_JP') $charset='euc-jp';
  1239.             $charset_auto = $charset;
  1240.         } else {
  1241.             $charset = $charset_auto;
  1242.         }
  1243.     }
  1244.     $charset = strtolower($charset);
  1245.  
  1246.     // in_array call is expensive => do it once and use a static var for
  1247.     // storing the results
  1248.     if (!isset($bUse_mb)) {
  1249.         if (function_exists('mb_strtoupper') &&
  1250.             in_array($charset,sq_mb_list_encodings())) {
  1251.             $bUse_mb = true;
  1252.         } else {
  1253.             $bUse_mb = false;
  1254.         }
  1255.     }
  1256.  
  1257.     if ($bUse_mb) {
  1258.         return mb_strtoupper($string,$charset);
  1259.     }
  1260.     // TODO: add mbstring independent code
  1261.  
  1262.     // use vanilla string functions as last option
  1263.     return strtoupper($string);
  1264. }
  1265.  
  1266. /**
  1267.  * Counts 8bit bytes in string
  1268.  * @param string $string tested string
  1269.  * @return integer number of 8bit bytes
  1270.  */
  1271. function sq_count8bit($string) {
  1272.     $count=0;
  1273.     for ($i=0; $i<strlen($string); $i++) {
  1274.         if (ord($string[$i]) > 127) $count++;
  1275.     }
  1276.     return $count;
  1277. }
  1278.  
  1279. /**
  1280.  * Callback function to trim whitespace from a value, to be used in array_walk
  1281.  * @param string $value value to trim
  1282.  * @since 1.5.2 and 1.4.7
  1283.  */
  1284. function sq_trim_value ( &$value ) {
  1285.     $value = trim($value);
  1286. }

Documentation generated on Sat, 07 Oct 2006 16:13:50 +0300 by phpDocumentor 1.3.0RC6