Code

f38f3bab81eb608f548871ba84a78b5674dc6fbb
[gosa.git] / include / sieve / class_My_Tree.inc
1 <?php
5 /* This class is inherited from the original 'Tree'
6  *  class written by Heiko Hund.
7  * It is partly rewritten to create a useable html interface 
8  *  for each single sieve token. 
9  * This gives us the ability to edit existing sieve filters. 
10  */
11 class My_Tree extends Tree
12 {
13   var $dumpFn_;
14   var $dump_;
16   var $mode_stack = array();
17   var $pap              = array();
19   function execute()
20   {
21     return($this->dump());
22   }
24   /* Create a html interface for the current sieve filter 
25    */
26   function dump()
27   {
28     /**************
29      * Handle new elements 
30      **************/
32     /* Only parse the tokens once */
33     if(!count($this->pap)){
34       $this->dump_ = "";
35       $this->mode_stack = array();
36       $this->pap = array();
37       $this->doDump_(0, '', true);
38     }
40     /* Create html results */
41     $smarty = get_smarty();
43     $this -> dump_ = "";
44     foreach($this->pap as $key => $object){
45       if(is_object($object)){
46         $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); 
47       }
48     }
49     
50     return($this->dump_);
51   }
54   /* This function walks through the object tree generated by the "Parse" class.
55    * All Commands will be resolved and grouped. So the Commands and their 
56    *  parameter are combined. Like "IF" and ":comparator"...
57    */  
58   function doDump_($node_id, $prefix, $last,$num = 1)
59   {
60     /* Indicates that current comman will only be valid for a single line. 
61      *  this command type will be removed from mode_stack after displaying it.
62      */
63     $rewoke_last = FALSE;
65     /* Get node */ 
66     $node = $this->nodes_[$node_id];
68     /* This closes the last mode */
69     if($node['class'] == "block-start"){
70       $tmp = array_pop($this->mode_stack);
71       $this->handle_elements($tmp,$node_id);
72       $this->handle_elements(array("TYPE" => "block_start"),preg_replace("/[^0-9]/","",microtime()));
73     }
75     /* This closes the last mode */
76     if($node['class'] == "block-end"){
77       $tmp = array_pop($this->mode_stack);
78       $this->handle_elements($tmp,$node_id);
79       $this->handle_elements(array("TYPE" => "block_end"),preg_replace("/[^0-9]/","",microtime()));
80     }
82     /* Semicolon indicates a new command */
83     if($node['class'] == "semicolon"){
84       $tmp =array_pop($this->mode_stack);
85       $this->handle_elements($tmp,$node_id);
86     }
88     /* Handle comments */
89     if($node['class'] == "comment"){
90       $this->mode_stack[] = array("TYPE" => $node['class']);
91       $rewoke_last = TRUE;
92     }
94     /* Handle identifiers */
95     $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
96     if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
97       $this->mode_stack[] = array("TYPE" => $node['text']); 
98     }
100     /* Add current node to current command stack */
101     end($this->mode_stack);
102     $key = key($this->mode_stack);
103     $this->mode_stack[$key]['ELEMENTS'][] = $node;
105     /* Remove last mode from mode stack, cause it was only valid for a single line */
106     if($rewoke_last){
107       $tmp =array_pop($this->mode_stack);
108       $this->handle_elements($tmp,$node_id);
109     }
111     /* If this is a sub element, just call this for all childs */       
112     if(isset($this->childs_[$node_id])){
113       $childs = $this->childs_[$node_id];
114       for ($i=0; $i<count($childs); ++$i)
115       {
116         $c_last = false;
117         if ($i+1 == count($childs))
118         {
119           $c_last = true;
120         }
121         $this->doDump_($childs[$i], "", $num);
122       }
123     }
124   }
127   /* Create a class for each resolved object.
128    * And append this class to a list of objects.
129    */
130   function handle_elements($data,$id)
131   {
132     if(!isset($data['TYPE'])){
133       return;
134     }
135     $type = $data['TYPE'];
136     
137     $class_name= "sieve_".$type ;
138     if(class_exists($class_name)){
139       $this->pap[] = new $class_name($data,$id);
140     }else{
141       echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
142     }
143   }
145   function save_object()
146   {
147     reset($this->pap);
148     foreach($this->pap as $key => $obj){
150       if(in_array(get_class($obj),array("sieve_if",
151                                         "sieve_elsif",
152                                         "sieve_vacation",
153                                         "sieve_comment",
154                                         "sieve_reject",
155                                         "sieve_fileinto",
156                                         "sieve_require",
157                                         "sieve_redirect"))){
160         if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
161           $this->pap[$key]->save_object();
162         }
163       }
164     }
165   }
168   /* Remove the object at the given position */
169   function remove_object($key_id)
170   {
171     $class = get_class($this->pap[$key_id]);
172     if(in_array($class,array("sieve_if","sieve_elsif","sieve_else"))){
173       $block_start= $key_id;
174       $block_end  = $this->get_block_end($key_id);
176       for($i = $block_start ; $i <= $block_end ; $i ++ ){
177         unset($this->pap[$i]);
178       }
179     }else{
180       unset($this->pap[$key_id]);
181     }
182     $tmp = array();
183     foreach($this->pap as $element){
184       $tmp[] = $element;
185     }
186     $this->pap = $tmp;
187   }
190   /* This function moves a given element to another position.
191    * Single elements like "keep;" will simply be moved one posisition down/up.
192    * Multiple elements like if-elsif-else will be moved as block. 
193    * 
194    *  $key_id     specified the element that should be moved.
195    *  $direction  specifies to move elements "up" or "down"
196    */
197   function move_up_down($key_id,$direction = "down")
198   {
199      
200     /* Get the current element to decide what to move. */ 
201     $e_class = get_class($this->pap[$key_id]);
202       
203     if(in_array($e_class,array("sieve_if"))){
204       $block_start= $key_id;
205       $block_end  = $this->get_block_end($key_id);
207       /* Depending on the direction move up down */
208       if($direction == "down"){
209         $next_free  = $this->_get_next_free_move_slot($block_end,$direction); 
210       }else{
211         $next_free  = $this->_get_next_free_move_slot($block_start,$direction); 
212       }
214       /* Move the given block */ 
215       $this->move_multiple_elements($block_start,$block_end,$next_free);
216     }
218     if(in_array($e_class,array( "sieve_stop",
219                                 "sieve_keep",
220                                 "sieve_require",
221                                 "sieve_comment",
222                                 "sieve_vacation",
223                                 "sieve_stop",   
224                                 "sieve_reject", 
225                                 "sieve_fileinto",
226                                 "sieve_redirect", 
227                                 "sieve_discard"))){
228       $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
229     }
230   }
232   
233   /* Move the given block to position */
234   function move_multiple_elements($start,$end,$to)
235   {
236     /* Use class names for testing */
237     $data = $this->pap;
239     /* Get block to move */
240     $block_to_move = array_slice($data,$start, ($end - $start +1));
242     /* We want do move this block up */
243     if($end > $to){
244       
245       /* Get start block */
246       $start_block = array_slice($data,0,$to);
248       /* Get Get all elements between the block to move 
249        *  and next free position 
250        */
251       $block_to_free = array_slice($data,$to ,$start - $to );  
252       $block_to_end = array_slice($data,$end+1);
253       $new = array();
254       foreach($start_block as $block){
255         $new[] = $block;
256       }
257       foreach($block_to_move as $block){
258         $new[] = $block;
259       }
260       foreach($block_to_free as $block){
261         $new[] = $block;
262       }
263       foreach($block_to_end as $block){
264         $new[] = $block;
265       }
266       $old = $this->pap;
267       $this->pap = $new;
268     }
269     
271     /* We want to move this block down. */
272     if($to > $end){
274       /* Get start block */
275       $start_block = array_slice($data,0,$start);
277       /* Get Get all elements between the block to move 
278        *  and next free position 
279        */
280       $block_to_free = array_slice($data,$end +1,($to - $end  ));  
282       /* Get the rest 
283        */
284       $block_to_end = array_slice($data,$to+1);
286       $new = array();
287       foreach($start_block as $block){
288         $new[] = $block;
289       }
290       foreach($block_to_free as $block){
291         $new[] = $block;
292       }
293       foreach($block_to_move as $block){
294         $new[] = $block;
295       }
296       foreach($block_to_end as $block){
297         $new[] = $block;
298       }
299       $old = $this->pap;
300       $this->pap = $new;
301     }
302   }  
304   
305   /* This function returns the id of the element 
306    *  where the current block ends  
307    */
308   function get_block_end($start)
309   {
310     /* Only execute if this is a really a block element. 
311      * Block elements is only sieve_if
312      */
313     if(in_array(get_class($this->pap[$start]),array("sieve_if","sieve_elsif","sieve_else"))){
315       $class      = get_class($this->pap[$start]);
316       $next_class = get_class($this->pap[$start+1]);
317       $block_depth = 0;
319       $end = FALSE;
321       while(!$end && $start < count($this->pap)){
322  
323         if($class == "sieve_block_start"){
324           $block_depth ++;
325         }
327         if($class == "sieve_block_end"){
328           $block_depth --;
329         }
331         if( $block_depth == 0 && 
332             $class == "sieve_block_end" && 
333             !in_array($next_class,array("sieve_else","sieve_elsif"))){
334           $end = TRUE;
335           $start --;
336         }
337         $start ++;       
338         $class      = get_class($this->pap[$start]);
339         
340         if(isset($this->pap[$start+1])){ 
341           $next_class = get_class($this->pap[$start+1]);
342         }else{
343           $next_class ="";
344         }
345       }
346     }
347     return($start);
348   }
351   /* This function moves the single element at 
352    *  position $from to position $to.
353    */
354   function move_single_element($from,$to)
355   {
356     if($from == $to) {
357       return;
358     }
360     $ret = array();
361     $tmp = $this->pap;
363     $begin = array();
364     $middle = array();
365     $end = array();
366     $element = $this->pap[$from];
368     if($from > $to ){
370       /* Get all element in fron to element to move */    
371       if($from  != 0){
372         $begin = array_slice($tmp,0,$to);
373       }
375       /* Get all elements between */
376       $middle = array_slice($tmp,$to , ($from - ($to) ));  
377     
378       /* Get the rest */ 
379       $end  = array_slice($tmp,$from+1);
380  
381       foreach($begin as $data){
382         $ret[] = $data;
383       }
384       $ret[] = $element;
385       foreach($middle as $data){
386         $ret[] = $data;
387       }
388       foreach($end as $data){
389         $ret[] = $data;
390       }
391       $this->pap = $ret;
392     }
393     if($from < $to ){
395       /* Get all element in fron to element to move */    
396       if($from  != 0){
397         $begin = array_slice($tmp,0,$from);
398       }
400       /* Get all elements between */
401       $middle = array_slice($tmp,$from+1 , ($to - ($from)));  
402     
403       /* Get the rest */ 
404       $end  = array_slice($tmp,$to+1);
405  
406       foreach($begin as $data){
407         $ret[] = $data;
408       }
409       foreach($middle as $data){
410         $ret[] = $data;
411       }
412       $ret[] = $element;
413       foreach($end as $data){
414         $ret[] = $data;
415       }
416       $this->pap = $ret;
417     }
418   }
421   /* Returns the next free position where we 
422    *  can add a new sinle element 
423    *    $key_id     = Current position
424    *    $direction  = Forward or backward.
425    */
426   function _get_next_free_move_slot($key_id,$direction)
427   {
428     $last_class = "";
429     $current_class ="";
430     $next_class = "";
432     /* After this elements we can add new elements 
433      *  without having any trouble.
434      */
435     $allowed_to_add_after = array("sieve_keep",
436                                   "sieve_require", 
437                                   "sieve_stop", 
438                                   "sieve_reject", 
439                                   "sieve_fileinto", 
440                                   "sieve_redirect", 
441                                   "sieve_discard",
442                                   "sieve_comment",
443                                   "sieve_block_start"
444                                  );
446     /* Before this elements we can add new elements 
447      *  without having any trouble.
448      */
449     $allowed_to_add_before = array("sieve_keep",
450                                   "sieve_require", 
451                                   "sieve_stop", 
452                                   "sieve_reject", 
453                                   "sieve_fileinto", 
454                                   "sieve_comment",
455                                   "sieve_redirect", 
456                                   "sieve_discard",
457                                   "sieve_if", 
458                                   "sieve_block_end"
459                                  );
461     if($direction == "down"){
462     
463       $test = $this->pap;
464       while($key_id < count($test)){
465         if(($key_id+1) == count($test)) {
466           return($key_id);
467         }
468         $key_id ++;
469         $current_class  = get_class($test[$key_id]);
470         if(in_array($current_class, $allowed_to_add_after)){
471           return($key_id);
472         } 
473       } 
474     }else{
475   
476       $test = $this->pap;
477       if($key_id == 0) {
478         return($key_id);
479       }
480       $key_id --;
481       while($key_id >=0 ){
482         $current_class  = get_class($test[$key_id]);
483         if(in_array($current_class, $allowed_to_add_before)){
484           return($key_id);
485         } 
486         $key_id --;
487       }
488       return(0);
489     }
490   }
493   /* Need to be reviewed */
494   function get_sieve_script()
495   {
496     $tmp ="";
497     if(count($this->pap)){
498       $buffer = "";    
499       foreach($this->pap as $part)  {
500         if(get_class($part) == "sieve_block_end"){
501           $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
502         }
503         $tmp2 = $part->get_sieve_script_part();
505         if(get_class($part) == "sieve_reject"){
506           $tmp.=$tmp2;
507         }else{
509           $tmp3 = split("\n",$tmp2);
510           foreach($tmp3 as $str){
511             $str2 = trim($str);
512             if(empty($str2)) continue;
513             $tmp.= $buffer.$str."\n";
514           }
515         }
516         if(get_class($part) == "sieve_block_start"){
517           $buffer .= SIEVE_INDENT_TAB;
518         }
519       }
520     }
521     if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
522       $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
523     }
524     return($tmp);
525   }
527   function check()
528   {
529                 $msgs = array();
531     /* Some logical checks. 
532      *  like :  only sieve_comment can appear before require.
533      */
534     
535     /* Ensure that there are no command before require 
536      *  - Get id of last require tag
537      *  - Collect object types in from of this tag. 
538      *  - Check if there are tags collected that are not allowed 
539      */
540     $last_found_at = -1; 
541     $objs = array();
542     foreach($this->pap as $key => $obj){
543       if(get_class($obj) == "sieve_require"){
544         $last_found_at = $key;
545       }
546     }
547     foreach($this->pap as $key => $obj){
548       if($key == $last_found_at) break;  
549       if(!in_array(get_class($obj),array("sieve_comment","sieve_require"))){
550         $objs[] = get_class($obj);
551       }
552     }
553     if(count($objs) && $last_found_at != -1){
554       $str = _("Require must be the first command in the script.");  
555       $msgs[] = $str;
556       print_red($str);;
557     }
558     
559                 foreach($this->pap as $obj){
560                         $o_msgs = $obj->check();
561                         foreach($o_msgs as $o_msg){
562                                 $msgs[] = $o_msg;
563                         }
564                 }
565                 return($msgs);
566   }
570 /* Create valid sieve string/string-list 
571  *  out of a given array
572  */
573 function sieve_create_strings($data,$force_string = FALSE)
575   $ret = "";
576   if(is_array($data)){
577     if(count($data) == 1){
578       $ret = "\"";
579       foreach($data as $dat){
580         $ret .=$dat;
581       }
582       $ret.="\"";
583     }else{
584       foreach($data as $dat){
585         $ret.= "\"";
586         $ret.=$dat;
587         $ret.="\", ";
588       }
589       $ret = preg_replace("/,$/","",trim($ret));
590       $ret = "[".$ret."]";
591     }
592   }else{
594     $Multiline = preg_match("/\n/",$data);
595     $data = preg_replace("/\r/","",$data);;
597     if($Multiline && !$force_string){
598       $ret = "text: \r\n".$data."\r\n.\r\n";
599     }else{
600       $ret = "\"".$data."\"";
601     }
602   }
603   $ret = preg_replace("/\"\"/","\"",$ret);
604   $ret = preg_replace("/\n/","\r\n",$ret);
605   
606   return($ret);
609 /* This checks if there is a string at the current position 
610  *  in the token array. 
611  * If there is a string list at the current position,
612  *  this function will return a complete list of all
613  *  strings used in this list.
614  * It also returns an offset of the last token position 
615  */
616 function sieve_get_strings($data,$id)
618   $ret = array();
619   if($data[$id]['class'] == "left-bracket"){
620     while($data[$id]['class']  != "right-bracket" && $id < count($data)){
621       
622       if($data[$id]['class'] == "quoted-string"){
623         $ret[] = $data[$id]['text'];
624       }
625       $id ++;
626     }
627   }elseif($data[$id]['class'] == "quoted-string"){
628     $ret[] = $data[$id]['text'];
629   }elseif($data[$id]['class'] == "number"){
630     $ret[] = $data[$id]['text'];
631   }elseif($data[$id]['class'] == "multi-line"){
632     $str = trim(preg_replace("/^text:/","",$data[$id]['text']));
633     $str = trim(preg_replace("/\.$/","",$str));
634     $ret[] = $str;
635   }
636   
637   return(array("OFFSET" => $id, "STRINGS" => $ret));
640 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
641 ?>