Code

Managed to edit script that can't be parsed ... .
[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   var $add_new          = FALSE;
20   var $add_new_id       = 0;
21   var $add_type         = "top";
22   var $add_element_type = "";
23   
24   var $Mode = "Structured";
26   function execute()
27   {
28     foreach($_POST as $name => $value){
29       if(preg_match("/Add_Test_Object_/",$name)) {
30         $name = preg_replace("/Add_Test_Object_/","",$name);
31         $name = preg_replace("/_(x|y)$/","",$name);
32     
33         $test_types_to_add = array(
34           "address" =>_("Address"),
35           "header"  =>_("Header"),
36           "envelope"=>_("Envelope"),
37           "size"    =>_("Size"),
38           "exists"  =>_("Exists"),
39           "allof"   =>_("All of"),
40           "anyof"   =>_("Any of"),
41           "true"    =>_("True"),
42           "false"   =>_("False"));
43  
44         $smarty = get_smarty();
45         $smarty->assign("ID",$name); 
46         $smarty->assign("test_types_to_add",$test_types_to_add); 
47         $ret = $smarty->fetch(get_template_path("templates/select_test_type.tpl",TRUE,dirname(__FILE__)));
48         return($ret);
49       }
50     }
52     return($this->dump());
53   }
55   /* Create a html interface for the current sieve filter 
56    */
57   function dump()
58   {
59     error_reporting(E_ALL);
61     /**************
62      * Handle new elements 
63      **************/
65     if(isset($_POST['select_new_element_type_cancel'])){
66       $this->add_new = FALSE;
67     }
69     if($this->add_new){
70   
71       $element_types= array(
72           "sieve_keep"      => _("Keep"),
73           "sieve_comment"   => _("Comment"),
74           "sieve_fileinto"  => _("File into"),
75           "sieve_keep"      => _("Keep"),
76           "sieve_discard"   => _("Discard"),
77           "sieve_redirect"  => _("Redirect"),
78           "sieve_reject"    => _("Reject"),
79           "sieve_require"   => _("Require"),
80           "sieve_stop"      => _("Stop"),
81           "sieve_vacation"  => _("Vacation message"),
82           "sieve_if"        => _("If"));
85       /* Element selected */
86       if(isset($_POST['element_type']) && isset($element_types[$_POST['element_type']])){
87         $this->add_element_type = $_POST['element_type'];
88       }
90       /* Create new element and add it at the selected position */
91       if(isset($_POST['select_new_element_type'])){
93         $ele[] = new $this->add_element_type(NULL, preg_replace("/[^0-9]/","",microtime()));
94         if($this->add_element_type == "sieve_if"){
95           $ele[] = new sieve_block_start(NULL,preg_replace("/[^0-9]/","",microtime()));
96           $ele[] = new sieve_block_end(NULL,preg_replace("/[^0-9]/","",microtime()));
97         }
98         $start = $end = array();
99         $found = false;  
101         /* Add above current element*/
102         if($this->add_type == "top"){
103           foreach($this->pap as $key => $obj){
104             if($obj->object_id == $this->add_new_id){
105               $found = true;
106             }
107             if(!$found){
108               $start[] = $obj;
109             }else{
110               $end[] = $obj;
111             }
112           }    
113         }else{
114         /* Add below current element */
115           foreach($this->pap as $key => $obj){
116             if(!$found){
117               $start[] = $obj;
118             }else{
119               $end[] = $obj;
120             }
121             if($obj->object_id == $this->add_new_id){
122               $found = true;
123             }
124           }    
125         }
127         /* Only add, if current element could be located */
128         if($found){
129           $new = array();
130           foreach($start as $obj){
131             $new[] = $obj;
132           }      
133           foreach($ele as $el){
134             $new[] = $el;
135           }
136           foreach($end as $obj){
137             $new[] = $obj;
138           }
139           $this->pap = $new;
140           $this->add_new = FALSE;
141         }else{
142           print_red(_("Something went wrong while adding a new entry."));
143         }
144       }
146     }
148     /* Only display select dialog if it is necessary */
149     if($this->add_new){  
150       $smarty = get_smarty();
151       $smarty->assign("element_types",$element_types );
152       $smarty->assign("element_type",$this->add_element_type);
153       $str = $smarty->fetch(get_template_path("templates/add_element.tpl",TRUE,dirname(__FILE__)));
154       return($str);
155     }
157     /* Only parse the tokens once */
158     if(!count($this->pap)){
159       $this->dump_ = "";
160       $this->mode_stack = array();
161       $this->pap = array();
162       $this->doDump_(0, '', true);
163     }
165     /* Create html results */
166     $smarty = get_smarty();
168     $this -> dump_ = "";
169     foreach($this->pap as $key => $object){
170       if(is_object($object)){
171         $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); 
172       }
173     }
174     
175     return($this->dump_);
176   }
179   /* This function walks through the object tree generated by the "Parse" class.
180    * All Commands will be resolved and grouped. So the Commands and their 
181    *  parameter are combined. Like "IF" and ":comparator"...
182    */  
183   function doDump_($node_id, $prefix, $last,$num = 1)
184   {
185     /* Indicates that current comman will only be valid for a single line. 
186      *  this command type will be removed from mode_stack after displaying it.
187      */
188     $rewoke_last = FALSE;
190     /* Get node */ 
191     $node = $this->nodes_[$node_id];
193     /* This closes the last mode */
194     if($node['class'] == "block-start"){
195       $tmp = array_pop($this->mode_stack);
196       $this->handle_elements($tmp,$node_id);
197       $this->handle_elements(array("TYPE" => "block_start"),$node_id);
198     }
200     /* This closes the last mode */
201     if($node['class'] == "block-end"){
202       $tmp = array_pop($this->mode_stack);
203       $this->handle_elements($tmp,$node_id);
204       $this->handle_elements(array("TYPE" => "block_end"),$node_id);
205     }
207     /* Semicolon indicates a new command */
208     if($node['class'] == "semicolon"){
209       $tmp =array_pop($this->mode_stack);
210       $this->handle_elements($tmp,$node_id);
211     }
213     /* Handle comments */
214     if($node['class'] == "comment"){
215       $this->mode_stack[] = array("TYPE" => $node['class']);
216       $rewoke_last = TRUE;
217     }
219     /* Handle identifiers */
220     $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
221     if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
222       $this->mode_stack[] = array("TYPE" => $node['text']); 
223     }
225     /* Add current node to current command stack */
226     end($this->mode_stack);
227     $key = key($this->mode_stack);
228     $this->mode_stack[$key]['ELEMENTS'][] = $node;
230     /* Remove last mode from mode stack, cause it was only valid for a single line */
231     if($rewoke_last){
232       $tmp =array_pop($this->mode_stack);
233       $this->handle_elements($tmp,$node_id);
234     }
236     /* If this is a sub element, just call this for all childs */       
237     if(isset($this->childs_[$node_id])){
238       $childs = $this->childs_[$node_id];
239       for ($i=0; $i<count($childs); ++$i)
240       {
241         $c_last = false;
242         if ($i+1 == count($childs))
243         {
244           $c_last = true;
245         }
246         $this->doDump_($childs[$i], "", $num);
247       }
248     }
249   }
252   /* Create a class for each resolved object.
253    * And append this class to a list of objects.
254    */
255   function handle_elements($data,$id)
256   {
257     if(!isset($data['TYPE'])){
258       return;
259     }
260     $type = $data['TYPE'];
261     
262     $class_name= "sieve_".$type ;
263     if(class_exists($class_name)){
264       $this->pap[] = new $class_name($data,$id);
265     }else{
266       echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
267     }
268   }
270   function save_object()
271   {
272     reset($this->pap);
273     foreach($this->pap as $key => $obj){
275       if(in_array(get_class($obj),array("sieve_if",
276                                         "sieve_elsif",
277                                         "sieve_vacation",
278                                         "sieve_comment",
279                                         "sieve_reject",
280                                         "sieve_fileinto",
281                                         "sieve_require",
282                                         "sieve_redirect"))){
285         if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
286           $this->pap[$key]->save_object();
287         }
288       }
290       $once = TRUE;
291       foreach($_POST as $name => $value){
293         if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
294           $once = FALSE;
295           $this->remove_object($key);
296         }
297         if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
298           $this->move_up_down($key,"up");
299           $once = FALSE;
300         }
301         if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
302           $this->move_up_down($key,"down");
303           $once = FALSE;
304         }
305         if(isset($obj->object_id) && preg_match("/^Add_Object_Top_".$obj->object_id."_/",$name) && $once){
306           $this->add_new_object($obj->object_id,"top");
307           $once = FALSE;
308         }
309         if(isset($obj->object_id) && preg_match("/^Add_Object_Bottom_".$obj->object_id."_/",$name) && $once){
310           $this->add_new_object($obj->object_id,"bottom");
311           $once = FALSE;
312         }
313       }
314     }
315   }
318   /* Add a new object at the given position */
319   function add_new_object($id,$top_bottom = "bottom")
320   {
321     $this->add_new    = TRUE;
322     $this->add_new_id = $id;
323     $this->add_type   = $top_bottom;
324   }
327   /* Remove the object at the given position */
328   function remove_object($key_id)
329   {
330     $class = get_class($this->pap[$key_id]);
331     if(in_array($class,array("sieve_if"))){
332       $block_start= $key_id;
333       $block_end  = $this->get_block_end($key_id);
334       for($i = $block_start ; $i <= $block_end ; $i ++ ){
335         unset($this->pap[$i]);
336       }
337     }else{
338       unset($this->pap[$key_id]);
339     }
340     $tmp = array();
341     foreach($this->pap as $element){
342       $tmp[] = $element;
343     }
344     $this->pap = $tmp;
345   }
348   /* This function moves a given element to another position.
349    * Single elements like "keep;" will simply be moved one posisition down/up.
350    * Multiple elements like if-elsif-else will be moved as block. 
351    * 
352    *  $key_id     specified the element that should be moved.
353    *  $direction  specifies to move elements "up" or "down"
354    */
355   function move_up_down($key_id,$direction = "down")
356   {
357      
358     /* Get the current element to decide what to move. */ 
359     $e_class = get_class($this->pap[$key_id]);
360       
361     if(in_array($e_class,array("sieve_if"))){
362       $block_start= $key_id;
363       $block_end  = $this->get_block_end($key_id);
365       /* Depending on the direction move up down */
366       if($direction == "down"){
367         $next_free  = $this->_get_next_free_move_slot($block_end,$direction); 
368       }else{
369         $next_free  = $this->_get_next_free_move_slot($block_start,$direction); 
370       }
372       /* Move the given block */ 
373       $this->move_multiple_elements($block_start,$block_end,$next_free);
374     }
376     if(in_array($e_class,array( "sieve_stop",
377                                 "sieve_keep",
378                                 "sieve_require",
379                                 "sieve_comment",
380                                 "sieve_stop",   
381                                 "sieve_reject", 
382                                 "sieve_fileinto",
383                                 "sieve_redirect", 
384                                 "sieve_discard"))){
385       $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
386     }
387   }
389   
390   /* Move the given block to position */
391   function move_multiple_elements($start,$end,$to)
392   {
393     /* Use class names for testing */
394     $data = $this->pap;
396     /* Get block to move */
397     $block_to_move = array_slice($data,$start, ($end - $start +1));
399     /* We want do move this block up */
400     if($end > $to){
401       
402       /* Get start block */
403       $start_block = array_slice($data,0,$to);
405       /* Get Get all elements between the block to move 
406        *  and next free position 
407        */
408       $block_to_free = array_slice($data,$to ,$start - $to );  
409       $block_to_end = array_slice($data,$end+1);
410       $new = array();
411       foreach($start_block as $block){
412         $new[] = $block;
413       }
414       foreach($block_to_move as $block){
415         $new[] = $block;
416       }
417       foreach($block_to_free as $block){
418         $new[] = $block;
419       }
420       foreach($block_to_end as $block){
421         $new[] = $block;
422       }
423       $old = $this->pap;
424       $this->pap = $new;
425     }
426     
428     /* We want to move this block down. */
429     if($to > $end){
431       /* Get start block */
432       $start_block = array_slice($data,0,$start);
434       /* Get Get all elements between the block to move 
435        *  and next free position 
436        */
437       $block_to_free = array_slice($data,$end +1,($to - $end  ));  
439       /* Get the rest 
440        */
441       $block_to_end = array_slice($data,$to+1);
443       $new = array();
444       foreach($start_block as $block){
445         $new[] = $block;
446       }
447       foreach($block_to_free as $block){
448         $new[] = $block;
449       }
450       foreach($block_to_move as $block){
451         $new[] = $block;
452       }
453       foreach($block_to_end as $block){
454         $new[] = $block;
455       }
456       $old = $this->pap;
457       $this->pap = $new;
458     }
459   }  
461   
462   /* This function returns the id of the element 
463    *  where the current block ends  
464    */
465   function get_block_end($start)
466   {
467     /* Only execute if this is a really a block element. 
468      * Block elements is only sieve_if
469      */
470     if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
472       $class      = get_class($this->pap[$start]);
473       $next_class = get_class($this->pap[$start+1]);
474       $block_depth = 0;
476       $end = FALSE;
478       while(!$end && $start < count($this->pap)){
479  
480         if($class == "sieve_block_start"){
481           $block_depth ++;
482         }
484         if($class == "sieve_block_end"){
485           $block_depth --;
486         }
488         if( $block_depth == 0 && 
489             $class == "sieve_block_end" && 
490             !in_array($next_class,array("sieve_else","sieve_elsif"))){
491           $end = TRUE;
492           $start --;
493         }
494         $start ++;       
495         $class      = get_class($this->pap[$start]);
496         $next_class = get_class($this->pap[$start+1]);
497       }
498     }
499     return($start);
500   }
503   /* This function moves the single element at 
504    *  position $from to position $to.
505    */
506   function move_single_element($from,$to)
507   {
508     if($from == $to) {
509       return;
510     }
512     $ret = array();
513     $tmp = $this->pap;
515     $begin = array();
516     $middle = array();
517     $end = array();
518     $element = $this->pap[$from];
520     if($from > $to ){
522       /* Get all element in fron to element to move */    
523       if($from  != 0){
524         $begin = array_slice($tmp,0,$to);
525       }
527       /* Get all elements between */
528       $middle = array_slice($tmp,$to , ($from - ($to) ));  
529     
530       /* Get the rest */ 
531       $end  = array_slice($tmp,$from+1);
532  
533       foreach($begin as $data){
534         $ret[] = $data;
535       }
536       $ret[] = $element;
537       foreach($middle as $data){
538         $ret[] = $data;
539       }
540       foreach($end as $data){
541         $ret[] = $data;
542       }
543       $this->pap = $ret;
544     }
545     if($from < $to ){
547       /* Get all element in fron to element to move */    
548       if($from  != 0){
549         $begin = array_slice($tmp,0,$from);
550       }
552       /* Get all elements between */
553       $middle = array_slice($tmp,$from+1 , ($to - ($from)));  
554     
555       /* Get the rest */ 
556       $end  = array_slice($tmp,$to+1);
557  
558       foreach($begin as $data){
559         $ret[] = $data;
560       }
561       foreach($middle as $data){
562         $ret[] = $data;
563       }
564       $ret[] = $element;
565       foreach($end as $data){
566         $ret[] = $data;
567       }
568       $this->pap = $ret;
569     }
570   }
573   /* Returns the next free position where we 
574    *  can add a new sinle element 
575    *    $key_id     = Current position
576    *    $direction  = Forward or backward.
577    */
578   function _get_next_free_move_slot($key_id,$direction)
579   {
580     $last_class = "";
581     $current_class ="";
582     $next_class = "";
584     /* After this elements we can add new elements 
585      *  without having any trouble.
586      */
587     $allowed_to_add_after = array("sieve_keep",
588                                   "sieve_require", 
589                                   "sieve_stop", 
590                                   "sieve_reject", 
591                                   "sieve_fileinto", 
592                                   "sieve_redirect", 
593                                   "sieve_discard",
594                                   "sieve_comment",
595                                   "sieve_block_start"
596                                  );
598     /* Before this elements we can add new elements 
599      *  without having any trouble.
600      */
601     $allowed_to_add_before = array("sieve_keep",
602                                   "sieve_require", 
603                                   "sieve_stop", 
604                                   "sieve_reject", 
605                                   "sieve_fileinto", 
606                                   "sieve_comment",
607                                   "sieve_redirect", 
608                                   "sieve_discard",
609                                   "sieve_if", 
610                                   "sieve_block_end"
611                                  );
613     if($direction == "down"){
614     
615       $test = $this->pap;
616       while($key_id < count($test)){
617         if(($key_id+1) == count($test)) {
618           return($key_id);
619         }
620         $key_id ++;
621         $current_class  = get_class($test[$key_id]);
622         if(in_array($current_class, $allowed_to_add_after)){
623           return($key_id);
624         } 
625       } 
626     }else{
627   
628       $test = $this->pap;
629       if($key_id == 0) {
630         return($key_id);
631       }
632       $key_id --;
633       while($key_id >=0 ){
634         $current_class  = get_class($test[$key_id]);
635         if(in_array($current_class, $allowed_to_add_before)){
636           return($key_id);
637         } 
638         $key_id --;
639       }
640       return(0);
641     }
642   }
645   /* Need to be reviewed */
646   function get_sieve_script()
647   {
648     $tmp ="";
649     if(count($this->pap)){
650       $buffer = "";    
651       foreach($this->pap as $part)  {
652         if(get_class($part) == "sieve_block_end"){
653           $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
654         }
655         $tmp2 = $part->get_sieve_script_part();
657         if(get_class($part) == "sieve_reject"){
658           $tmp.=$tmp2;
659         }else{
661           $tmp3 = split("\n",$tmp2);
662           foreach($tmp3 as $str){
663             $str2 = trim($str);
664             if(empty($str2)) continue;
665             $tmp.= $buffer.$str."\n";
666           }
667         }
668         if(get_class($part) == "sieve_block_start"){
669           $buffer .= SIEVE_INDENT_TAB;
670         }
671       }
672     }
673     if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
674       $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
675     }
676     return($tmp);
677   }
679   function Add_Element()
680   {
681     $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
682     $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
683   }
685   function check()
686   {
687                 $msgs = array();
689     /* Some logical checks. 
690      *  like :  only sieve_comment can appear before require.
691      */
692     
693     /* Ensure that there are no command before require 
694      *  - Get id of last require tag
695      *  - Collect object types in from of this tag. 
696      *  - Check if there are tags collected that are not allowed 
697      */
698     $last_found_at = -1; 
699     $objs = array();
700     foreach($this->pap as $key => $obj){
701       if(get_class($obj) == "sieve_require"){
702         $last_found_at = $key;
703       }
704     }
705     foreach($this->pap as $key => $obj){
706       if($key == $last_found_at) break;  
707       if(!in_array(get_class($obj),array("sieve_comment","sieve_require"))){
708         $objs[] = get_class($obj);
709       }
710     }
711     if(count($objs) && $last_found_at != -1){
712       $str = _("Require must be the first command in the script.");  
713       $msgs[] = $str;
714       print_red($str);;
715     }
716     
717                 foreach($this->pap as $obj){
718                         $o_msgs = $obj->check();
719                         foreach($o_msgs as $o_msg){
720                                 $msgs[] = $o_msg;
721                         }
722                 }
723                 return($msgs);
724   }
728 /* Create valid sieve string/string-list 
729  *  out of a given array
730  */
731 function sieve_create_strings($data)
733   $ret = "";
734   if(is_array($data)){
735     if(count($data) == 1){
736       $ret = "\"";
737       foreach($data as $dat){
738         $ret .=$dat;
739       }
740       $ret.="\"";
741     }else{
742       foreach($data as $dat){
743         $ret.= "\"";
744         $ret.=$dat;
745         $ret.="\", ";
746       }
747       $ret = preg_replace("/,$/","",trim($ret));
748       $ret = "[".$ret."]";
749     }
750   }else{
752     $Multiline = preg_match("/\n/",$data);
753     $data = preg_replace("/\r/","",$data);;
755     if($Multiline){
756       $ret = "text: \r\n".$data."\r\n.\r\n";
757     }else{
758       $ret = "\"".$data."\"";
759     }
760   }
761   $ret = preg_replace("/\"\"/","\"",$ret);
762   $ret = preg_replace("/\n/","\r\n",$ret);
763   
764   return($ret);
767 /* This checks if there is a string at the current position 
768  *  in the token array. 
769  * If there is a string list at the current position,
770  *  this function will return a complete list of all
771  *  strings used in this list.
772  * It also returns an offset of the last token position 
773  */
774 function sieve_get_strings($data,$id)
776   $ret = array();
777   if($data[$id]['class'] == "left-bracket"){
778     while($data[$id]['class']  != "right-bracket" && $id < count($data)){
779       
780       if($data[$id]['class'] == "quoted-string"){
781         $ret[] = $data[$id]['text'];
782       }
783       $id ++;
784     }
785   }elseif($data[$id]['class'] == "quoted-string"){
786     $ret[] = $data[$id]['text'];
787   }elseif($data[$id]['class'] == "number"){
788     $ret[] = $data[$id]['text'];
789   }
790   return(array("OFFSET" => $id, "STRINGS" => $ret));
793 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
794 ?>