source: github/program/include/rcube_shared.inc @ 86df152

HEADcourier-fixdev-browser-capabilitiespdorelease-0.6release-0.7release-0.8
Last change on this file since 86df152 was 86df152, checked in by thomascube <thomas@…>, 6 years ago

Error handling for attachment uploads; multibyte-safe string functions; XSS improvements

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