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.184.2.58 2006/08/11 11:24:05 kink Exp $
  12.  * @package squirrelmail
  13.  */
  14.  
  15. /**
  16.  * SquirrelMail version number -- DO NOT CHANGE
  17.  */
  18. global $version;
  19. $version '1.4.9 [CVS]';
  20.  
  21. /**
  22.  * SquirrelMail internal version number -- DO NOT CHANGE
  23.  * $sm_internal_version = array (release, major, minor)
  24.  */
  25. global $SQM_INTERNAL_VERSION;
  26. $SQM_INTERNAL_VERSION array(1,4,9);
  27.  
  28. /**
  29.  * There can be a circular issue with includes, where the $version string is
  30.  * referenced by the include of global.php, etc. before it's defined.
  31.  * For that reason, bring in global.php AFTER we define the version strings.
  32.  */
  33. require_once(SM_PATH 'functions/global.php');
  34.  
  35. /**
  36.  * Wraps text at $wrap characters
  37.  *
  38.  * Has a problem with special HTML characters, so call this before
  39.  * you do character translation.
  40.  *
  41.  * Specifically, &#039 comes up as 5 characters instead of 1.
  42.  * This should not add newlines to the end of lines.
  43.  */
  44. function sqWordWrap(&$line$wrap$charset=null{
  45.     global $languages$squirrelmail_language;
  46.  
  47.     if (isset($languages[$squirrelmail_language]['XTRA_CODE']&&
  48.         function_exists($languages[$squirrelmail_language]['XTRA_CODE'])) {
  49.         if (mb_detect_encoding($line!= 'ASCII'{
  50.             $line $languages[$squirrelmail_language]['XTRA_CODE']('wordwrap'$line$wrap);
  51.             return;
  52.         }
  53.     }
  54.  
  55.     ereg("^([\t >]*)([^\t >].*)?$"$line$regs);
  56.     $beginning_spaces $regs[1];
  57.     if (isset($regs[2])) {
  58.         $words explode(' '$regs[2]);
  59.     else {
  60.         $words '';
  61.     }
  62.  
  63.     $i 0;
  64.     $line $beginning_spaces;
  65.  
  66.     while ($i count($words)) {
  67.         /* Force one word to be on a line (minimum) */
  68.         $line .= $words[$i];
  69.         $line_len strlen($beginning_spacessq_strlen($words[$i],$charset2;
  70.         if (isset($words[$i 1]))
  71.             $line_len += sq_strlen($words[$i 1],$charset);
  72.         $i ++;
  73.  
  74.         /* Add more words (as long as they fit) */
  75.         while ($line_len $wrap && $i count($words)) {
  76.             $line .= ' ' $words[$i];
  77.             $i++;
  78.             if (isset($words[$i]))
  79.                 $line_len += sq_strlen($words[$i],$charset1;
  80.             else
  81.                 $line_len += 1;
  82.         }
  83.  
  84.         /* Skip spaces if they are the first thing on a continued line */
  85.         while (!isset($words[$i]&& $i count($words)) {
  86.             $i ++;
  87.         }
  88.  
  89.         /* Go to the next line if we have more to process */
  90.         if ($i count($words)) {
  91.             $line .= "\n";
  92.         }
  93.     }
  94. }
  95.  
  96. /**
  97.  * Does the opposite of sqWordWrap()
  98.  * @param string body the text to un-wordwrap
  99.  * @return void 
  100.  */
  101. function sqUnWordWrap(&$body{
  102.     global $squirrelmail_language;
  103.  
  104.     if ($squirrelmail_language == 'ja_JP'{
  105.         return;
  106.     }
  107.  
  108.     $lines explode("\n"$body);
  109.     $body '';
  110.     $PreviousSpaces '';
  111.     $cnt count($lines);
  112.     for ($i 0$i $cnt$i ++{
  113.         preg_match("/^([\t >]*)([^\t >].*)?$/"$lines[$i]$regs);
  114.         $CurrentSpaces $regs[1];
  115.         if (isset($regs[2])) {
  116.             $CurrentRest $regs[2];
  117.         else {
  118.             $CurrentRest '';
  119.         }
  120.  
  121.         if ($i == 0{
  122.             $PreviousSpaces $CurrentSpaces;
  123.             $body $lines[$i];
  124.         else if (($PreviousSpaces == $CurrentSpaces/* Do the beginnings match */
  125.                    && (strlen($lines[$i 1]65)    /* Over 65 characters long */
  126.                    && strlen($CurrentRest)) {          /* and there's a line to continue with */
  127.             $body .= ' ' $CurrentRest;
  128.         else {
  129.             $body .= "\n" $lines[$i];
  130.             $PreviousSpaces $CurrentSpaces;
  131.         }
  132.     }
  133.     $body .= "\n";
  134. }
  135.  
  136. /**
  137.  * Truncates a string and take care of html encoded characters
  138.  *
  139.  * @param string  $s string to truncate
  140.  * @param int $iTrimAt Trim at nn characters
  141.  * @return string  Trimmed string
  142.  */
  143. function truncateWithEntities($s$iTrimAt{
  144.     global $languages$squirrelmail_language;
  145.  
  146.     $ent_strlen strlen($s);
  147.     if (($iTrimAt <= 0|| ($ent_strlen <= $iTrimAt))
  148.         return $s;
  149.  
  150.  
  151.     if (isset($languages[$squirrelmail_language]['XTRA_CODE']&&
  152.         function_exists($languages[$squirrelmail_language]['XTRA_CODE'])) {
  153.             return $languages[$squirrelmail_language]['XTRA_CODE']('strimwidth'$s$iTrimAt);
  154.     else {
  155.         /*
  156.          * see if this is entities-encoded string
  157.          * If so, Iterate through the whole string, find out
  158.          * the real number of characters, and if more
  159.          * than $iTrimAt, substr with an updated trim value.
  160.          */
  161.         $trim_val $iTrimAt;
  162.         $ent_offset 0;
  163.         $ent_loc 0;
  164.         while $ent_loc $trim_val && (($ent_loc strpos($s'&'$ent_offset)) !== false&&
  165.                 (($ent_loc_end strpos($s';'$ent_loc+3)) !== false) ) {
  166.             $trim_val += ($ent_loc_end-$ent_loc);
  167.             $ent_offset  $ent_loc_end+1;
  168.         }
  169.  
  170.         if (($trim_val $iTrimAt&& ($ent_strlen $trim_val&& (strpos($s,';',$trim_val($trim_val 6))) {
  171.             $i strpos($s,';',$trim_val);
  172.             if ($i !== false{
  173.                 $trim_val strpos($s,';',$trim_val)+1;
  174.             }
  175.         }
  176.         // only print '...' when we're actually dropping part of the subject
  177.         if ($ent_strlen <= $trim_val)
  178.             return $s;
  179.     }
  180.  
  181.     return substr_replace($s'...'$trim_val);
  182. }
  183.  
  184. /**
  185.  * If $haystack is a full mailbox name and $needle is the mailbox
  186.  * separator character, returns the last part of the mailbox name.
  187.  *
  188.  * @param string haystack full mailbox name to search
  189.  * @param string needle the mailbox separator character
  190.  * @return string the last part of the mailbox name
  191.  */
  192. function readShortMailboxName($haystack$needle{
  193.  
  194.     if ($needle == ''{
  195.         $elem $haystack;
  196.     else {
  197.         $parts explode($needle$haystack);
  198.         $elem array_pop($parts);
  199.         while ($elem == '' && count($parts)) {
  200.             $elem array_pop($parts);
  201.         }
  202.     }
  203.     return$elem );
  204. }
  205.  
  206. /**
  207.  * php_self
  208.  *
  209.  * Creates an URL for the page calling this function, using either the PHP global
  210.  * REQUEST_URI, or the PHP global PHP_SELF with QUERY_STRING added.
  211.  *
  212.  * @return string the complete url for this page
  213.  */
  214. function php_self ({
  215.     if sqgetGlobalVar('REQUEST_URI'$req_uriSQ_SERVER&& !empty($req_uri) ) {
  216.       return $req_uri;
  217.     }
  218.  
  219.     if sqgetGlobalVar('PHP_SELF'$php_selfSQ_SERVER&& !empty($php_self) ) {
  220.  
  221.       // need to add query string to end of PHP_SELF to match REQUEST_URI
  222.       //
  223.       if sqgetGlobalVar('QUERY_STRING'$query_stringSQ_SERVER&& !empty($query_string) ) {
  224.          $php_self .= '?' $query_string;
  225.       }
  226.  
  227.       return $php_self;
  228.     }
  229.  
  230.     return '';
  231. }
  232.  
  233.  
  234. /**
  235.  * Find out where squirrelmail lives and try to be smart about it.
  236.  * The only problem would be when squirrelmail lives in directories
  237.  * called "src", "functions", or "plugins", but people who do that need
  238.  * to be beaten with a steel pipe anyway.
  239.  *
  240.  * @return string the base uri of squirrelmail installation.
  241.  */
  242. function sqm_baseuri(){
  243.     global $base_uri$PHP_SELF;
  244.     /**
  245.      * If it is in the session, just return it.
  246.      */
  247.     if (sqgetGlobalVar('base_uri',$base_uri,SQ_SESSION)){
  248.         return $base_uri;
  249.     }
  250.     $dirs array('|src/.*|''|plugins/.*|''|functions/.*|');
  251.     $repl array('''''');
  252.     $base_uri preg_replace($dirs$repl$PHP_SELF);
  253.     return $base_uri;
  254. }
  255.  
  256. /**
  257.  * get_location
  258.  *
  259.  * Determines the location to forward to, relative to your server.
  260.  * This is used in HTTP Location: redirects.
  261.  * If set, it uses $config_location_base as the first part of the URL,
  262.  * specifically, the protocol, hostname and port parts. The path is
  263.  * always autodetected.
  264.  *
  265.  * @return string the base url for this SquirrelMail installation
  266.  */
  267. function get_location ({
  268.  
  269.  
  270.     /* Get the path, handle virtual directories */
  271.     if(strpos(php_self()'?')) {
  272.         $path substr(php_self()0strpos(php_self()'?'));
  273.     else {
  274.         $path php_self();
  275.     }
  276.     $path substr($path0strrpos($path'/'));
  277.  
  278.     // proto+host+port are already set in config:
  279.     if !empty($config_location_base) ) {
  280.         // register it in the session just in case some plugin depends on this
  281.         sqsession_register($config_location_base $path'sq_base_url');
  282.         return $config_location_base $path ;
  283.     }
  284.     // we computed it before, get it from the session:
  285.     if sqgetGlobalVar('sq_base_url'$full_urlSQ_SESSION) ) {
  286.         return $full_url $path;
  287.     }
  288.     // else: autodetect
  289.  
  290.     /* Check if this is a HTTPS or regular HTTP request. */
  291.     $proto 'http://';
  292.  
  293.     /*
  294.      * If you have 'SSLOptions +StdEnvVars' in your apache config
  295.      *     OR if you have HTTPS=on in your HTTP_SERVER_VARS
  296.      *     OR if you are on port 443
  297.      */
  298.     $getEnvVar getenv('HTTPS');
  299.     if ((isset($getEnvVar&& strcasecmp($getEnvVar'on'=== 0||
  300.         (sqgetGlobalVar('HTTPS'$https_onSQ_SERVER&& strcasecmp($https_on'on'=== 0||
  301.         (sqgetGlobalVar('SERVER_PORT'$server_portSQ_SERVER&&  $server_port == 443)) {
  302.         $proto 'https://';
  303.     }
  304.  
  305.     /* Get the hostname from the Host header or server config. */
  306.     if !sqgetGlobalVar('HTTP_X_FORWARDED_HOST'$hostSQ_SERVER|| empty($host) ) {
  307.         if !sqgetGlobalVar('HTTP_HOST'$hostSQ_SERVER|| empty($host) ) {
  308.             if !sqgetGlobalVar('SERVER_NAME'$hostSQ_SERVER|| empty($host) ) {
  309.                 $host '';
  310.             }
  311.         }
  312.     }
  313.  
  314.     $port '';
  315.     if (strstr($host':')) {
  316.         if (sqgetGlobalVar('SERVER_PORT'$server_portSQ_SERVER)) {
  317.             if (($server_port != 80 && $proto == 'http://'||
  318.                 ($server_port != 443 && $proto == 'https://')) {
  319.                 $port sprintf(':%d'$server_port);
  320.             }
  321.         }
  322.     }
  323.  
  324.    /* this is a workaround for the weird macosx caching that
  325.       causes Apache to return 16080 as the port number, which causes
  326.       SM to bail */
  327.  
  328.    if ($imap_server_type == 'macosx' && $port == ':16080'{
  329.         $port '';
  330.    }
  331.  
  332.    /* Fallback is to omit the server name and use a relative */
  333.    /* URI, although this is not RFC 2616 compliant.          */
  334.    $full_url ($host $proto $host $port '');
  335.    sqsession_register($full_url'sq_base_url');
  336.    return $full_url $path;
  337. }
  338.  
  339.  
  340. /**
  341.  * Encrypts password
  342.  *
  343.  * These functions are used to encrypt the password before it is
  344.  * stored in a cookie. The encryption key is generated by
  345.  * OneTimePadCreate();
  346.  *
  347.  * @param string string the (password)string to encrypt
  348.  * @param string epad the encryption key
  349.  * @return string the base64-encoded encrypted password
  350.  */
  351. function OneTimePadEncrypt ($string$epad{
  352.     $pad base64_decode($epad);
  353.  
  354.     if (strlen($pad)>0{
  355.         // make sure that pad is longer than string
  356.         while (strlen($string)>strlen($pad)) {
  357.             $pad.=$pad;
  358.         }
  359.     else {
  360.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  361.     }
  362.  
  363.     $encrypted '';
  364.     for ($i 0$i strlen ($string)$i++{
  365.         $encrypted .= chr (ord($string[$i]ord($pad[$i]));
  366.     }
  367.  
  368.     return base64_encode($encrypted);
  369. }
  370.  
  371. /**
  372.  * Decrypts a password from the cookie
  373.  *
  374.  * Decrypts a password from the cookie, encrypted by OneTimePadEncrypt.
  375.  * This uses the encryption key that is stored in the session.
  376.  *
  377.  * @param string string the string to decrypt
  378.  * @param string epad the encryption key from the session
  379.  * @return string the decrypted password
  380.  */
  381. function OneTimePadDecrypt ($string$epad{
  382.     $pad base64_decode($epad);
  383.  
  384.     if (strlen($pad)>0{
  385.         // make sure that pad is longer than string
  386.         while (strlen($string)>strlen($pad)) {
  387.             $pad.=$pad;
  388.         }
  389.     else {
  390.         // FIXME: what should we do when $epad is not base64 encoded or empty.
  391.     }
  392.  
  393.     $encrypted base64_decode ($string);
  394.     $decrypted '';
  395.     for ($i 0$i strlen ($encrypted)$i++{
  396.         $decrypted .= chr (ord($encrypted[$i]ord($pad[$i]));
  397.     }
  398.  
  399.     return $decrypted;
  400. }
  401.  
  402.  
  403. /**
  404.  * Randomizes the mt_rand() function.
  405.  *
  406.  * Toss this in strings or integers and it will seed the generator
  407.  * appropriately. With strings, it is better to get them long.
  408.  * Use md5() to lengthen smaller strings.
  409.  *
  410.  * @param mixed val a value to seed the random number generator
  411.  * @return void 
  412.  */
  413. function sq_mt_seed($Val{
  414.     /* if mt_getrandmax() does not return a 2^n - 1 number,
  415.        this might not work well.  This uses $Max as a bitmask. */
  416.     $Max mt_getrandmax();
  417.  
  418.     if (is_int($Val)) {
  419.             $Val crc32($Val);
  420.     }
  421.  
  422.     if ($Val 0{
  423.         $Val *= -1;
  424.     }
  425.  
  426.     if ($Val == 0{
  427.         return;
  428.     }
  429.  
  430.     mt_srand(($Val mt_rand(0$Max)) $Max);
  431. }
  432.  
  433.  
  434. /**
  435.  * Init random number generator
  436.  *
  437.  * This function initializes the random number generator fairly well.
  438.  * It also only initializes it once, so you don't accidentally get
  439.  * the same 'random' numbers twice in one session.
  440.  *
  441.  * @return void 
  442.  */
  443. function sq_mt_randomize({
  444.     static $randomized;
  445.  
  446.     if ($randomized{
  447.         return;
  448.     }
  449.  
  450.     /* Global. */
  451.     sqgetGlobalVar('REMOTE_PORT'$remote_portSQ_SERVER);
  452.     sqgetGlobalVar('REMOTE_ADDR'$remote_addrSQ_SERVER);
  453.     sq_mt_seed((int)((double) microtime(1000000));
  454.     sq_mt_seed(md5($remote_port $remote_addr getmypid()));
  455.  
  456.     /* getrusage */
  457.     if (function_exists('getrusage')) {
  458.         /* Avoid warnings with Win32 */
  459.         $dat @getrusage();
  460.         if (isset($dat&& is_array($dat)) {
  461.             $Str '';
  462.             foreach ($dat as $k => $v)
  463.                 {
  464.                     $Str .= $k $v;
  465.                 }
  466.             sq_mt_seed(md5($Str));
  467.         }
  468.     }
  469.  
  470.     if(sqgetGlobalVar('UNIQUE_ID'$unique_idSQ_SERVER)) {
  471.         sq_mt_seed(md5($unique_id));
  472.     }
  473.  
  474.     $randomized 1;
  475. }
  476.  
  477. /**
  478.  * Creates encryption key
  479.  *
  480.  * Creates an encryption key for encrypting the password stored in the cookie.
  481.  * The encryption key itself is stored in the session.
  482.  *
  483.  * @param int length optional, length of the string to generate
  484.  * @return string the encryption key
  485.  */
  486. function OneTimePadCreate ($length=100{
  487.     sq_mt_randomize();
  488.  
  489.     $pad '';
  490.     for ($i 0$i $length$i++{
  491.         $pad .= chr(mt_rand(0,255));
  492.     }
  493.  
  494.     return base64_encode($pad);
  495. }
  496.  
  497. /**
  498.  * Returns a string showing the size of the message/attachment.
  499.  *
  500.  * @param int bytes the filesize in bytes
  501.  * @return string the filesize in human readable format
  502.  */
  503. function show_readable_size($bytes{
  504.     $bytes /= 1024;
  505.     $type 'k';
  506.  
  507.     if ($bytes 1024 1{
  508.         $bytes /= 1024;
  509.         $type 'M';
  510.     }
  511.  
  512.     if ($bytes 10{
  513.         $bytes *= 10;
  514.         settype($bytes'integer');
  515.         $bytes /= 10;
  516.     else {
  517.         settype($bytes'integer');
  518.     }
  519.  
  520.     return $bytes '<small>&nbsp;' $type '</small>';
  521. }
  522.  
  523. /**
  524.  * Generates a random string from the caracter set you pass in
  525.  *
  526.  * @param int size the size of the string to generate
  527.  * @param string chars a string containing the characters to use
  528.  * @param int flags a flag to add a specific set to the characters to use:
  529.  *      Flags:
  530.  *        1 = add lowercase a-z to $chars
  531.  *        2 = add uppercase A-Z to $chars
  532.  *        4 = add numbers 0-9 to $chars
  533.  * @return string the random string
  534.  */
  535. function GenerateRandomString($size$chars$flags 0{
  536.     if ($flags 0x1{
  537.         $chars .= 'abcdefghijklmnopqrstuvwxyz';
  538.     }
  539.     if ($flags 0x2{
  540.         $chars .= 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  541.     }
  542.     if ($flags 0x4{
  543.         $chars .= '0123456789';
  544.     }
  545.  
  546.     if (($size 1|| (strlen($chars1)) {
  547.         return '';
  548.     }
  549.  
  550.     sq_mt_randomize()/* Initialize the random number generator */
  551.  
  552.     $String '';
  553.     $j strlen$chars 1;
  554.     while (strlen($String$size{
  555.         $String .= $chars{mt_rand(0$j)};
  556.     }
  557.  
  558.     return $String;
  559. }
  560.  
  561. /**
  562.  * Escapes special characters for use in IMAP commands.
  563.  *
  564.  * @param