]> git.donarmstrong.com Git - roundcube.git/blob - program/include/rcube_shared.inc
Imported Upstream version 0.1~rc1~dfsg
[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 543 2007-04-28 18:07:12Z thomasb $
19
20 */
21
22
23 // ********* round cube schared classes *********
24
25 class rcube_html_page
26   {
27   var $css;
28   
29   var $scripts_path = '';
30   var $script_files = array();
31   var $external_scripts = array();
32   var $scripts = array();
33   var $charset = 'ISO-8859-1';
34   
35   var $script_tag_file = "<script type=\"text/javascript\" src=\"%s%s\"></script>\n";
36   var $script_tag      = "<script type=\"text/javascript\">\n<!--\n%s\n\n//-->\n</script>\n";
37   var $default_template = "<html>\n<head><title></title></head>\n<body></body>\n</html>";
38   var $tag_format_external_script = "<script type=\"text/javascript\" src=\"%s\"></script>\n";
39
40   var $title = '';
41   var $header = '';
42   var $footer = '';
43   var $body = '';
44   var $body_attrib = array();
45   var $meta_tags = array();
46
47
48   // PHP 5 constructor
49   function __construct()
50     {
51     $this->css = new rcube_css();
52     }
53
54   // PHP 4 compatibility
55   function rcube_html_page()
56     {
57     $this->__construct();
58     }
59
60
61   function include_script($file, $position='head')
62     {
63     static $sa_files = array();
64     
65     if (in_array($file, $sa_files))
66       return;
67       
68     if (!is_array($this->script_files[$position]))
69       $this->script_files[$position] = array();
70       
71     $this->script_files[$position][] = $file;
72     }
73     
74   function include_external_script($script_location, $position='head')
75   {
76      if (!is_array($this->external_scripts[$position]))
77      {
78         $this->external_scripts[$position] = array();
79      }
80      
81      $this->external_scripts[$position][] = $script_location;
82   }
83
84   function add_script($script, $position='head')
85     {
86     if (!isset($this->scripts[$position]))
87       $this->scripts[$position] = "\n".rtrim($script);
88     else
89       $this->scripts[$position] .= "\n".rtrim($script);
90     }
91
92   function add_header($str)
93     {
94     $this->header .= "\n".$str;
95     }
96
97   function add_footer($str)
98     {
99     $this->footer .= "\n".$str;
100     }
101
102   function set_title($t)
103     {
104     $this->title = $t;
105     }
106
107
108   function set_charset($charset)
109     {
110     global $MBSTRING;
111     
112     $this->charset = $charset;
113     
114     if ($MBSTRING && function_exists("mb_internal_encoding"))
115       {
116       if(!@mb_internal_encoding($charset))
117         $MBSTRING = FALSE;
118       }
119     }
120
121   function get_charset()
122     {
123     return $this->charset;
124     }
125
126
127   function reset()
128     {
129     $this->css = new rcube_css();
130     $this->script_files = array();
131     $this->scripts = array();
132     $this->title = '';
133     $this->header = '';
134     $this->footer = '';
135     }
136
137
138   function write($templ='', $base_path='')
139     {
140     $output = empty($templ) ? $this->default_template : trim($templ);
141     
142     // set default page title
143     if (empty($this->title))
144       $this->title = 'RoundCube Mail';
145   
146     // replace specialchars in content
147     $__page_title = Q($this->title, 'show', FALSE);
148     $__page_header = $__page_body = $__page_footer = '';
149     
150     
151     // include meta tag with charset
152     if (!empty($this->charset))
153       {
154       header('Content-Type: text/html; charset='.$this->charset);
155       $__page_header = '<meta http-equiv="content-type" content="text/html; charset='.$this->charset.'" />'."\n";
156       }
157   
158   
159     // definition of the code to be placed in the document header and footer
160     if (is_array($this->script_files['head']))
161       foreach ($this->script_files['head'] as $file)
162         $__page_header .= sprintf($this->script_tag_file, $this->scripts_path, $file);
163
164     if (is_array($this->external_scripts['head']))
165       foreach ($this->external_scripts['head'] as $xscript)
166         $__page_header .= sprintf($this->tag_format_external_script, $xscript);
167
168     $head_script = $this->scripts['head_top'] . $this->scripts['head'];
169     if (!empty($head_script))
170       $__page_header .= sprintf($this->script_tag, $head_script);
171
172     if (!empty($this->header))
173       $__page_header .= $this->header;
174
175     if (is_array($this->script_files['foot']))
176       foreach ($this->script_files['foot'] as $file)
177         $__page_footer .= sprintf($this->script_tag_file, $this->scripts_path, $file);
178
179     if (!empty($this->scripts['foot']))
180       $__page_footer .= sprintf($this->script_tag, $this->scripts['foot']);
181       
182     if (!empty($this->footer))
183       $__page_footer .= $this->footer;
184
185     $__page_header .= $this->css->show();
186   
187     // find page header
188     if($hpos = strpos(strtolower($output), '</head>'))
189       $__page_header .= "\n";
190     else 
191       {
192       if (!is_numeric($hpos))
193         $hpos = strpos(strtolower($output), '<body');
194       if (!is_numeric($hpos) && ($hpos = strpos(strtolower($output), '<html')))
195         {
196         while($output[$hpos]!='>')
197         $hpos++;
198         $hpos++;
199         }
200   
201       $__page_header = "<head>\n<title>$__page_title</title>\n$__page_header\n</head>\n";
202       }
203   
204     // add page hader
205     if($hpos)
206       $output = substr($output,0,$hpos) . $__page_header . substr($output,$hpos,strlen($output));
207     else
208       $output = $__page_header . $output;
209   
210   
211     // find page body
212     if($bpos = strpos(strtolower($output), '<body'))
213       {
214       while($output[$bpos]!='>') $bpos++;
215       $bpos++;
216       }
217     else
218       $bpos = strpos(strtolower($output), '</head>')+7;
219   
220     // add page body
221     if($bpos && $__page_body)
222       $output = substr($output,0,$bpos) . "\n$__page_body\n" . substr($output,$bpos,strlen($output));
223   
224   
225     // find and add page footer
226     $output_lc = strtolower($output);
227     if(($fpos = strrstr($output_lc, '</body>')) ||
228        ($fpos = strrstr($output_lc, '</html>')))
229       $output = substr($output, 0, $fpos) . "$__page_footer\n" . substr($output, $fpos);
230     else
231       $output .= "\n$__page_footer";
232   
233   
234     // reset those global vars
235     $__page_header = $__page_footer = '';
236   
237   
238     // correct absolute paths in images and other tags
239     $output = preg_replace('/(src|href|background)=(["\']?)(\/[a-z0-9_\-]+)/Ui', "\\1=\\2$base_path\\3", $output);
240     $output = str_replace('$__skin_path', $base_path, $output);
241   
242     print rcube_charset_convert($output, 'UTF-8', $this->charset);
243     }
244     
245     
246   function _parse($templ)
247     {
248     
249     }
250   }
251
252
253
254
255 class rcube_css
256   {
257   var $css_data = array();
258
259   var $css_groups = array();
260
261   var $include_files = array();
262
263   var $grouped_output = TRUE;
264
265   var $content_type = 'text/css';
266
267   var $base_path = '';
268
269   var $indent_chars = "\t";
270
271
272   // add or overwrite a css definition
273   // either pass porperty and value as separate arguments
274   // or provide an associative array as second argument
275   function set_style($selector, $property, $value='')
276     {
277     $a_elements = $this->_parse_selectors($selector);
278     foreach ($a_elements as $element)
279       {
280       if (!is_array($property))
281         $property = array($property => $value);
282
283       foreach ($property as $name => $value)
284         $this->css_data[$element][strtolower($name)] = $value;
285       }
286
287     // clear goups array
288     $this->css_groups = array();
289     }
290
291
292   // unset a style property
293   function remove_style($selector, $property)
294     {
295     if (!is_array($property))
296       $property = array($property);
297
298     foreach ($property as $key)
299       unset($this->css_data[$selector][strtolower($key)]);
300
301     // clear goups array
302     $this->css_groups = array();
303     }
304
305
306   // define base path for external css files
307   function set_basepath($path)
308     {
309     $this->base_path = preg_replace('/\/$/', '', $path);
310     }
311
312
313   // enable/disable grouped output
314   function set_grouped_output($grouped)
315     {
316     $this->grouped_output = $grouped;
317     }
318
319
320   // add a css file as external source
321   function include_file($filename, $media='')
322     {
323     // include multiple files
324     if (is_array($filename))
325       {
326       foreach ($filename as $file)
327         $this->include_file($file, $media);
328       }
329     // add single file
330     else if (!in_array($filename, $this->include_files))
331       $this->include_files[] = array('file' => $filename,
332                                      'media' => $media);
333     }
334
335
336   // parse css code
337   function import_string($str)
338     {
339     $ret = FALSE;
340     if (strlen($str))
341       $ret = $this->_parse($str);
342
343     return $ret;
344     }
345
346
347   // open and parse a css file
348   function import_file($file)
349     {
350     $ret = FALSE;
351
352     if (!is_file($file))
353       return $ret;
354
355     // for php version >= 4.3.0
356     if (function_exists('file_get_contents'))
357       $ret = $this->_parse(file_get_contents($file));
358
359     // for order php versions
360     else if ($fp = fopen($file, 'r'))
361       {
362       $ret = $this->_parse(fread($fp, filesize($file)));
363       fclose($fp);
364       }
365
366     return $ret;
367     }
368
369
370   // copy all properties inherited from superior styles to a specific selector
371   function copy_inherited_styles($selector)
372     {
373     // get inherited props from body and tag/class selectors
374     $css_props = $this->_get_inherited_styles($selector);
375
376     // write modified props back and clear goups array
377     if (sizeof($css_props))
378       {
379       $this->css_data[$selector] = $css_props;
380       $this->css_groups = array();
381       }
382     }
383
384
385   // return css definition for embedding in HTML
386   function show()
387     {
388     $out = '';
389
390     // include external css files
391     if (sizeof($this->include_files))
392       foreach ($this->include_files as $file_arr)
393       $out .= sprintf('<link rel="stylesheet" type="%s" href="%s"%s>'."\n",
394                         $this->content_type,
395                         $this->_get_file_path($file_arr['file']),
396                         $file_arr['media'] ? ' media="'.$file_arr['media'].'"' : '');
397
398
399     // compose css string
400     if (sizeof($this->css_data))
401       $out .= sprintf("<style type=\"%s\">\n<!--\n\n%s-->\n</style>",
402                       $this->content_type,
403                       $this->to_string());
404
405
406     return $out;
407     }
408
409
410   // return valid css code of the current styles grid
411   function to_string($selector=NULL)
412     {
413     // return code for a single selector
414     if ($selector)
415       {
416       $indent_str = $this->indent_chars;
417       $this->indent_chars = '';
418
419       $prop_arr = $this->to_array($selector);
420       $out = $this->_style2string($prop_arr, TRUE);
421
422       $this->indent_chars = $indent_str;
423       }
424
425     // compose css code for complete data grid
426     else
427       {
428       $out = '';
429       $css_data = $this->to_array();
430
431       foreach ($css_data as $key => $prop_arr)
432         $out .= sprintf("%s {\n%s}\n\n",
433                         $key,
434                         $this->_style2string($prop_arr, TRUE));
435       }
436
437     return $out;
438     }
439
440
441   // return a single-line string of a css definition
442   function to_inline($selector)
443     {
444     if ($this->css_data[$selector])
445       return str_replace('"', '\\"', $this->_style2string($this->css_data[$selector], FALSE));
446     }
447
448
449   // return an associative array with selector(s) as key and styles array as value
450   function to_array($selector=NULL)
451     {
452     if (!$selector && $this->grouped_output)
453       {
454       // build groups if desired
455       if (!sizeof($this->css_groups))
456         $this->_build_groups();
457
458       // modify group array to get an array(selector => properties)
459       $out_arr = array();
460       foreach ($this->css_groups as $group_arr)
461         {
462         $key = join(', ', $group_arr['selectors']);
463         $out_arr[$key] = $group_arr['properties'];
464         }
465       }
466     else
467       $out_arr = $this->css_data;
468
469     return $selector ? $out_arr[$selector] : $out_arr;
470     }
471
472
473   // create a css file
474   function to_file($filepath)
475     {
476     if ($fp = fopen($filepath, 'w'))
477       {
478       fwrite($fp, $this->to_string());
479       fclose($fp);
480       return TRUE;
481       }
482
483     return FALSE;
484     }
485
486
487   // alias method for import_string() [DEPRECATED]
488   function add($str)
489     {
490     $this->import_string($str);
491     }
492
493   // alias method for to_string() [DEPRECATED]
494   function get()
495     {
496     return $this->to_string();
497     }
498
499
500
501   // ******** private methods ********
502
503
504   // parse a string and add styles to internal data grid
505   function _parse($str)
506     {
507     // remove comments
508     $str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
509
510     // parse style definitions
511     if (!preg_match_all ('/([a-z0-9\.#*:_][a-z0-9\.\-_#:*,\[\]\(\)\s\"\'\+\|>~=]+)\s*\{([^\}]*)\}/ims', $str, $matches, PREG_SET_ORDER))
512       return FALSE;
513
514
515     foreach ($matches as $match_arr)
516       {
517       // split selectors into array
518       $a_keys = $this->_parse_selectors(trim($match_arr[1]));
519
520       // parse each property of an element
521       $codes = explode(";", trim($match_arr[2]));
522       foreach ($codes as $code)
523         {
524         if (strlen(trim($code))>0)
525           {
526           // find the property and the value
527           if (!($sep = strpos($code, ':')))
528             continue;
529
530           $property = strtolower(trim(substr($code, 0, $sep)));
531           $value    = trim(substr($code, $sep+1));
532
533           // add the property to the object array
534           foreach ($a_keys as $key)
535             $this->css_data[$key][$property] = $value;
536           }
537         }
538       }
539
540     // clear goups array
541     if (sizeof($matches))
542       {
543       $this->css_groups = array();
544       return TRUE;
545       }
546
547     return FALSE;
548     }
549
550
551   // split selector group
552   function _parse_selectors($selector)
553     {
554     // trim selector and remove multiple spaces
555     $selector = preg_replace('/\s+/', ' ', trim($selector));
556
557     if (strpos($selector, ','))
558       return preg_split('/[\t\s\n\r]*,[\t\s\n\r]*/mi', $selector);
559     else
560       return array($selector);
561     }
562
563
564   // compare identical styles and make groups
565   function _build_groups()
566     {
567     // clear group array
568     $this->css_groups = array();
569     $string_group_map = array();
570
571     // bulild css string for each selector and check if the same is already defines
572     foreach ($this->css_data as $selector => $prop_arr)
573       {
574       // make shure to compare props in the same order
575       ksort($prop_arr);
576       $compare_str = preg_replace('/[\s\t]+/', '', $this->_style2string($prop_arr, FALSE));
577
578       // add selector to extisting group
579       if (isset($string_group_map[$compare_str]))
580         {
581         $group_index = $string_group_map[$compare_str];
582         $this->css_groups[$group_index]['selectors'][] = $selector;
583         }
584
585       // create new group
586       else
587         {
588         $i = sizeof($this->css_groups);
589         $string_group_map[$compare_str] = $i;
590         $this->css_groups[$i] = array('selectors' => array($selector),
591                                       'properties' => $this->css_data[$selector]);
592         }
593       }
594     }
595
596
597   // convert the prop array into a valid css definition
598   function _style2string($prop_arr, $multiline=TRUE)
599     {
600     $out = '';
601     $delm   = $multiline ? "\n" : '';
602     $spacer = $multiline ? ' ' : '';
603     $indent = $multiline ? $this->indent_chars : '';
604
605     if (is_array($prop_arr))
606       foreach ($prop_arr as $prop => $value)
607         if (strlen($value))
608           $out .= sprintf('%s%s:%s%s;%s',
609                           $indent,
610                           $prop,
611                           $spacer,
612                           $value,
613                           $delm);
614
615     return $out;
616     }
617
618
619   // copy all properties inherited from superior styles to a specific selector
620   function _get_inherited_styles($selector, $loop=FALSE)
621     {
622     $css_props = $this->css_data[$selector] ? $this->css_data[$selector] : array();
623
624     // get styles from tag selector
625     if (preg_match('/(([a-z0-9]*)(\.[^\s]+)?)$/i', $selector, $regs))
626       {
627       $sel = $regs[1];
628       $tagname = $regs[2];
629       $class = $regs[3];
630
631       if ($sel && is_array($this->css_data[$sel]))
632         $css_props = $this->_merge_styles($this->css_data[$sel], $css_props);
633
634       if ($class && is_array($this->css_data[$class]))
635         $css_props = $this->_merge_styles($this->css_data[$class], $css_props);
636
637       if ($tagname && is_array($this->css_data[$tagname]))
638         $css_props = $this->_merge_styles($this->css_data[$tagname], $css_props);
639       }
640
641     // analyse inheritance
642     if (strpos($selector, ' '))
643       {
644       $a_hier = split(' ', $selector);
645       if (sizeof($a_hier)>1)
646         {
647         array_pop($a_hier);
648         $base_selector = join(' ', $a_hier);
649
650         // call this method recursively
651         $new_props = $this->_get_inherited_styles($base_selector, TRUE);
652         $css_props = $this->_merge_styles($new_props, $css_props);
653         }
654       }
655
656     // get body style
657     if (!$loop && is_array($this->css_data['body']))
658       $css_props = $this->_merge_styles($this->css_data['body'], $css_props);
659
660     return $css_props;
661     }
662
663
664   // merge two arrays with style properties together like a browser would do
665   function _merge_styles($one, $two)
666     {
667     // these properties are additive
668     foreach (array('text-decoration') as $prop)
669       if ($one[$prop] && $two[$prop])
670         {
671         // if value contains 'none' it's ignored
672         if (strstr($one[$prop], 'none'))
673           continue;
674         else if (strstr($two[$prop], 'none'))
675           unset($two[$prop]);
676
677         $a_values_one = split(' ', $one[$prop]);
678         $a_values_two = split(' ', $two[$prop]);
679         $two[$prop] = join(' ', array_unique(array_merge($a_values_one, $a_values_two)));
680         }
681
682     return array_merge($one, $two);
683     }
684
685
686   // resolve file path
687   function _get_file_path($file)
688     {
689     if (!$this->base_path && $GLOBALS['CSS_PATH'])
690       $this->set_basepath($GLOBALS['CSS_PATH']);
691
692     $base = ($file{0}=='/' || $file{0}=='.' || substr($file, 0, 7)=='http://') ? '' :
693             ($this->base_path ? $this->base_path.'/' : '');
694     return $base.$file;
695     }
696
697   }
698
699
700
701 class base_form_element
702   {
703   var $uppertags = FALSE;
704   var $upperattribs = FALSE;
705   var $upperprops = FALSE;
706   var $newline = FALSE;
707   
708   var $attrib = array();
709
710
711   // create string with attributes
712   function create_attrib_string($tagname='')
713     {
714     if (!sizeof($this->attrib))
715       return '';
716
717     if ($this->name!='')
718       $this->attrib['name'] = $this->name;
719
720     $attrib_arr = array();
721     foreach ($this->attrib as $key => $value)
722       {
723       // don't output some internally used attributes
724       if (in_array($key, array('form', 'quicksearch')))
725         continue;
726
727       // skip if size if not numeric
728       if (($key=='size' && !is_numeric($value)))
729         continue;
730         
731       // skip empty eventhandlers
732       if ((strpos($key,'on')===0 && $value==''))
733         continue;
734
735       // encode textarea content
736       if ($key=='value')
737         $value = Q($value, 'strict', FALSE);
738
739       // attributes with no value
740       if (in_array($key, array('checked', 'multiple', 'disabled', 'selected')))
741         {
742         if ($value)
743           $attrib_arr[] = $key;
744         }
745       // don't convert size of value attribute
746       else if ($key=='value')
747         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $value, 'value');
748         
749       // regular tag attributes
750       else
751         $attrib_arr[] = sprintf('%s="%s"', $this->_conv_case($key, 'attrib'), $this->_conv_case($value, 'value'));
752       }
753
754     return sizeof($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
755     }
756     
757     
758   // convert tags and attributes to upper-/lowercase
759   // $type can either be "tag" or "attrib"
760   function _conv_case($str, $type='attrib')
761     {
762     if ($type == 'tag')
763       return $this->uppertags ? strtoupper($str) : strtolower($str);
764     else if ($type == 'attrib')
765       return $this->upperattribs ? strtoupper($str) : strtolower($str);
766     else if ($type == 'value')
767       return $this->upperprops ? strtoupper($str) : strtolower($str);
768     }    
769   }
770
771
772 class input_field extends base_form_element
773   {
774   var $type = 'text';
775   
776   // PHP 5 constructor
777   function __construct($attrib=NULL)
778     {
779     if (is_array($attrib))
780       $this->attrib = $attrib;
781
782     if ($attrib['type'])
783       $this->type = $attrib['type'];    
784
785     if ($attrib['newline'])
786       $this->newline = TRUE;    
787     }
788
789   // PHP 4 compatibility
790   function input_field($attrib=array())
791     {
792     $this->__construct($attrib);
793     }  
794
795   // compose input tag
796   function show($value=NULL, $attrib=NULL)
797     {
798     // overwrite object attributes
799     if (is_array($attrib))
800       $this->attrib = array_merge($this->attrib, $attrib);
801
802     // set value attribute
803     if ($value!==NULL)
804       $this->attrib['value'] = $value;
805
806     $this->attrib['type'] = $this->type;
807
808     // return final tag
809     return sprintf('<%s%s />%s',
810                    $this->_conv_case('input', 'tag'),
811                    $this->create_attrib_string(),
812                    ($this->newline ? "\n" : ""));    
813     }  
814   }
815
816
817 class textfield extends input_field
818   {
819   var $type = 'text';
820   }
821
822 class passwordfield extends input_field
823   {
824   var $type = 'password';
825   }
826
827 class radiobutton extends input_field
828   {
829   var $type = 'radio';
830   }
831
832 class checkbox extends input_field
833   {
834   var $type = 'checkbox';
835
836
837   function show($value='', $attrib=NULL)
838     {
839     // overwrite object attributes
840     if (is_array($attrib))
841       $this->attrib = array_merge($this->attrib, $attrib);    
842
843     $this->attrib['type'] = $this->type;
844
845     if ($value && (string)$value==(string)$this->attrib['value'])
846       $this->attrib['checked'] = TRUE;
847     else
848       $this->attrib['checked'] = FALSE;
849
850     // return final tag
851     return sprintf('<%s%s />%s',
852                    $this->_conv_case('input', 'tag'),
853                    $this->create_attrib_string(),
854                    ($this->newline ? "\n" : ""));    
855     }
856   }
857
858
859 class textarea extends base_form_element
860   {
861   // PHP 5 constructor
862   function __construct($attrib=array())
863     {
864     $this->attrib = $attrib;
865
866     if ($attrib['newline'])
867       $this->newline = TRUE;    
868     }
869
870   // PHP 4 compatibility
871   function textarea($attrib=array())
872     {
873     $this->__construct($attrib);
874     }
875     
876   function show($value='', $attrib=NULL)
877     {
878     // overwrite object attributes
879     if (is_array($attrib))
880       $this->attrib = array_merge($this->attrib, $attrib);
881     
882     // take value attribute as content
883     if ($value=='')
884       $value = $this->attrib['value'];
885     
886     // make shure we don't print the value attribute
887     if (isset($this->attrib['value']))
888       unset($this->attrib['value']);
889
890     if (!empty($value) && !isset($this->attrib['mce_editable']))
891       $value = Q($value, 'strict', FALSE);
892
893     // return final tag
894     return sprintf('<%s%s>%s</%s>%s',
895                    $this->_conv_case('textarea', 'tag'),
896                    $this->create_attrib_string(),
897                    $value,
898                    $this->_conv_case('textarea', 'tag'),
899                    ($this->newline ? "\n" : ""));       
900     }
901   }
902
903
904 class hiddenfield extends base_form_element
905   {
906   var $fields_arr = array();
907   var $newline = TRUE;
908
909   // PHP 5 constructor
910   function __construct($attrib=NULL)
911     {
912     if (is_array($attrib))
913       $this->add($attrib);
914     }
915
916   // PHP 4 compatibility
917   function hiddenfield($attrib=NULL)
918     {
919     $this->__construct($attrib);
920     }
921
922   // add a hidden field to this instance
923   function add($attrib)
924     {
925     $this->fields_arr[] = $attrib;
926     }
927
928
929   function show()
930     {
931     $out = '';
932     foreach ($this->fields_arr as $attrib)
933       {
934       $this->attrib = $attrib;
935       $this->attrib['type'] = 'hidden';
936       
937       $out .= sprintf('<%s%s />%s',
938                    $this->_conv_case('input', 'tag'),
939                    $this->create_attrib_string(),
940                    ($this->newline ? "\n" : ""));   
941       }
942
943     return $out;
944     }
945   }
946
947
948 class select extends base_form_element
949   {
950   var $options = array();
951
952   /*
953   syntax:
954   -------
955   // create instance. arguments are used to set attributes of select-tag
956   $select = new select(array('name' => 'fieldname'));
957
958   // add one option
959   $select->add('Switzerland', 'CH');
960
961   // add multiple options
962   $select->add(array('Switzerland', 'Germany'),
963                array('CH', 'DE'));
964
965   // add 10 blank options with 50 chars
966   // used to fill with javascript (necessary for 4.x browsers)
967   $select->add_blank(10, 50);
968
969   // generate pulldown with selection 'Switzerland'  and return html-code
970   // as second argument the same attributes available to instanciate can be used
971   print $select->show('CH');
972   */
973
974   // PHP 5 constructor
975   function __construct($attrib=NULL)
976     {
977     if (is_array($attrib))
978       $this->attrib = $attrib;
979
980     if ($attrib['newline'])
981       $this->newline = TRUE;
982     }
983
984   // PHP 4 compatibility
985   function select($attrib=NULL)
986     {
987     $this->__construct($attrib);
988     }
989
990
991   function add($names, $values=NULL)
992     {
993     if (is_array($names))
994       {
995       foreach ($names as $i => $text)
996         $this->options[] = array('text' => $text, 'value' => (string)$values[$i]);
997       }
998     else
999       {
1000       $this->options[] = array('text' => $names, 'value' => (string)$values);
1001       }
1002     }
1003
1004     
1005   function add_blank($nr, $width=0)
1006     {
1007     $text = $width ? str_repeat('&nbsp;', $width) : '';
1008     
1009     for ($i=0; $i < $nr; $i++)
1010       $this->options[] = array('text' => $text);
1011     }
1012
1013   
1014   function show($select=array(), $attrib=NULL)
1015     {
1016     $options_str = "\n";
1017     $value_str = $this->_conv_case(' value="%s"', 'attrib');
1018     
1019     if (!is_array($select))
1020       $select = array((string)$select);
1021
1022     foreach ($this->options as $option)
1023       {
1024       $selected = ((isset($option['value']) &&
1025                     in_array($option['value'], $select, TRUE)) ||
1026                    (in_array($option['text'], $select, TRUE))) ?
1027         $this->_conv_case(' selected', 'attrib') : '';
1028                    
1029       $options_str .= sprintf("<%s%s%s>%s</%s>\n",
1030                              $this->_conv_case('option', 'tag'),
1031                              !empty($option['value']) ? sprintf($value_str, Q($option['value'])) : '',
1032                              $selected, 
1033                              Q($option['text'], 'strict', FALSE),
1034                              $this->_conv_case('option', 'tag'));
1035       }
1036
1037     // return final tag
1038     return sprintf('<%s%s>%s</%s>%s',
1039                    $this->_conv_case('select', 'tag'),
1040                    $this->create_attrib_string(),
1041                    $options_str,
1042                    $this->_conv_case('select', 'tag'),
1043                    ($this->newline ? "\n" : ""));    
1044     }
1045   }
1046
1047
1048
1049
1050 // ********* rcube schared functions *********
1051
1052
1053 // provide details about the client's browser
1054 function rcube_browser()
1055   {
1056   $HTTP_USER_AGENT = $_SERVER['HTTP_USER_AGENT'];
1057
1058   $bw['ver'] = 0;
1059   $bw['win'] = stristr($HTTP_USER_AGENT, 'win');
1060   $bw['mac'] = stristr($HTTP_USER_AGENT, 'mac');
1061   $bw['linux'] = stristr($HTTP_USER_AGENT, 'linux');
1062   $bw['unix']  = stristr($HTTP_USER_AGENT, 'unix');
1063
1064   $bw['ns4'] = stristr($HTTP_USER_AGENT, 'mozilla/4') && !stristr($HTTP_USER_AGENT, 'msie');
1065   $bw['ns']  = ($bw['ns4'] || stristr($HTTP_USER_AGENT, 'netscape'));
1066   $bw['ie']  = stristr($HTTP_USER_AGENT, 'msie');
1067   $bw['mz']  = stristr($HTTP_USER_AGENT, 'mozilla/5');
1068   $bw['opera'] = stristr($HTTP_USER_AGENT, 'opera');
1069   $bw['safari'] = stristr($HTTP_USER_AGENT, 'safari');
1070
1071   if($bw['ns'])
1072     {
1073     $test = eregi("mozilla\/([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1074     $bw['ver'] = $test ? (float)$regs[1] : 0;
1075     }
1076   if($bw['mz'])
1077     {
1078     $test = ereg("rv:([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1079     $bw['ver'] = $test ? (float)$regs[1] : 0;
1080     }
1081   if($bw['ie'])
1082     {
1083     $test = eregi("msie ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1084     $bw['ver'] = $test ? (float)$regs[1] : 0;
1085     }
1086   if($bw['opera'])
1087     {
1088     $test = eregi("opera ([0-9\.]+)", $HTTP_USER_AGENT, $regs);
1089     $bw['ver'] = $test ? (float)$regs[1] : 0;
1090     }
1091
1092   if(eregi(" ([a-z]{2})-([a-z]{2})", $HTTP_USER_AGENT, $regs))
1093     $bw['lang'] =  $regs[1];
1094   else
1095     $bw['lang'] =  'en';
1096
1097   $bw['dom'] = ($bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5) || ($bw['opera'] && $bw['ver']>=7));
1098   $bw['pngalpha'] = $bw['mz'] || $bw['safari'] || ($bw['ie'] && $bw['ver']>=5.5) ||
1099                     ($bw['ie'] && $bw['ver']>=5 && $bw['mac']) || ($bw['opera'] && $bw['ver']>=7) ? TRUE : FALSE;
1100
1101   return $bw;
1102   }
1103
1104
1105 // get text in the desired language from the language file
1106 function rcube_label($attrib)
1107   {
1108   global $sess_user_lang, $INSTALL_PATH, $OUTPUT;
1109   static $sa_text_data, $s_language, $utf8_decode;
1110
1111   // extract attributes
1112   if (is_string($attrib))
1113     $attrib = array('name' => $attrib);
1114     
1115   $nr = is_numeric($attrib['nr']) ? $attrib['nr'] : 1;
1116   $vars = isset($attrib['vars']) ? $attrib['vars'] : '';
1117
1118   $command_name = !empty($attrib['command']) ? $attrib['command'] : NULL;
1119   $alias = $attrib['name'] ? $attrib['name'] : ($command_name && $command_label_map[$command_name] ? $command_label_map[$command_name] : '');
1120
1121
1122   // load localized texts
1123   if (!$sa_text_data || $s_language != $sess_user_lang)
1124     {
1125     $sa_text_data = array();
1126     
1127     // get english labels (these should be complete)
1128     @include($INSTALL_PATH.'program/localization/en_US/labels.inc');
1129     @include($INSTALL_PATH.'program/localization/en_US/messages.inc');
1130
1131     if (is_array($labels))
1132       $sa_text_data = $labels;
1133     if (is_array($messages))
1134       $sa_text_data = array_merge($sa_text_data, $messages);
1135     
1136     // include user language files
1137     if ($sess_user_lang!='en' && is_dir($INSTALL_PATH.'program/localization/'.$sess_user_lang))
1138       {
1139       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/labels.inc');
1140       include_once($INSTALL_PATH.'program/localization/'.$sess_user_lang.'/messages.inc');
1141
1142       if (is_array($labels))
1143         $sa_text_data = array_merge($sa_text_data, $labels);
1144       if (is_array($messages))
1145         $sa_text_data = array_merge($sa_text_data, $messages);
1146       }
1147       
1148     $s_language = $sess_user_lang;
1149     }
1150
1151   // text does not exist
1152   if (!($text_item = $sa_text_data[$alias]))
1153     {
1154     /*
1155     raise_error(array('code' => 500,
1156                       'type' => 'php',
1157                       'line' => __LINE__,
1158                       'file' => __FILE__,
1159                       'message' => "Missing localized text for '$alias' in '$sess_user_lang'"), TRUE, FALSE);
1160     */
1161     return "[$alias]";
1162     }
1163
1164   // make text item array 
1165   $a_text_item = is_array($text_item) ? $text_item : array('single' => $text_item);
1166
1167   // decide which text to use
1168   if ($nr==1)
1169     $text = $a_text_item['single'];
1170   else if ($nr>0)
1171     $text = $a_text_item['multiple'];
1172   else if ($nr==0)
1173     {
1174     if ($a_text_item['none'])
1175       $text = $a_text_item['none'];
1176     else if ($a_text_item['single'])
1177       $text = $a_text_item['single'];
1178     else if ($a_text_item['multiple'])
1179       $text = $a_text_item['multiple'];
1180     }
1181
1182   // default text is single
1183   if ($text=='')
1184     $text = $a_text_item['single'];
1185
1186   // replace vars in text
1187   if (is_array($attrib['vars']))
1188     {
1189     foreach ($attrib['vars'] as $var_key=>$var_value)
1190       $a_replace_vars[substr($var_key, 0, 1)=='$' ? substr($var_key, 1) : $var_key] = $var_value;
1191     }
1192
1193   if ($a_replace_vars)
1194     $text = preg_replace('/\${?([_a-z]{1}[_a-z0-9]*)}?/ei', '$a_replace_vars["\1"]', $text);
1195
1196   // remove variables in text which were not available in arg $vars and $nr
1197   eval("\$text = <<<EOF
1198 $text
1199 EOF;
1200 ");
1201
1202   // format output
1203   if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst'])
1204     return ucfirst($text);
1205   else if ($attrib['uppercase'])
1206     return strtoupper($text);
1207   else if ($attrib['lowercase'])
1208     return strtolower($text);
1209
1210   return $text;
1211   }
1212
1213
1214 // send HTTP header for no-cacheing steps
1215 function send_nocacheing_headers()
1216   {
1217   if (headers_sent())
1218     return;
1219
1220   header("Expires: ".gmdate("D, d M Y H:i:s")." GMT");
1221   header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
1222   header("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
1223   header("Pragma: no-cache");
1224   }
1225
1226
1227 // send header with expire date 30 days in future
1228 function send_future_expire_header($offset=2600000)
1229   {
1230   if (headers_sent())
1231     return;
1232
1233   header("Expires: ".gmdate("D, d M Y H:i:s", mktime()+$offset)." GMT");
1234   header("Cache-Control: max-age=$offset");
1235   header("Pragma: ");
1236   }
1237
1238
1239 // check request for If-Modified-Since and send an according response
1240 function send_modified_header($mdate, $etag=null)
1241 {
1242   if (headers_sent())
1243     return;
1244     
1245   $iscached = false;
1246   if ($_SERVER['HTTP_IF_MODIFIED_SINCE'] && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mdate)
1247     $iscached = true;
1248   
1249   $etag = $etag ? "\"$etag\"" : null;
1250   if ($etag && $_SERVER['HTTP_IF_NONE_MATCH'] == $etag)
1251     $iscached = true;
1252   
1253   if ($iscached)
1254     header("HTTP/1.x 304 Not Modified");
1255   else
1256     header("Last-Modified: ".gmdate("D, d M Y H:i:s", $mdate)." GMT");
1257   
1258   header("Cache-Control: max-age=0");
1259   header("Expires: ");
1260   header("Pragma: ");
1261   
1262   if ($etag)
1263     header("Etag: $etag");
1264   
1265   if ($iscached)
1266     exit;
1267 }
1268
1269
1270 /**
1271  * Convert a variable into a javascript notation string
1272  */
1273 function json_serialize($var)
1274   {
1275     if (is_object($var))
1276       $var = get_object_vars($var);
1277
1278     if (is_array($var))
1279     {
1280       // empty array
1281       if (!sizeof($var))
1282         return '[]';
1283       else
1284       {
1285         $keys_arr = array_keys($var);
1286         $is_assoc = $have_numeric = 0;
1287
1288         for ($i=0; $i<sizeof($keys_arr); ++$i)
1289         {
1290           if (is_numeric($keys_arr[$i]))
1291             $have_numeric = 1;
1292           if (!is_numeric($keys_arr[$i]) || $keys_arr[$i] != $i)
1293             $is_assoc = 1;
1294           if ($is_assoc && $have_numeric)
1295             break;
1296         }
1297         
1298         $brackets = $is_assoc ? '{}' : '[]';
1299         $pairs = array();
1300
1301         foreach ($var as $key => $value)
1302         {
1303           // enclose key with quotes if it is not variable-name conform
1304           if (!ereg("^[_a-zA-Z]{1}[_a-zA-Z0-9]*$", $key) /* || is_js_reserved_word($key) */)
1305             $key = "'$key'";
1306
1307           $pairs[] = sprintf("%s%s", $is_assoc ? "$key:" : '', json_serialize($value));
1308         }
1309
1310         return $brackets{0} . implode(',', $pairs) . $brackets{1};
1311       }
1312     }
1313     else if (is_numeric($var) && strval(intval($var)) === strval($var))
1314       return $var;
1315     else if (is_bool($var))
1316       return $var ? '1' : '0';
1317     else
1318       return "'".JQ($var)."'";
1319   
1320   }
1321
1322 /**
1323  * function to convert an array to a javascript array
1324  * @deprecated
1325  */
1326 function array2js($arr, $type='')
1327   {
1328   return json_serialize($arr);
1329   }
1330
1331
1332 /**
1333  * Similar function as in_array() but case-insensitive
1334  */
1335 function in_array_nocase($needle, $haystack)
1336   {
1337   foreach ($haystack as $value)
1338     {
1339     if (strtolower($needle)===strtolower($value))
1340       return TRUE;
1341     }
1342     
1343   return FALSE;
1344   }
1345
1346
1347 /**
1348  * Find out if the string content means TRUE or FALSE
1349  */
1350 function get_boolean($str)
1351   {
1352   $str = strtolower($str);
1353   if(in_array($str, array('false', '0', 'no', 'nein', ''), TRUE))
1354     return FALSE;
1355   else
1356     return TRUE;
1357   }
1358
1359
1360 // parse a human readable string for a number of bytes
1361 function parse_bytes($str)
1362   {
1363   if (is_numeric($str))
1364     return intval($str);
1365     
1366   if (preg_match('/([0-9]+)([a-z])/i', $str, $regs))
1367     {
1368       $bytes = floatval($regs[1]);
1369       switch (strtolower($regs[2]))
1370       {
1371         case 'g':
1372           $bytes *= 1073741824;
1373           break;
1374         case 'm':
1375           $bytes *= 1048576;
1376           break;
1377         case 'k':
1378           $bytes *= 1024;
1379           break;
1380       }
1381     }
1382
1383   return intval($bytes);
1384   }
1385     
1386 // create a human readable string for a number of bytes
1387 function show_bytes($bytes)
1388   {
1389   if ($bytes > 1073741824)
1390     {
1391     $gb = $bytes/1073741824;
1392     $str = sprintf($gb>=10 ? "%d GB" : "%.1f GB", $gb);
1393     }
1394   else if ($bytes > 1048576)
1395     {
1396     $mb = $bytes/1048576;
1397     $str = sprintf($mb>=10 ? "%d MB" : "%.1f MB", $mb);
1398     }
1399   else if ($bytes > 1024)
1400     $str = sprintf("%d KB",  round($bytes/1024));
1401   else
1402     $str = sprintf('%d B', $bytes);
1403
1404   return $str;
1405   }
1406
1407
1408 // convert paths like ../xxx to an absolute path using a base url
1409 function make_absolute_url($path, $base_url)
1410     {
1411     $host_url = $base_url;
1412     $abs_path = $path;
1413
1414     // cut base_url to the last directory
1415     if (strpos($base_url, '/')>7)
1416       {
1417       $host_url = substr($base_url, 0, strpos($base_url, '/'));
1418       $base_url = substr($base_url, 0, strrpos($base_url, '/'));
1419       }
1420
1421     // $path is absolute
1422     if ($path{0}=='/')
1423       $abs_path = $host_url.$path;
1424     else
1425       {
1426       // strip './' because its the same as ''
1427       $path = preg_replace('/^\.\//', '', $path);
1428
1429       if(preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER))
1430         foreach($matches as $a_match)
1431           {
1432           if (strrpos($base_url, '/'))
1433             $base_url = substr($base_url, 0, strrpos($base_url, '/'));
1434           
1435           $path = substr($path, 3);
1436           }
1437
1438       $abs_path = $base_url.'/'.$path;
1439       }
1440       
1441     return $abs_path;
1442     }
1443
1444
1445 // wrapper function for strlen
1446 function rc_strlen($str)
1447   {
1448     if (function_exists('mb_strlen'))
1449       return mb_strlen($str);
1450     else
1451       return strlen($str);
1452   }
1453   
1454 // wrapper function for strtolower
1455 function rc_strtolower($str)
1456   {
1457     if (function_exists('mb_strtolower'))
1458       return mb_strtolower($str);
1459     else
1460       return strtolower($str);
1461   }
1462
1463 // wrapper function for substr
1464 function rc_substr($str, $start, $len=null)
1465   {
1466   if (function_exists('mb_substr'))
1467     return mb_substr($str, $start, $len);
1468   else
1469     return substr($str, $start, $len);
1470   }
1471
1472 // wrapper function for strpos
1473 function rc_strpos($haystack, $needle, $offset=0)
1474   {
1475   if (function_exists('mb_strpos'))
1476     return mb_strpos($haystack, $needle, $offset);
1477   else
1478     return strpos($haystack, $needle, $offset);
1479   }
1480
1481 // wrapper function for strrpos
1482 function rc_strrpos($haystack, $needle, $offset=0)
1483   {
1484   if (function_exists('mb_strrpos'))
1485     return mb_strrpos($haystack, $needle, $offset);
1486   else
1487     return strrpos($haystack, $needle, $offset);
1488   }
1489
1490
1491 // replace the middle part of a string with ...
1492 // if it is longer than the allowed length
1493 function abbrevate_string($str, $maxlength, $place_holder='...')
1494   {
1495   $length = rc_strlen($str);
1496   $first_part_length = floor($maxlength/2) - rc_strlen($place_holder);
1497   
1498   if ($length > $maxlength)
1499     {
1500     $second_starting_location = $length - $maxlength + $first_part_length + 1;
1501     $str = rc_substr($str, 0, $first_part_length) . $place_holder . rc_substr($str, $second_starting_location, $length);
1502     }
1503
1504   return $str;
1505   }
1506
1507
1508 // make sure the string ends with a slash
1509 function slashify($str)
1510   {
1511   return unslashify($str).'/';
1512   }
1513
1514
1515 // remove slash at the end of the string
1516 function unslashify($str)
1517   {
1518   return preg_replace('/\/$/', '', $str);
1519   }
1520   
1521
1522 // delete all files within a folder
1523 function clear_directory($dir_path)
1524   {
1525   $dir = @opendir($dir_path);
1526   if(!$dir) return FALSE;
1527
1528   while ($file = readdir($dir))
1529     if (strlen($file)>2)
1530       unlink("$dir_path/$file");
1531
1532   closedir($dir);
1533   return TRUE;
1534   }
1535
1536
1537 // create a unix timestamp with a specified offset from now
1538 function get_offset_time($offset_str, $factor=1)
1539   {
1540   if (preg_match('/^([0-9]+)\s*([smhdw])/i', $offset_str, $regs))
1541     {
1542     $amount = (int)$regs[1];
1543     $unit = strtolower($regs[2]);
1544     }
1545   else
1546     {
1547     $amount = (int)$offset_str;
1548     $unit = 's';
1549     }
1550     
1551   $ts = mktime();
1552   switch ($unit)
1553     {
1554     case 'w':
1555       $amount *= 7;
1556     case 'd':
1557       $amount *= 24;
1558     case 'h':
1559       $amount *= 60;
1560     case 'm':
1561       $amount *= 60;
1562     case 's':
1563       $ts += $amount * $factor;
1564     }
1565
1566   return $ts;
1567   }
1568
1569
1570 /**
1571  * strrstr
1572  *
1573  * return the last occurence of a string in another string
1574  * @param haystack string string in which to search
1575  * @param needle string string for which to search
1576  * @return index of needle within haystack, or false if not found
1577  */
1578 function strrstr($haystack, $needle)
1579   {
1580     $pver = phpversion();
1581     if ($pver[0] >= 5)
1582       {
1583         return strrpos($haystack, $needle);
1584       }
1585     else
1586       {
1587         $index = strpos(strrev($haystack), strrev($needle));
1588         if($index === false) {
1589             return false;
1590         }
1591         $index = strlen($haystack) - strlen($needle) - $index;
1592         return $index;
1593       }
1594   }
1595
1596
1597 ?>