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 1999-2020 The SquirrelMail Project Team
  10.  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
  11.  * @version $Id: strings.php 14845 2020-01-07 08:09:34Z pdontthink $
  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($outString1};
  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 ($outString0$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 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 ($outString0$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 == $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.     preg_match('/^([\t >]*)([^\t >].*)?$/'$line$regs);
  353.     $beginning_spaces $regs[1];
  354.     if (isset($regs[2])) {
  355.         $words explode(' '$regs[2]);
  356.     else {
  357.         $words array();
  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_spacessq_strlen($words[$i],$charset2;
  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],$charset1;
  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.            $is_secure_connection$sq_ignore_http_x_forwarded_headers;
  474.  
  475.     /* Get the path, handle virtual directories */
  476.     if(strpos(php_self()'?')) {
  477.         $path substr(php_self()0strpos(php_self()'?'));
  478.     else {
  479.         $path php_self();
  480.     }
  481.     $path substr($path0strrpos($path'/'));
  482.  
  483.     // proto+host+port are already set in config:
  484.     if !empty($config_location_base) ) {
  485.         return $config_location_base $path ;
  486.     }
  487.     // we computed it before, get it from the session:
  488.     if sqgetGlobalVar('sq_base_url'$full_urlSQ_SESSION) ) {
  489.       return $full_url $path;
  490.     }
  491.     // else: autodetect
  492.  
  493.     /* Check if this is a HTTPS or regular HTTP request. */
  494.     $proto 'http://';
  495.     if ($is_secure_connection)
  496.         $proto 'https://';
  497.  
  498.     /* Get the hostname from the Host header or server config. */
  499.     if ($sq_ignore_http_x_forwarded_headers
  500.      || !sqgetGlobalVar('HTTP_X_FORWARDED_HOST'$hostSQ_SERVER)
  501.      || empty($host)) {
  502.         if !sqgetGlobalVar('HTTP_HOST'$hostSQ_SERVER|| empty($host) ) {
  503.             if !sqgetGlobalVar('SERVER_NAME'$hostSQ_SERVER|| empty($host) ) {
  504.                 $host '';
  505.             }
  506.         }
  507.     }
  508.  
  509.     $port '';
  510.     if (strstr($host':')) {
  511.         // Note: HTTP_X_FORWARDED_PROTO could be sent from the client and
  512.         //       therefore possibly spoofed/hackable.  Thus, SquirrelMail
  513.         //       ignores such headers by default.  The administrator
  514.         //       can tell SM to use such header values by setting
  515.         //       $sq_ignore_http_x_forwarded_headers to boolean FALSE
  516.         //       in config/config.php or by using config/conf.pl.
  517.         global $sq_ignore_http_x_forwarded_headers;
  518.         if ($sq_ignore_http_x_forwarded_headers
  519.          || !sqgetGlobalVar('HTTP_X_FORWARDED_PROTO'$forwarded_protoSQ_SERVER))
  520.             $forwarded_proto '';
  521.         if (sqgetGlobalVar('SERVER_PORT'$server_portSQ_SERVER)) {
  522.             if (($server_port != 80 && $proto == 'http://'||
  523.                 ($server_port != 443 && $proto == 'https://' &&
  524.                  strcasecmp($forwarded_proto'https'!== 0)) {
  525.                 $port sprintf(':%d'$server_port);
  526.             }
  527.         }
  528.     }
  529.  
  530.     /* this is a workaround for the weird macosx caching that
  531.      * causes Apache to return 16080 as the port number, which causes
  532.      * SM to bail */
  533.  
  534.     if ($imap_server_type == 'macosx' && $port == ':16080'{
  535.         $port '';
  536.     }
  537.  
  538.     /* Fallback is to omit the server name and use a relative */
  539.     /* URI, although this is not RFC 2616 compliant.          */
  540.     $full_url ($host $proto $host $port '');
  541.     sqsession_register($full_url'sq_base_url');
  542.     return $full_url $path;
  543. }
  544.  
  545.  
  546. /**
  547.  * Get Message List URI
  548.  *
  549.  * @param string $mailbox      Current mailbox name (unencoded/raw)
  550.  * @param string $startMessage The mailbox page offset
  551.  * @param string $what         Any current search parameters (OPTIONAL;
  552.  *                              default empty string)
  553.  *
  554.  * @return string The message list URI
  555.  *
  556.  * @since 1.5.2
  557.  *
  558.  */
  559. function get_message_list_uri($mailbox$startMessage$what=''{
  560.  
  561.     global $base_uri;
  562.  
  563.     $urlMailbox urlencode($mailbox);
  564.  
  565.     $list_xtra "?where=read_body.php&what=$what&mailbox=$urlMailbox.
  566.                  "&startMessage=$startMessage";
  567.  
  568.     return $base_uri .'src/right_main.php'$list_xtra;
  569. }
  570.  
  571.  
  572. /**
  573.  * Encrypts password
  574.  *
  575.  * These functions are used to encrypt the password before it is
  576.  * stored in a cookie. The encryption key is generated by
  577.  * OneTimePadCreate();
  578.  *
  579.  * @param string $string the (password)string to encrypt
  580.  * @param string $epad the encryption key
  581.  * @return string the base64-encoded encrypted password
  582.  * @since 1.0
  583.  */
  584. function OneTimePadEncrypt ($string$epad{
  585.     $pad base64_decode($epad);
  586.  
  587.     if (strlen($pad)>0{
  588.         // make sure that pad is longer than string
  589.         while (strlen($string)>strlen($pad)) {
  590.             $pad.=$pad;
  591.         }
  592.     else {
  593.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  594.     }
  595.  
  596.     $encrypted '';
  597.     for ($i 0$i strlen ($string)$i++{
  598.         $encrypted .= chr (ord($string[$i]ord($pad[$i]));
  599.     }
  600.  
  601.     return base64_encode($encrypted);
  602. }
  603.  
  604. /**
  605.  * Decrypts a password from the cookie
  606.  *
  607.  * Decrypts a password from the cookie, encrypted by OneTimePadEncrypt.
  608.  * This uses the encryption key that is stored in the session.
  609.  *
  610.  * @param string $string the string to decrypt
  611.  * @param string $epad the encryption key from the session
  612.  * @return string the decrypted password
  613.  * @since 1.0
  614.  */
  615. function OneTimePadDecrypt ($string$epad{
  616.     $pad base64_decode($epad);
  617.  
  618.     if (strlen($pad)>0{
  619.         // make sure that pad is longer than string
  620.         while (strlen($string)>strlen($pad)) {
  621.             $pad.=$pad;
  622.         }
  623.     else {
  624.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  625.     }
  626.  
  627.     $encrypted base64_decode ($string);
  628.     $decrypted '';
  629.     for ($i 0$i strlen ($encrypted)$i++{
  630.         $decrypted .= chr (ord($encrypted[$i]ord($pad[$i]));
  631.     }
  632.  
  633.     return $decrypted;
  634. }
  635.  
  636. /**
  637.  * Creates encryption key
  638.  *
  639.  * Creates an encryption key for encrypting the password stored in the cookie.
  640.  * The encryption key itself is stored in the session.
  641.  *
  642.  * Pad must be longer or equal to encoded string length in 1.4.4/1.5.0 and older.
  643.  * @param int $length optional, length of the string to generate
  644.  * @return string the encryption key
  645.  * @since 1.0
  646.  */
  647. function OneTimePadCreate ($length=100{
  648.     $pad '';
  649.     for ($i 0$i $length$i++{
  650.         $pad .= chr(mt_rand(0,255));
  651.     }
  652.  
  653.     return base64_encode($pad);
  654. }
  655.  
  656. /**
  657.   * Returns a string showing a byte size figure in
  658.   * a more easily digested (readable) format
  659.   *
  660.   * @param int $bytes the size in bytes
  661.   *
  662.   * @return string The size in human readable format
  663.   *
  664.   * @since 1.0
  665.   *
  666.   */
  667. function show_readable_size($bytes{
  668.     $bytes /= 1024;
  669.     $type _("KiB");
  670.  
  671.     if ($bytes 1024 1{
  672.         $bytes /= 1024;
  673.         $type _("MiB");
  674.     }
  675.  
  676.     if ($bytes 10{
  677.         $bytes *= 10;
  678.         settype($bytes'integer');
  679.         $bytes /= 10;
  680.     else {
  681.         settype($bytes'integer');
  682.     }
  683.  
  684.     global $nbsp;
  685.     return $bytes $nbsp $type;
  686. }
  687.  
  688. /**
  689.  * Generates a random string from the character set you pass in
  690.  *
  691.  * @param int $size the length of the string to generate
  692.  * @param string $chars a string containing the characters to use
  693.  * @param int $flags a flag to add a specific set to the characters to use:
  694.  *      Flags:
  695.  *        1 = add lowercase a-z to $chars
  696.  *        2 = add uppercase A-Z to $chars
  697.  *        4 = add numbers 0-9 to $chars
  698.  * @return string the random string
  699.  * @since 1.0
  700.  */
  701. function GenerateRandomString($size$chars$flags 0{
  702.     if ($flags 0x1{
  703.         $chars .= 'abcdefghijklmnopqrstuvwxyz';
  704.     }
  705.     if ($flags 0x2{
  706.         $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  707.     }
  708.     if ($flags 0x4{
  709.         $chars .= '0123456789';
  710.     }
  711.  
  712.     if (($size 1|| (strlen($chars1)) {
  713.         return '';
  714.     }
  715.  
  716.     $String '';
  717.     $j strlen$chars 1;
  718.     while (strlen($String$size{
  719.         $String .= $chars{mt_rand(0$j)};
  720.     }
  721.  
  722.     return $String;
  723. }
  724.  
  725. /**
  726.  * Escapes special characters for use in IMAP commands.
  727.  *
  728.  * @param string $str the string to escape
  729.  * @return string the escaped string
  730.  * @since 1.0.3
  731.  */
  732. function quoteimap($str{
  733.     return str_replace(array('\\''"')array('\\\\''\\"')$str);
  734. }
  735.  
  736. /**
  737.  * Create compose link
  738.  *
  739.  * Returns a link to the compose-page, taking in consideration
  740.  * the compose_in_new and javascript settings.
  741.  *
  742.  * @param string $url       The URL to the compose page
  743.  * @param string $text      The link text, default "Compose"
  744.  * @param string $target    URL target, if any (since 1.4.3)
  745.  * @param string $accesskey The access key to be used, if any
  746.  *
  747.  * @return string a link to the compose page
  748.  *
  749.  * @since 1.4.2
  750.  */
  751. function makeComposeLink($url$text null$target=''$accesskey='NONE'{
  752.     global $compose_new_win$compose_width
  753.            $compose_height$oTemplate;
  754.  
  755.     if(!$text{
  756.         $text _("Compose");
  757.     }
  758.  
  759.     // if not using "compose in new window", make
  760.     // regular link and be done with it
  761.     if($compose_new_win != '1'{
  762.         return makeInternalLink($url$text$target$accesskey);
  763.     }
  764.  
  765.     // build the compose in new window link...
  766.  
  767.  
  768.     // if javascript is on, use onclick event to handle it
  769.     if(checkForJavascript()) {
  770.         sqgetGlobalVar('base_uri'$base_uriSQ_SESSION);
  771.         $compuri SM_BASE_URI.$url;
  772.  
  773.         return create_hyperlink('javascript:void(0)'$text'',
  774.                                 "comp_in_new('$compuri','$compose_width','$compose_height')",
  775.                                 '''''',
  776.                                 ($accesskey == 'NONE'
  777.                                 ? array()
  778.                                 : array('accesskey' => $accesskey)));
  779.     }
  780.  
  781.     // otherwise, just open new window using regular HTML
  782.     return makeInternalLink($url$text'_blank'$accesskey);
  783. }
  784.  
  785. /**
  786.  * version of fwrite which checks for failure
  787.  * @param resource $fp 
  788.  * @param string $string 
  789.  * @return number of written bytes. false on failure
  790.  * @since 1.4.3
  791.  */
  792. function sq_fwrite($fp$string{
  793.         // write to file
  794.         $count @fwrite($fp,$string);
  795.         // the number of bytes written should be the length of the string
  796.         if($count != strlen($string)) {
  797.                 return FALSE;
  798.         }
  799.  
  800.         return $count;
  801. }
  802.  
  803. /**
  804.  * sq_get_html_translation_table
  805.  *
  806.  * Returns the translation table used by sq_htmlentities()
  807.  *
  808.  * @param integer $table html translation table. Possible values (without quotes):
  809.  *              <ul>
  810.  *                 <li>HTML_ENTITIES - full html entities table defined by charset</li>
  811.  *                 <li>HTML_SPECIALCHARS - html special characters table</li>
  812.  *              </ul>
  813.  * @param integer $quote_style quote encoding style. Possible values (without quotes):
  814.  *               <ul>
  815.  *                 <li>ENT_COMPAT - (default) encode double quotes</li>
  816.  *                 <li>ENT_NOQUOTES -  don't encode double or single quotes</li>
  817.  *                 <li>ENT_QUOTES - encode double and single quotes</li>
  818.  *               </ul>
  819.  * @param string $charset charset used for encoding. default to us-ascii, 'auto' uses $default_charset global value.
  820.  * @return array html translation array
  821.  * @since 1.5.1
  822.  */
  823. function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='us-ascii'{
  824.   global $default_charset;
  825.  
  826.   if ($table == HTML_SPECIALCHARS$charset='us-ascii';
  827.  
  828.   // Start array with ampersand
  829.   $sq_html_ent_table array"&" => '&amp;' );
  830.  
  831.   // < and >
  832.   $sq_html_ent_table array_merge($sq_html_ent_table,
  833.                         array("<" => '&lt;',
  834.                               ">" => '&gt;')
  835.                         );
  836.   // double quotes
  837.   if ($quote_style == ENT_COMPAT)
  838.      $sq_html_ent_table array_merge($sq_html_ent_table,
  839.                             array("\"" => '&quot;')
  840.                             );
  841.  
  842.   // double and single quotes
  843.   if ($quote_style == ENT_QUOTES)
  844.      $sq_html_ent_table array_merge($sq_html_ent_table,
  845.                             array("\"" => '&quot;',
  846.                               "'" => '&#39;')
  847.                             );
  848.  
  849.   if ($charset=='auto'$charset=$default_charset;
  850.  
  851.   // add entities that depend on charset
  852.   switch($charset){
  853.   case 'iso-8859-1':
  854.     include_once(SM_PATH 'functions/htmlentities/iso-8859-1.php');
  855.     break;
  856.   case 'utf-8':
  857.     include_once(SM_PATH 'functions/htmlentities/utf-8.php');
  858.     break;
  859.   case 'us-ascii':
  860.   default:
  861.     break;
  862.   }
  863.   // return table
  864.   return $sq_html_ent_table;
  865. }
  866.  
  867. /**
  868.  * sq_htmlentities
  869.  *
  870.  * Convert all applicable characters to HTML entities.
  871.  * Minimal php requirement - v.4.0.5.
  872.  *
  873.  * Function is designed for people that want to use full power of htmlentities() in
  874.  * i18n environment.
  875.  *
  876.  * @param string $string string that has to be sanitized
  877.  * @param integer $quote_style quote encoding style. Possible values (without quotes):
  878.  *               <ul>
  879.  *                 <li>ENT_COMPAT - (default) encode double quotes</li>
  880.  *                 <li>ENT_NOQUOTES - don't encode double or single quotes</li>
  881.  *                 <li>ENT_QUOTES - encode double and single quotes</li>
  882.  *               </ul>
  883.  * @param string $charset charset used for encoding. defaults to 'us-ascii', 'auto' uses $default_charset global value.
  884.  * @return string sanitized string
  885.  * @since 1.5.1
  886.  */
  887. function sq_htmlentities($string,$quote_style=ENT_COMPAT,$charset='us-ascii'{
  888.   // get translation table
  889.   $sq_html_ent_table=sq_get_html_translation_table(HTML_ENTITIES,$quote_style,$charset);
  890.   // convert characters
  891.   return str_replace(array_keys($sq_html_ent_table),array_values($sq_html_ent_table),$string);
  892. }
  893.  
  894. /**
  895.  * Tests if string contains 8bit symbols.
  896.  *
  897.  * If charset is not set, function defaults to default_charset.
  898.  * $default_charset global must be set correctly if $charset is
  899.  * not used.
  900.  * @param string $string tested string
  901.  * @param string $charset charset used in a string
  902.  * @return bool true if 8bit symbols are detected
  903.  * @since 1.5.1 and 1.4.4
  904.  */
  905. function sq_is8bit($string,$charset=''{
  906.     global $default_charset;
  907.  
  908.     if ($charset==''$charset=$default_charset;
  909.  
  910.     /**
  911.      * Don't use \240 in ranges. Sometimes RH 7.2 doesn't like it.
  912.      * Don't use \200-\237 for iso-8859-x charsets. This range
  913.      * stores control symbols in those charsets.
  914.      * Use preg_match instead of ereg in order to avoid problems
  915.      * with mbstring overloading
  916.      */
  917.     if (preg_match("/^iso-8859/i",$charset)) {
  918.         $needle='/\240|[\241-\377]/';
  919.     else {
  920.         $needle='/[\200-\237]|\240|[\241-\377]/';
  921.     }
  922.     return preg_match("$needle",$string);
  923. }
  924.  
  925. /**
  926.  * Replacement of mb_list_encodings function
  927.  *
  928.  * This function provides replacement for function that is available only
  929.  * in php 5.x. Function does not test all mbstring encodings. Only the ones
  930.  * that might be used in SM translations.
  931.  *
  932.  * Supported strings are stored in session in order to reduce number of
  933.  * mb_internal_encoding function calls.
  934.  *
  935.  * If you want to test all mbstring encodings - fill $list_of_encodings
  936.  * array.
  937.  * @return array list of encodings supported by php mbstring extension
  938.  * @since 1.5.1 and 1.4.6
  939.  */
  940. function sq_mb_list_encodings({
  941.     if (function_exists('mb_internal_encoding'))
  942.         return array();
  943.  
  944.     // php 5+ function
  945.     if (function_exists('mb_list_encodings')) {
  946.         $ret mb_list_encodings();
  947.         array_walk($ret,'sq_lowercase_array_vals');
  948.         return $ret;
  949.     }
  950.  
  951.     // don't try to test encodings, if they are already stored in session
  952.     if (sqgetGlobalVar('mb_supported_encodings',$mb_supported_encodings,SQ_SESSION))
  953.         return $mb_supported_encodings;
  954.  
  955.     // save original encoding
  956.     $orig_encoding=mb_internal_encoding();
  957.  
  958.     $list_of_encoding=array(
  959.         'pass',
  960.         'auto',
  961.         'ascii',
  962.         'jis',
  963.         'utf-8',
  964.         'sjis',
  965.         'euc-jp',
  966.         'iso-8859-1',
  967.         'iso-8859-2',
  968.         'iso-8859-7',
  969.         'iso-8859-9',
  970.         'iso-8859-15',
  971.         'koi8-r',
  972.         'koi8-u',
  973.         'big5',
  974.         'gb2312',
  975.         'gb18030',
  976.         'windows-1251',
  977.         'windows-1255',
  978.         'windows-1256',
  979.         'tis-620',
  980.         'iso-2022-jp',
  981.         'euc-cn',
  982.         'euc-kr',
  983.         'euc-tw',
  984.         'uhc',
  985.         'utf7-imap');
  986.  
  987.     $supported_encodings=array();
  988.  
  989.     foreach ($list_of_encoding as $encoding{
  990.         // try setting encodings. suppress warning messages
  991.         if (@mb_internal_encoding($encoding))
  992.             $supported_encodings[]=$encoding;
  993.     }
  994.  
  995.     // restore original encoding
  996.     mb_internal_encoding($orig_encoding);
  997.  
  998.     // register list in session
  999.     sqsession_register($supported_encodings,'mb_supported_encodings');
  1000.  
  1001.     return $supported_encodings;
  1002. }
  1003.  
  1004. /**
  1005.  * Callback function used to lowercase array values.
  1006.  * @param string $val array value
  1007.  * @param mixed $key array key
  1008.  * @since 1.5.1 and 1.4.6
  1009.  */
  1010. function sq_lowercase_array_vals(&$val,$key{
  1011.     $val strtolower($val);
  1012. }
  1013.  
  1014.  
  1015. /**
  1016.  * Function returns number of characters in string.
  1017.  *
  1018.  * Returned number might be different from number of bytes in string,
  1019.  * if $charset is multibyte charset. Detection depends on mbstring
  1020.  * functions. If mbstring does not support tested multibyte charset,
  1021.  * vanilla string length function is used.
  1022.  * @param string $str string
  1023.  * @param string $charset charset
  1024.  * @since 1.5.1 and 1.4.6
  1025.  * @return integer number of characters in string
  1026.  */
  1027. function sq_strlen($str$charset=null){
  1028.     // default option
  1029.     if (is_null($charset)) return strlen($str);
  1030.  
  1031.     // lowercase charset name
  1032.     $charset=strtolower($charset);
  1033.  
  1034.     // use automatic charset detection, if function call asks for it
  1035.     if ($charset=='auto'{
  1036.         global $default_charset$squirrelmail_language;
  1037.         set_my_charset();
  1038.         $charset=$default_charset;
  1039.         if ($squirrelmail_language=='ja_JP'$charset='euc-jp';
  1040.     }
  1041.  
  1042.     // Use mbstring only with listed charsets
  1043.     $aList_of_mb_charsets=array('utf-8','big5','gb2312','gb18030','euc-jp','euc-cn','euc-tw','euc-kr');
  1044.  
  1045.     // calculate string length according to charset
  1046.     if (in_array($charset,$aList_of_mb_charsets&& in_array($charset,sq_mb_list_encodings())) {
  1047.         $real_length mb_strlen($str,$charset);
  1048.     else {
  1049.         // own strlen detection code is removed because missing strpos,
  1050.         // strtoupper and substr implementations break string wrapping.
  1051.         $real_length=strlen($str);
  1052.     }
  1053.     return $real_length;
  1054. }
  1055.  
  1056. /**
  1057.  * string padding with multibyte support
  1058.  *
  1059.  * @link http://www.php.net/str_pad
  1060.  * @param string $string original string
  1061.  * @param integer $width padded string width
  1062.  * @param string $pad padding symbols
  1063.  * @param integer $padtype padding type
  1064.  *   (internal php defines, see str_pad() description)
  1065.  * @param string $charset charset used in original string
  1066.  * @return string padded string
  1067.  */
  1068. function sq_str_pad($string$width$pad$padtype$charset=''{
  1069.  
  1070.     $charset strtolower($charset);
  1071.     $padded_string '';
  1072.  
  1073.     switch ($charset{
  1074.     case 'utf-8':
  1075.     case 'big5':
  1076.     case 'gb2312':
  1077.     case 'euc-kr':
  1078.         /*
  1079.          * all multibyte charsets try to increase width value by
  1080.          * adding difference between number of bytes and real length
  1081.          */
  1082.         $width $width sq_strlen($string,$charsetstrlen($string);
  1083.     default:
  1084.         $padded_string=str_pad($string,$width,$pad,$padtype);
  1085.     }
  1086.     return $padded_string;
  1087. }
  1088.  
  1089. /**
  1090.  * Wrapper that is used to switch between vanilla and multibyte substr
  1091.  * functions.
  1092.  * @param string $string 
  1093.  * @param integer $start 
  1094.  * @param integer $length 
  1095.  * @param string $charset 
  1096.  * @return string 
  1097.  * @since 1.5.1
  1098.  * @link http://www.php.net/substr
  1099.  * @link http://www.php.net/mb_substr
  1100.  */
  1101. function sq_substr($string,$start,$length=NULL,$charset='auto'{
  1102.  
  1103.     // if $length is NULL, use the full string length...
  1104.     // we have to do this to mimick the use of substr()
  1105.     // where $length is not given
  1106.     //
  1107.     if (is_null($length))
  1108.         $length sq_strlen($length);
  1109.  
  1110.  
  1111.     // use automatic charset detection, if function call asks for it
  1112.     static $charset_auto$bUse_mb;
  1113.  
  1114.     if ($charset=='auto'{
  1115.         if (!isset($charset_auto)) {
  1116.             global $default_charset$squirrelmail_language;
  1117.             set_my_charset();
  1118.             $charset=$default_charset;
  1119.             if ($squirrelmail_language=='ja_JP'$charset='euc-jp';
  1120.             $charset_auto $charset;
  1121.         else {
  1122.             $charset $charset_auto;
  1123.         }
  1124.     }
  1125.     $charset strtolower($charset);
  1126.  
  1127.     // in_array call is expensive => do it once and use a static var for
  1128.     // storing the results
  1129.     if (!isset($bUse_mb)) {
  1130.         if (in_array($charset,sq_mb_list_encodings())) {
  1131.             $bUse_mb true;
  1132.         else {
  1133.             $bUse_mb false;
  1134.         }
  1135.     }
  1136.  
  1137.     if ($bUse_mb{
  1138.         return mb_substr($string,$start,$length,$charset);
  1139.     }
  1140.     // TODO: add mbstring independent code
  1141.  
  1142.     // use vanilla string functions as last option
  1143.     return substr($string,$start,$length);
  1144. }
  1145.  
  1146. /**
  1147.   * This is a replacement for PHP's substr_replace() that is
  1148.   * multibyte-aware.
  1149.   *
  1150.   * @param string $string      The string to operate upon
  1151.   * @param string $replacement The string to be inserted
  1152.   * @param int    $start       The offset at which to begin substring replacement
  1153.   * @param int    $length      The number of characters after $start to remove
  1154.   *                             NOTE that if you need to specify a charset but
  1155.   *                             want to achieve normal substr_replace() behavior
  1156.   *                             where $length is not specified, use NULL (OPTIONAL;
  1157.   *                             default from $start to end of string)
  1158.   * @param string $charset     The charset of the given string.  A value of NULL
  1159.   *                             here will force the use of PHP's standard substr().
  1160.   *                             (OPTIONAL; default is "auto", which indicates that
  1161.   *                             the user's current charset should be used).
  1162.   *
  1163.   * @return string The manipulated string
  1164.   *
  1165.   *  Of course, you can use more advanced (e.g., negative) values
  1166.   *  for $start and $length as needed - see the PHP manual for more
  1167.   *  information:  http://www.php.net/manual/function.substr-replace.php
  1168.   *
  1169.   */
  1170. function sq_substr_replace($string$replacement$start$length=NULL,
  1171.                            $charset='auto')
  1172. {
  1173.  
  1174.    // NULL charset?  Just use substr_replace()
  1175.    //
  1176.    if (is_null($charset))
  1177.       return is_null($lengthsubstr_replace($string$replacement$start)
  1178.                               : substr_replace($string$replacement$start$length);
  1179.  
  1180.  
  1181.    // use current character set?
  1182.    //
  1183.    if ($charset == 'auto')
  1184.    {
  1185. //FIXME: is there any reason why this cannot be a global flag used by all string wrapper functions?
  1186.       static $auto_charset;
  1187.       if (!isset($auto_charset))
  1188.       {
  1189.          global $default_charset;
  1190. //FIXME - do we need this?
  1191. global $squirrelmail_language;
  1192.          set_my_charset();
  1193.          $auto_charset $default_charset;
  1194. //FIXME - do we need this?
  1195. if ($squirrelmail_language == 'ja_JP'$auto_charset 'euc-jp';
  1196.       }
  1197.       $charset $auto_charset;
  1198.    }
  1199.  
  1200.  
  1201.    // standardize character set name
  1202.    //
  1203.    $charset strtolower($charset);
  1204.  
  1205.  
  1206. /* ===== FIXME: this list is not used in 1.5.x, but if we need it, unless this differs between all our string function wrappers, we should store this info in the session
  1207.    // only use mbstring with the following character sets
  1208.    //
  1209.    $sq_substr_replace_mb_charsets = array(
  1210.       'utf-8',
  1211.       'big5',
  1212.       'gb2312',
  1213.       'gb18030',
  1214.       'euc-jp',
  1215.       'euc-cn',
  1216.       'euc-tw',
  1217.       'euc-kr'
  1218.    );
  1219.  
  1220.  
  1221.    // now we can use our own implementation using
  1222.    // mb_substr() and mb_strlen() if needed
  1223.    //
  1224.    if (in_array($charset, $sq_substr_replace_mb_charsets)
  1225.     && in_array($charset, sq_mb_list_encodings()))
  1226. ===== */
  1227. //FIXME: is there any reason why this cannot be a global array used by all string wrapper functions?
  1228.    if (in_array($charsetsq_mb_list_encodings()))
  1229.    {
  1230.  
  1231.       $string_length mb_strlen($string$charset);
  1232.  
  1233.       if ($start 0)
  1234.          $start max(0$string_length $start);
  1235.  
  1236.       else if ($start $string_length)
  1237.          $start $string_length;
  1238.  
  1239.       if ($length 0)
  1240.          $length max(0$string_length $start $length);
  1241.  
  1242.       else if (is_null($length|| $length $string_length)
  1243.          $length $string_length;
  1244.  
  1245.       if ($start $length $string_length)
  1246.          $length $string_length $start;
  1247.  
  1248.       return mb_substr($string0$start$charset)
  1249.            . $replacement
  1250.            . mb_substr($string,
  1251.                        $start $length,
  1252.                        $string_length// FIXME: I can't see why this is needed:  - $start - $length,
  1253.                        $charset);
  1254.  
  1255.    }
  1256.  
  1257.  
  1258.    // else use normal substr_replace()
  1259.    //
  1260.    return is_null($lengthsubstr_replace($string$replacement$start)
  1261.                            : substr_replace($string$replacement$start$length);
  1262.  
  1263. }
  1264.  
  1265. /**
  1266.  * Wrapper that is used to switch between vanilla and multibyte strpos
  1267.  * functions.
  1268.  * @param string $haystack 
  1269.  * @param mixed $needle 
  1270.  * @param integer $offset 
  1271.  * @param string $charset 
  1272.  * @return string 
  1273.  * @since 1.5.1
  1274.  * @link http://www.php.net/strpos
  1275.  * @link http://www.php.net/mb_strpos
  1276.  */
  1277. function sq_strpos($haystack,$needle,$offset,$charset='auto'{
  1278.     // use automatic charset detection, if function call asks for it
  1279.     static $charset_auto$bUse_mb;
  1280.  
  1281.     if ($charset=='auto'{
  1282.         if (!isset($charset_auto)) {
  1283.             global $default_charset$squirrelmail_language;
  1284.             set_my_charset();
  1285.             $charset=$default_charset;
  1286.             if ($squirrelmail_language=='ja_JP'$charset='euc-jp';
  1287.             $charset_auto $charset;
  1288.         else {
  1289.             $charset $charset_auto;
  1290.         }
  1291.     }
  1292.     $charset strtolower($charset);
  1293.  
  1294.     // in_array call is expensive => do it once and use a static var for
  1295.     // storing the results
  1296.     if (!isset($bUse_mb)) {
  1297.         if (in_array($charset,sq_mb_list_encodings())) {
  1298.             $bUse_mb true;
  1299.         else {
  1300.             $bUse_mb false;
  1301.         }
  1302.     }
  1303.     if ($bUse_mb{
  1304.         return mb_strpos($haystack,$needle,$offset,$charset);
  1305.     }
  1306.     // TODO: add mbstring independent code
  1307.  
  1308.     // use vanilla string functions as last option
  1309.     return strpos($haystack,$needle,$offset);
  1310. }
  1311.  
  1312. /**
  1313.  * Wrapper that is used to switch between vanilla and multibyte strtoupper
  1314.  * functions.
  1315.  * @param string $string 
  1316.  * @param string $charset 
  1317.  * @return string 
  1318.  * @since 1.5.1
  1319.  * @link http://www.php.net/strtoupper
  1320.  * @link http://www.php.net/mb_strtoupper
  1321.  */
  1322. function sq_strtoupper($string,$charset='auto'{
  1323.     // use automatic charset detection, if function call asks for it
  1324.     static $charset_auto$bUse_mb;
  1325.  
  1326.     if ($charset=='auto'{
  1327.         if (!isset($charset_auto)) {
  1328.             global $default_charset$squirrelmail_language;
  1329.             set_my_charset();
  1330.             $charset=$default_charset;
  1331.             if ($squirrelmail_language=='ja_JP'$charset='euc-jp';
  1332.             $charset_auto $charset;
  1333.         else {
  1334.             $charset $charset_auto;
  1335.         }
  1336.     }
  1337.     $charset strtolower($charset);
  1338.  
  1339.     // in_array call is expensive => do it once and use a static var for
  1340.     // storing the results
  1341.     if (!isset($bUse_mb)) {
  1342.         if (function_exists('mb_strtoupper'&&
  1343.             in_array($charset,sq_mb_list_encodings())) {
  1344.             $bUse_mb true;
  1345.         else {
  1346.             $bUse_mb false;
  1347.         }
  1348.     }
  1349.  
  1350.     if ($bUse_mb{
  1351.         return mb_strtoupper($string,$charset);
  1352.     }
  1353.     // TODO: add mbstring independent code
  1354.  
  1355.     // use vanilla string functions as last option
  1356.     return strtoupper($string);
  1357. }
  1358.  
  1359. /**
  1360.  * Counts 8bit bytes in string
  1361.  * @param string $string tested string
  1362.  * @return integer number of 8bit bytes
  1363.  */
  1364. function sq_count8bit($string{
  1365.     $count=0;
  1366.     for ($i=0$i<strlen($string)$i++{
  1367.         if (ord($string[$i]127$count++;
  1368.     }
  1369.     return $count;
  1370. }
  1371.  
  1372. /**
  1373.  * Callback function to trim whitespace from a value, to be used in array_walk
  1374.  * @param string $value value to trim
  1375.  * @since 1.5.2 and 1.4.7
  1376.  */
  1377. function sq_trim_value &$value {
  1378.     $value trim($value);
  1379. }
  1380.  
  1381. /**
  1382.   * Truncates the given string so that it has at
  1383.   * most $max_chars characters.  NOTE that a "character"
  1384.   * may be a multibyte character, or (optionally), an
  1385.   * HTML entity , so this function is different than
  1386.   * using substr() or mb_substr().
  1387.   *
  1388.   * NOTE that if $elipses is given and used, the returned
  1389.   *      number of characters will be $max_chars PLUS the
  1390.   *      length of $elipses
  1391.   *
  1392.   * @param string  $string    The string to truncate
  1393.   * @param int     $max_chars The maximum allowable characters
  1394.   * @param string  $elipses   A string that will be added to
  1395.   *                            the end of the truncated string
  1396.   *                            (ONLY if it is truncated) (OPTIONAL;
  1397.   *                            default not used)
  1398.   * @param boolean $html_entities_as_chars Whether or not to keep
  1399.   *                                         HTML entities together
  1400.   *                                         (OPTIONAL; default ignore
  1401.   *                                         HTML entities)
  1402.   *
  1403.   * @return string The truncated string
  1404.   *
  1405.   * @since 1.4.20 and 1.5.2 (replaced truncateWithEntities())
  1406.   *
  1407.   */
  1408. function sm_truncate_string($string$max_chars$elipses='',
  1409.                             $html_entities_as_chars=FALSE)
  1410. {
  1411.  
  1412.    // if the length of the string is less than
  1413.    // the allowable number of characters, just
  1414.    // return it as is (even if it contains any
  1415.    // HTML entities, that would just make the
  1416.    // actual length even smaller)
  1417.    //
  1418.    $actual_strlen sq_strlen($string'auto');
  1419.    if ($max_chars <= || $actual_strlen <= $max_chars)
  1420.       return $string;
  1421.  
  1422.  
  1423.    // if needed, count the number of HTML entities in
  1424.    // the string up to the maximum character limit,
  1425.    // pushing that limit up for each entity found
  1426.    //
  1427.    $adjusted_max_chars $max_chars;
  1428.    if ($html_entities_as_chars)
  1429.    {
  1430.  
  1431.       // $loop_count is needed to prevent an endless loop
  1432.       // which is caused by buggy mbstring versions that
  1433.       // return 0 (zero) instead of FALSE in some rare
  1434.       // cases.  Thanks, PHP.
  1435.       // see: http://bugs.php.net/bug.php?id=52731
  1436.       // also: tracker $3053349
  1437.       //
  1438.       $loop_count 0;
  1439.       $entity_pos $entity_end_pos = -1;
  1440.       while ($entity_end_pos $actual_strlen
  1441.           && ($entity_pos sq_strpos($string'&'$entity_end_pos 1)) !== FALSE
  1442.           && ($entity_end_pos sq_strpos($string';'$entity_pos)) !== FALSE
  1443.           && $entity_pos <= $adjusted_max_chars
  1444.           && $loop_count++ < $max_chars)
  1445.       {
  1446.          $adjusted_max_chars += $entity_end_pos $entity_pos;
  1447.       }
  1448.  
  1449.  
  1450.       // this isn't necessary because sq_substr() would figure this
  1451.       // out anyway, but we can avoid a sq_substr() call and we
  1452.       // know that we don't have to add an elipses (this is now
  1453.       // an accurate comparison, since $adjusted_max_chars, like
  1454.       // $actual_strlen, does not take into account HTML entities)
  1455.       //
  1456.       if ($actual_strlen <= $adjusted_max_chars)
  1457.          return $string;
  1458.  
  1459.    }
  1460.  
  1461.  
  1462.    // get the truncated string
  1463.    //
  1464.    $truncated_string sq_substr($string0$adjusted_max_chars);
  1465.  
  1466.  
  1467.    // return with added elipses
  1468.    //
  1469.    return $truncated_string $elipses;
  1470.  
  1471. }
  1472.  
  1473. /**
  1474.   * Gathers the list of secuirty tokens currently
  1475.   * stored in the user's preferences and optionally
  1476.   * purges old ones from the list.
  1477.   *
  1478.   * @param boolean $purge_old Indicates if old tokens
  1479.   *                            should be purged from the
  1480.   *                            list ("old" is 2 days or
  1481.   *                            older unless the administrator
  1482.   *                            overrides that value using
  1483.   *                            $max_token_age_days in
  1484.   *                            config/config_local.php)
  1485.   *                            (OPTIONAL; default is to always
  1486.   *                            purge old tokens)
  1487.   *
  1488.   * @return array The list of tokens
  1489.   *
  1490.   * @since 1.4.19 and 1.5.2
  1491.   *
  1492.   */
  1493. function sm_get_user_security_tokens($purge_old=TRUE)
  1494. {
  1495.  
  1496.    global $data_dir$username$max_token_age_days,
  1497.           $use_expiring_security_tokens;
  1498.  
  1499.    $tokens getPref($data_dir$username'security_tokens''');
  1500.    if (($tokens unserialize($tokens)) === FALSE || !is_array($tokens))
  1501.       $tokens array();
  1502.  
  1503.    // purge old tokens if necessary
  1504.    //
  1505.    if ($purge_old)
  1506.    {
  1507.       if (empty($max_token_age_days)) $max_token_age_days 2;
  1508.       $now time();
  1509.       $discard_token_date $now ($max_token_age_days 86400);
  1510.       $cleaned_tokens array();
  1511.       foreach ($tokens as $token => $timestamp)
  1512.          if ($timestamp >= $discard_token_date)
  1513.             $cleaned_tokens[$token$timestamp;
  1514.       $tokens $cleaned_tokens;
  1515.    }
  1516.  
  1517.    return $tokens;
  1518.  
  1519. }
  1520.  
  1521. /**
  1522.   * Generates a security token that is then stored in
  1523.   * the user's preferences with a timestamp for later
  1524.   * verification/use (although session-based tokens
  1525.   * are not stored in user preferences).
  1526.   *
  1527.   * NOTE: By default SquirrelMail will use a single session-based
  1528.   *       token, but if desired, user tokens can have expiration
  1529.   *       dates associated with them and become invalid even during
  1530.   *       the same login session.  When in that mode, the note
  1531.   *       immediately below applies, otherwise it is irrelevant.
  1532.   *       To enable that mode, the administrator must add the
  1533.   *       following to config/config_local.php:
  1534.   *       $use_expiring_security_tokens = TRUE;
  1535.   *
  1536.   * NOTE: The administrator can force SquirrelMail to generate
  1537.   * a new token every time one is requested (which may increase
  1538.   * obscurity through token randomness at the cost of some
  1539.   * performance) by adding the following to
  1540.   * config/config_local.php:   $do_not_use_single_token = TRUE;
  1541.   * Otherwise, only one token will be generated per user which
  1542.   * will change only after it expires or is used outside of the
  1543.   * validity period specified when calling sm_validate_security_token()
  1544.   *
  1545.   * WARNING: If the administrator has turned the token system
  1546.   *          off by setting $disable_security_tokens to TRUE in
  1547.   *          config/config.php or the configuration tool, this
  1548.   *          function will not store tokens in the user
  1549.   *          preferences (but it will still generate and return
  1550.   *          a random string).
  1551.   *
  1552.   * @param boolean $force_generate_new When TRUE, a new token will
  1553.   *                                     always be created even if current
  1554.   *                                     configuration dictates otherwise
  1555.   *                                     (OPTIONAL; default FALSE)
  1556.   *
  1557.   * @return string A security token
  1558.   *
  1559.   * @since 1.4.19 and 1.5.2
  1560.   *
  1561.   */
  1562. function sm_generate_security_token($force_generate_new=FALSE)
  1563. {
  1564.  
  1565.    global $data_dir$username$disable_security_tokens$do_not_use_single_token,
  1566.           $use_expiring_security_tokens;
  1567.    $max_generation_tries 1000;
  1568.  
  1569.    // if we're using session-based tokens, just return
  1570.    // the same one every time (generate it if it's not there)
  1571.    //
  1572.    if (!$use_expiring_security_tokens)
  1573.    {
  1574.       if (sqgetGlobalVar('sm_security_token'$tokenSQ_SESSION))
  1575.          return $token;
  1576.  
  1577.       // create new one since there was none in session
  1578.       $token GenerateRandomString(12''7);
  1579.       sqsession_register($token'sm_security_token');
  1580.       return $token;
  1581.    }
  1582.  
  1583.    $tokens sm_get_user_security_tokens();
  1584.  
  1585.    if (!$force_generate_new && !$do_not_use_single_token && !empty($tokens))
  1586.       return key($tokens);
  1587.  
  1588.    $new_token GenerateRandomString(12''7);
  1589.    $count 0;
  1590.    while (isset($tokens[$new_token]))
  1591.    {
  1592.       $new_token GenerateRandomString(12''7);
  1593.       if (++$count $max_generation_tries)
  1594.       {
  1595.          logout_error(_("Fatal token generation error; please contact your system administrator or the SquirrelMail Team"));
  1596.          exit;
  1597.       }
  1598.    }
  1599.  
  1600.    // is the token system enabled?  CAREFUL!
  1601.    //
  1602.    if (!$disable_security_tokens)
  1603.    {
  1604.       $tokens[$new_tokentime();
  1605.       setPref($data_dir$username'security_tokens'serialize($tokens));
  1606.    }
  1607.  
  1608.    return $new_token;
  1609.  
  1610. }
  1611.  
  1612. /**
  1613.   * Validates a given security token and optionally remove it
  1614.   * from the user's preferences if it was valid.  If the token
  1615.   * is too old but otherwise valid, it will still be rejected.
  1616.   *
  1617.   * "Too old" is 2 days or older unless the administrator
  1618.   * overrides that value using $max_token_age_days in
  1619.   * config/config_local.php
  1620.   *
  1621.   * Session-based tokens of course are always reused and are
  1622.   * valid for the lifetime of the login session.
  1623.   *
  1624.   * WARNING: If the administrator has turned the token system
  1625.   *          off by setting $disable_security_tokens to TRUE in
  1626.   *          config/config.php or the configuration tool, this
  1627.   *          function will always return TRUE.
  1628.   *
  1629.   * @param string  $token           The token to validate
  1630.   * @param int     $validity_period The number of seconds tokens are valid
  1631.   *                                  for (set to zero to remove valid tokens
  1632.   *                                  after only one use; set to -1 to allow
  1633.   *                                  indefinite re-use (but still subject to
  1634.   *                                  $max_token_age_days - see elsewhere);
  1635.   *                                  use 3600 to allow tokens to be reused for
  1636.   *                                  an hour) (OPTIONAL; default is to only
  1637.   *                                  allow tokens to be used once)
  1638.   *                                  NOTE this is unrelated to $max_token_age_days
  1639.   *                                  or rather is an additional time constraint on
  1640.   *                                  tokens that allows them to be re-used (or not)
  1641.   *                                  within a more narrow timeframe
  1642.   * @param boolean $show_error      Indicates that if the token is not
  1643.   *                                  valid, this function should display
  1644.   *                                  a generic error, log the user out
  1645.   *                                  and exit - this function will never
  1646.   *                                  return in that case.
  1647.   *                                  (OPTIONAL; default FALSE)
  1648.   *
  1649.   * @return boolean TRUE if the token validated; FALSE otherwise
  1650.   *
  1651.   * @since 1.4.19 and 1.5.2
  1652.   *
  1653.   */
  1654. function sm_validate_security_token($token$validity_period=0$show_error=FALSE)
  1655. {
  1656.  
  1657.    global $data_dir$username$max_token_age_days,
  1658.           $use_expiring_security_tokens,
  1659.           $disable_security_tokens;
  1660.  
  1661.    // bypass token validation?  CAREFUL!
  1662.    //
  1663.    if ($disable_security_tokensreturn TRUE;
  1664.  
  1665.    // if we're using session-based tokens, just compare
  1666.    // the same one every time
  1667.    //
  1668.    if (!$use_expiring_security_tokens)
  1669.    {
  1670.       if (!sqgetGlobalVar('sm_security_token'$session_tokenSQ_SESSION))
  1671.       {
  1672.          if (!$show_errorreturn FALSE;
  1673.          logout_error(_("Fatal security token error; please log in again"));
  1674.          exit;
  1675.       }
  1676.       if ($token !== $session_token)
  1677.       {
  1678.          if (!$show_errorreturn FALSE;
  1679.          logout_error(_("The current page request appears to have originated from an untrusted source."));
  1680.          exit;
  1681.       }
  1682.       return TRUE;
  1683.    }
  1684.  
  1685.    // don't purge old tokens here because we already
  1686.    // do it when generating tokens
  1687.    //
  1688.    $tokens sm_get_user_security_tokens(FALSE);
  1689.  
  1690.    // token not found?
  1691.    //
  1692.    if (empty($tokens[$token]))
  1693.    {
  1694.       if (!$show_errorreturn FALSE;
  1695.       logout_error(_("This page request could not be verified and appears to have expired."));
  1696.       exit;
  1697.    }
  1698.  
  1699.    $now time();
  1700.    $timestamp $tokens[$token];
  1701.  
  1702.    // whether valid or not, we want to remove it from
  1703.    // user prefs if it's old enough (unless requested to
  1704.    // bypass this (in which case $validity_period is -1))
  1705.    //
  1706.    if ($validity_period >= 0
  1707.     && $timestamp $now $validity_period)
  1708.    {
  1709.       unset($tokens[$token]);
  1710.       setPref($data_dir$username'security_tokens'serialize($tokens));
  1711.    }
  1712.  
  1713.    // reject tokens that are too old
  1714.    //
  1715.    if (empty($max_token_age_days)) $max_token_age_days 2;
  1716.    $old_token_date $now ($max_token_age_days 86400);
  1717.    if ($timestamp $old_token_date)
  1718.    {
  1719.       if (!$show_errorreturn FALSE;
  1720.       logout_error(_("The current page request appears to have originated from an untrusted source."));
  1721.       exit;
  1722.    }
  1723.  
  1724.    // token OK!
  1725.    //
  1726.    return TRUE;
  1727.  
  1728. }
  1729.  
  1730. /**
  1731.   * Wrapper for PHP's htmlspecialchars() that
  1732.   * attempts to add the correct character encoding
  1733.   *
  1734.   * @param string $string The string to be converted
  1735.   * @param int $flags A bitmask that controls the behavior of htmlspecialchars()
  1736.   *                    (See http://php.net/manual/function.htmlspecialchars.php )
  1737.   *                    (OPTIONAL; default ENT_COMPAT, ENT_COMPAT | ENT_SUBSTITUTE for PHP >=5.4)
  1738.   * @param string $encoding The character encoding to use in the conversion
  1739.   *                          (OPTIONAL; default automatic detection)
  1740.   * @param boolean $double_encode Whether or not to convert entities that are
  1741.   *                                already in the string (only supported in
  1742.   *                                PHP 5.2.3+) (OPTIONAL; default TRUE)
  1743.   *
  1744.   * @return string The converted text
  1745.   *
  1746.   */
  1747. function sm_encode_html_special_chars($string$flags=ENT_COMPAT,
  1748.                                       $encoding=NULL$double_encode=TRUE)
  1749. {
  1750.    if (!$encoding)
  1751.    {
  1752.       global $default_charset;
  1753.       if ($default_charset == 'iso-2022-jp')
  1754.          $default_charset 'EUC-JP';
  1755.       $encoding $default_charset;
  1756.    }
  1757.  
  1758.    if (check_php_version(523)) {
  1759.       // Replace invalid characters with a symbol instead of returning
  1760.       // empty string for the entire to be encoded string.
  1761.       if (check_php_version(540&& $flags == ENT_COMPAT{
  1762.          $flags $flags ENT_SUBSTITUTE;
  1763.       }
  1764.       return htmlspecialchars($string$flags$encoding$double_encode);
  1765.    }
  1766.  
  1767.    return htmlspecialchars($string$flags$encoding);
  1768. }

Documentation generated on Mon, 13 Jan 2020 04:23:39 +0100 by phpDocumentor 1.4.3