]> git.donarmstrong.com Git - roundcube.git/blob - program/include/rcube_shared.inc
Imported Upstream version 0.1.1
[roundcube.git] / program / include / rcube_shared.inc
1 <?php
2
3 /*
4  +-----------------------------------------------------------------------+
5  | rcube_shared.inc                                                      |
6  |                                                                       |
7  | This file is part of the RoundCube PHP suite                          |
8  | Copyright (C) 2005-2007, RoundCube Dev. - Switzerland                 |
9  | Licensed under the GNU GPL                                            |
10  |                                                                       |
11  | CONTENTS:                                                             |
12  |   Shared functions and classes used in PHP projects                   |
13  |                                                                       |
14  +-----------------------------------------------------------------------+
15  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
16  +-----------------------------------------------------------------------+
17
18  $Id: rcube_shared.inc 1255 2008-04-05 12:49:21Z thomasb $
19
20 */
21
22
23 /**
24  * RoundCube shared functions
25  * 
26  * @package Core
27  */
28
29
30 /**
31  * Provide details about the client's browser
32  *
33  * @return array Key-value pairs of browser properties
34  */
35 function rcube_browser()
36 {
37   $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
38
39   $bw['ver'] = 0;
40   $bw['win'] = stristr($HTTP_USER_AGENT, 'win');
41   $bw['mac'] = stristr($HTTP_USER_AGENT, 'mac');
42   $bw['linux'] = stristr($HTTP_USER_AGENT, 'linux');
43   $bw['unix']  = stristr($HTTP_USER_AGENT, 'unix');
44
45   $bw['ns4'] = stristr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
46   $bw['ns']  = ($bw['ns4'] || stristr($HTTP_USER_AGENT, 'netscape'));
47   $bw['ie']  = stristr($HTTP_USER_AGENT, 'msie');
48   $bw['mz']  = stristr($HTTP_USER_AGENT, 'mozilla/5');
49   $bw['opera'] = stristr($HTTP_USER_AGENT, 'opera');
50   $bw['safari'] = stristr($HTTP_USER_AGENT, 'safari');
51
52   if($bw['ns'])
53   {
54     $test = eregi("mozilla\/([0-9\.]+)", $HTTP_USER_AGENT, $regs);
55     $bw['ver'] = $test ? (float)$regs[1] : 0;
56   }
57   if($bw['mz'])
58   {
59     $test = ereg("rv:([0-9\.]+)", $HTTP_USER_AGENT, $regs);
60     $bw['ver'] = $test ? (float)$regs[1] : 0;
61   }
62   if($bw['ie'])
63   {
64     $test = eregi("msie ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
65     $bw['ver'] = $test ? (float)$regs[1] : 0;
66   }
67   if($bw['opera'])
68   {
69     $test = eregi("opera ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
70     $bw['ver'] = $test ? (float)$regs[1] : 0;
71   }
72
73   if(eregi(" ([a-z]{2})-([a-z]{2})", $HTTP_USER_AGENT, $regs))
74     $bw['lang'] =  $regs[1];
75   else
76     $bw['lang'] =  'en';
77
78   $bw['dom'] = ($bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5) || ($bw['opera'] && $bw['ver']>=7));
79   $bw['pngalpha'] = $bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5.5) ||
80                     ($bw['ie'] && $bw['ver']>=5 && $bw['mac']) || ($bw['opera'] && $bw['ver']>=7) ? TRUE : FALSE;
81
82   return $bw;
83 }
84
85
86 /**
87  * Get localized text in the desired language
88  *
89  * @param mixed Named parameters array or label name
90  * @return string Localized text
91  */
92 function rcube_label($attrib)
93 {
94   global $sess_user_lang, $INSTALL_PATH, $OUTPUT;
95   static $sa_text_data, $s_language, $utf8_decode;
96
97   // extract attributes
98   if (is_string($attrib))
99     $attrib = array('name' => $attrib);
100     
101   $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
102   $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
103
104   $command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
105   $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
106
107
108   // load localized texts
109   if (!$sa_text_data || $s_language != $sess_user_lang)
110     {
111     $sa_text_data = array();
112     
113     // get english labels (these should be complete)
114     @include($INSTALL_PATH.'program/localization/en_US/labels.inc');
115     @include($INSTALL_PATH.'program/localization/en_US/messages.inc');
116
117     if (is_array($labels))
118       $sa_text_data = $labels;
119     if (is_array($messages))
120       $sa_text_data = array_merge($sa_text_data, $messages);
121     
122     // include user language files
123     if ($sess_user_lang!='en' && is_dir($INSTALL_PATH.'program/localization/'.$sess_user_lang))
124     {
125       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/labels.inc');
126       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/messages.inc');
127
128       if (is_array($labels))
129         $sa_text_data = array_merge($sa_text_data, $labels);
130       if (is_array($messages))
131         $sa_text_data = array_merge($sa_text_data, $messages);
132     }
133       
134     $s_language = $sess_user_lang;
135   }
136
137   // text does not exist
138   if (!($text_item = $sa_text_data[$alias]))
139   {
140     /*
141     raise_error(array(
142       'code' => 500,
143       'type' => 'php',
144       'line' => __LINE__,
145       'file' => __FILE__,
146       'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
147     */
148     return "[$alias]";
149   }
150
151   // make text item array 
152   $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
153
154   // decide which text to use
155   if ($nr==1)
156     $text = $a_text_item['single'];
157   else if ($nr>0)
158     $text = $a_text_item['multiple'];
159   else if ($nr==0)
160   {
161     if ($a_text_item['none'])
162       $text = $a_text_item['none'];
163     else if ($a_text_item['single'])
164       $text = $a_text_item['single'];
165     else if ($a_text_item['multiple'])
166       $text = $a_text_item['multiple'];
167   }
168
169   // default text is single
170   if ($text=='')
171     $text = $a_text_item['single'];
172
173   // replace vars in text
174   if (is_array($attrib['vars']))
175   {
176     foreach ($attrib['vars'] as $var_key=>$var_value)
177       $a_replace_vars[substr($var_key, 0, 1)=='$' ? substr($var_key, 1) : $var_key] = $var_value;
178   }
179
180   if ($a_replace_vars)
181     $text = preg_replace('/\${?([_a-z]{1}[_a-z0-9]*)}?/ei', '$a_replace_vars["\1"]', $text);
182
183   // remove variables in text which were not available in arg $vars and $nr
184   eval("\$text = <<<EOF
185 $text
186 EOF;
187 ");
188
189   // format output
190   if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
191     return ucfirst($text);
192   else if ($attrib['uppercase'])
193     return strtoupper($text);
194   else if ($attrib['lowercase'])
195     return strtolower($text);
196
197   return $text;
198 }
199
200
201 /**
202  * Send HTTP headers to prevent caching this page
203  */
204 function send_nocacheing_headers()
205 {
206   if (headers_sent())
207     return;
208
209   header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
210   header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
211   header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
212   header("Pragma: no-cache");
213 }
214
215
216 /**
217  * Send header with expire date 30 days in future
218  *
219  * @param int Expiration time in seconds
220  */
221 function send_future_expire_header($offset=2600000)
222 {
223   if (headers_sent())
224     return;
225
226   header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+$offset)." GMT");
227   header("Cache-Control: max-age=$offset");
228   header("Pragma: ");
229 }
230
231
232 /**
233  * Check request for If-Modified-Since and send an according response.
234  * This will terminate the current script if headers match the given values
235  *
236  * @param int Modified date as unix timestamp
237  * @param string Etag value for caching
238  */
239 function send_modified_header($mdate, $etag=null)
240 {
241   if (headers_sent())
242     return;
243     
244   $iscached = false;
245   if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mdate)
246     $iscached = true;
247   
248   $etag = $etag ? "\"$etag\"" : null;
249   if ($etag)
250     $iscached = ($_SERVER['HTTP_IF_NONE_MATCH'] == $etag);
251   
252   if ($iscached)
253     header("HTTP/1.x 304 Not Modified");
254   else
255     header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mdate)." GMT");
256   
257   header("Cache-Control: max-age=0");
258   header("Expires: ");
259   header("Pragma: ");
260   
261   if ($etag)
262     header("Etag: $etag");
263   
264   if ($iscached)
265     {
266     ob_end_clean();
267     exit;
268     }
269 }
270
271
272 /**
273  * Convert a variable into a javascript object notation
274  *
275  * @param mixed Input value
276  * @return string Serialized JSON string
277  */
278 function json_serialize($var)
279 {
280   if (is_object($var))
281     $var = get_object_vars($var);
282
283   if (is_array($var))
284   {
285     // empty array
286     if (!sizeof($var))
287       return '[]';
288     else
289     {
290       $keys_arr = array_keys($var);
291       $is_assoc = $have_numeric = 0;
292
293       for ($i=0; $i<sizeof($keys_arr); ++$i)
294       {
295         if (is_numeric($keys_arr[$i]))
296           $have_numeric = 1;
297         if (!is_numeric($keys_arr[$i]) || $keys_arr[$i] != $i)
298           $is_assoc = 1;
299         if ($is_assoc && $have_numeric)
300           break;
301       }
302       
303       $brackets = $is_assoc ? '{}' : '[]';
304       $pairs = array();
305
306       foreach ($var as $key => $value)
307       {
308         // enclose key with quotes if it is not variable-name conform
309         if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */)
310           $key = "'$key'";
311
312         $pairs[] = sprintf("%s%s", $is_assoc ? "$key:" : '', json_serialize($value));
313       }
314
315       return $brackets{0} . implode(',', $pairs) . $brackets{1};
316     }
317   }
318   else if (is_numeric($var) && strval(intval($var)) === strval($var))
319     return $var;
320   else if (is_bool($var))
321     return $var ? '1' : '0';
322   else
323     return "'".JQ($var)."'";
324
325 }
326
327 /**
328  * Function to convert an array to a javascript array
329  * Actually an alias function for json_serialize()
330  * @deprecated
331  */
332 function array2js($arr, $type='')
333 {
334   return json_serialize($arr);
335 }
336
337
338 /**
339  * Similar function as in_array() but case-insensitive
340  *
341  * @param mixed Needle value
342  * @param array Array to search in
343  * @return boolean True if found, False if not
344  */
345 function in_array_nocase($needle, $haystack)
346 {
347   foreach ($haystack as $value)
348     if (strtolower($needle)===strtolower($value))
349       return true;
350   
351   return false;
352 }
353
354
355 /**
356  * Find out if the string content means TRUE or FALSE
357  *
358  * @param string Input value
359  * @return boolean Imagine what!
360  */
361 function get_boolean($str)
362 {
363   $str = strtolower($str);
364   if(in_array($str, array('false', '0', 'no', 'nein', ''), TRUE))
365     return FALSE;
366   else
367     return TRUE;
368 }
369
370
371 /**
372  * Parse a human readable string for a number of bytes
373  *
374  * @param string Input string
375  * @return int Number of bytes
376  */
377 function parse_bytes($str)
378 {
379   if (is_numeric($str))
380     return intval($str);
381     
382   if (preg_match('/([0-9]+)([a-z])/i', $str, $regs))
383   {
384     $bytes = floatval($regs[1]);
385     switch (strtolower($regs[2]))
386     {
387       case 'g':
388         $bytes *= 1073741824;
389         break;
390       case 'm':
391         $bytes *= 1048576;
392         break;
393       case 'k':
394         $bytes *= 1024;
395         break;
396     }
397   }
398
399   return intval($bytes);
400 }
401     
402 /**
403  * Create a human readable string for a number of bytes
404  *
405  * @param int Number of bytes
406  * @return string Byte string
407  */
408 function show_bytes($bytes)
409 {
410   if ($bytes > 1073741824)
411   {
412     $gb = $bytes/1073741824;
413     $str = sprintf($gb>=10 ? "%d GB" : "%.1f GB", $gb);
414   }
415   else if ($bytes > 1048576)
416   {
417     $mb = $bytes/1048576;
418     $str = sprintf($mb>=10 ? "%d MB" : "%.1f MB", $mb);
419   }
420   else if ($bytes > 1024)
421     $str = sprintf("%d KB",  round($bytes/1024));
422   else
423     $str = sprintf('%d B', $bytes);
424
425   return $str;
426 }
427
428
429 /**
430  * Convert paths like ../xxx to an absolute path using a base url
431  *
432  * @param string Relative path
433  * @param string Base URL
434  * @return string Absolute URL
435  */
436 function make_absolute_url($path, $base_url)
437 {
438   $host_url = $base_url;
439   $abs_path = $path;
440   
441   // check if path is an absolute URL
442   if (preg_match('/^[fhtps]+:\/\//', $path))
443     return $path;
444
445   // cut base_url to the last directory
446   if (strpos($base_url, '/')>7)
447   {
448     $host_url = substr($base_url, 0, strpos($base_url, '/'));
449     $base_url = substr($base_url, 0, strrpos($base_url, '/'));
450   }
451
452   // $path is absolute
453   if ($path{0}=='/')
454     $abs_path = $host_url.$path;
455   else
456   {
457     // strip './' because its the same as ''
458     $path = preg_replace('/^\.\//', '', $path);
459
460     if (preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER))
461       foreach ($matches as $a_match)
462       {
463         if (strrpos($base_url, '/'))
464           $base_url = substr($base_url, 0, strrpos($base_url, '/'));
465         
466         $path = substr($path, 3);
467       }
468
469     $abs_path = $base_url.'/'.$path;
470   }
471     
472   return $abs_path;
473 }
474
475
476 /**
477  * Wrapper function for strlen
478  */
479 function rc_strlen($str)
480 {
481   if (function_exists('mb_strlen'))
482     return mb_strlen($str);
483   else
484     return strlen($str);
485 }
486   
487 /**
488  * Wrapper function for strtolower
489  */
490 function rc_strtolower($str)
491 {
492   if (function_exists('mb_strtolower'))
493     return mb_strtolower($str);
494   else
495     return strtolower($str);
496 }
497
498 /**
499  * Wrapper function for substr
500  */
501 function rc_substr($str, $start, $len=null)
502 {
503   if (function_exists('mb_substr'))
504     return mb_substr($str, $start, $len);
505   else
506     return substr($str, $start, $len);
507 }
508
509 /**
510  * Wrapper function for strpos
511  */
512 function rc_strpos($haystack, $needle, $offset=0)
513 {
514   if (function_exists('mb_strpos'))
515     return mb_strpos($haystack, $needle, $offset);
516   else
517     return strpos($haystack, $needle, $offset);
518 }
519
520 /**
521  * Wrapper function for strrpos
522  */
523 function rc_strrpos($haystack, $needle, $offset=0)
524 {
525   if (function_exists('mb_strrpos'))
526     return mb_strrpos($haystack, $needle, $offset);
527   else
528     return strrpos($haystack, $needle, $offset);
529 }
530
531
532 /**
533  * Read a specific HTTP request header
534  *
535  * @access static
536  * @param  string $name Header name
537  * @return mixed  Header value or null if not available
538  */
539 function rc_request_header($name)
540 {
541   if (function_exists('getallheaders'))
542   {
543     $hdrs = array_change_key_case(getallheaders(), CASE_UPPER);
544     $key  = strtoupper($name);
545   }
546   else
547   {
548     $key  = 'HTTP_' . strtoupper(strtr($name, '-', '_'));
549     $hdrs = array_change_key_case($_SERVER, CASE_UPPER);
550   }
551
552   return $hdrs[$key];
553   }
554
555
556 /**
557  * Replace the middle part of a string with ...
558  * if it is longer than the allowed length
559  *
560  * @param string Input string
561  * @param int    Max. length
562  * @param string Replace removed chars with this
563  * @return string Abbreviated string
564  */
565 function abbreviate_string($str, $maxlength, $place_holder='...')
566 {
567   $length = rc_strlen($str);
568   $first_part_length = floor($maxlength/2) - rc_strlen($place_holder);
569   
570   if ($length > $maxlength)
571   {
572     $second_starting_location = $length - $maxlength + $first_part_length + 1;
573     $str = rc_substr($str, 0, $first_part_length) . $place_holder . rc_substr($str, $second_starting_location, $length);
574   }
575
576   return $str;
577 }
578
579
580 /**
581  * Make sure the string ends with a slash
582  */
583 function slashify($str)
584 {
585   return unslashify($str).'/';
586 }
587
588
589 /**
590  * Remove slash at the end of the string
591  */
592 function unslashify($str)
593 {
594   return preg_replace('/\/$/', '', $str);
595 }
596   
597
598 /**
599  * Delete all files within a folder
600  *
601  * @param string Path to directory
602  * @return boolean True on success, False if directory was not found
603  */
604 function clear_directory($dir_path)
605 {
606   $dir = @opendir($dir_path);
607   if(!$dir) return FALSE;
608
609   while ($file = readdir($dir))
610     if (strlen($file)>2)
611       unlink("$dir_path/$file");
612
613   closedir($dir);
614   return TRUE;
615 }
616
617
618 /**
619  * Create a unix timestamp with a specified offset from now
620  *
621  * @param string String representation of the offset (e.g. 20min, 5h, 2days)
622  * @param int Factor to multiply with the offset
623  * @return int Unix timestamp
624  */
625 function get_offset_time($offset_str, $factor=1)
626   {
627   if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
628   {
629     $amount = (int)$regs[1];
630     $unit = strtolower($regs[2]);
631   }
632   else
633   {
634     $amount = (int)$offset_str;
635     $unit = 's';
636   }
637     
638   $ts = mktime();
639   switch ($unit)
640   {
641     case 'w':
642       $amount *= 7;
643     case 'd':
644       $amount *= 24;
645     case 'h':
646       $amount *= 60;
647     case 'm':
648       $amount *= 60;
649     case 's':
650       $ts += $amount * $factor;
651   }
652
653   return $ts;
654 }
655
656
657 /**
658  * Return the last occurence of a string in another string
659  *
660  * @param haystack string string in which to search
661  * @param needle string string for which to search
662  * @return index of needle within haystack, or false if not found
663  */
664 function strrstr($haystack, $needle)
665 {
666   $pver = phpversion();
667   if ($pver[0] >= 5)
668       return strrpos($haystack, $needle);
669   else
670   {
671     $index = strpos(strrev($haystack), strrev($needle));
672     if($index === false)
673         return false;
674     
675     $index = strlen($haystack) - strlen($needle) - $index;
676     return $index;
677   }
678 }
679
680 /**
681  * A method to guess the mime_type of an attachment.
682  *
683  * @param string $path     Path to the file.
684  * @param string $failover Mime type supplied for failover.
685  *
686  * @return string
687  * @author Till Klampaeckel <till@php.net>
688  * @see    http://de2.php.net/manual/en/ref.fileinfo.php
689  * @see    http://de2.php.net/mime_content_type
690  */
691 function rc_mime_content_type($path, $failover = 'unknown/unknown')
692 {
693     global $CONFIG;
694
695     $mime_magic = $CONFIG['mime_magic'];
696
697     if (function_exists('mime_content_type')) {
698         return mime_content_type($path);
699     }
700     if (!extension_loaded('fileinfo')) { 
701         if (!dl('fileinfo.' . PHP_SHLIB_SUFFIX)) {
702             return $failover;
703         }
704     }
705     $finfo = finfo_open(FILEINFO_MIME, $mime_magic);
706     if (!$finfo) {
707         return $failover;
708     }
709     $mime_type = finfo_file($finfo,$path);
710     if (!$mime_type) {
711         return $failover;
712     }
713     finfo_close($finfo);
714
715     return $mime_type;
716 }
717
718 ?>