826817b88778fd5c649ee4abf2656356de6d9130
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 = "";
24 /* Create a html interface for the current sieve filter
25 */
26 function dump()
27 {
28 error_reporting(E_ALL);
29 $element_types= array(
30 "sieve_keep" => _("Keep"),
31 "sieve_comment" => _("Comment"),
32 "sieve_fileinto" => _("File into"),
33 "sieve_keep" => _("Keep"),
34 "sieve_redirect" => _("Redirect"),
35 "sieve_reject" => _("Reject"),
36 "sieve_require" => _("Require"),
37 "sieve_stop" => _("Stop"),
38 "sieve_vacation" => _("Vacation message"),
39 "sieve_if" => _("If"));
42 if($this->add_new && isset($_POST['element_type']) && isset($element_types[$_POST['element_type']])){
43 $this->add_element_type = $_POST['element_type'];
44 }
47 if($this->add_new && isset($_POST['select_new_element_type'])){
49 $ele = new $this->add_element_type(NULL, preg_replace("/[^0-9]/","",microtime()));
50 $start = $end = array();
51 $found = false;
53 if($this->add_type == "top"){
54 foreach($this->pap as $key => $obj){
55 if($obj->object_id == $this->add_new_id){
56 $found = true;
57 }
58 if(!$found){
59 $start[] = $obj;
60 }else{
61 $end[] = $obj;
62 }
63 }
64 }else{
65 foreach($this->pap as $key => $obj){
66 if(!$found){
67 $start[] = $obj;
68 }else{
69 $end[] = $obj;
70 }
71 if($obj->object_id == $this->add_new_id){
72 $found = true;
73 }
74 }
75 }
77 if($found){
78 $new = array();
79 foreach($start as $obj){
80 $new[] = $obj;
81 }
82 $new[] = $ele;
83 foreach($end as $obj){
84 $new[] = $obj;
85 }
86 $this->pap = $new;
87 $this->add_new = FALSE;
88 }else{
89 print_red(_("Something went wrong while adding a new entry."));
90 }
91 }
93 if($this->add_new){
95 $smarty = get_smarty();
96 $smarty->assign("element_types",$element_types );
97 $smarty->assign("element_type",$this->add_element_type);
99 $str = $smarty->fetch(get_template_path("templates/add_element.tpl",TRUE,dirname(__FILE__)));
100 return($str);
101 }
103 /* Only parse the tokens once */
104 if(!count($this->pap)){
105 $this->dump_ = "";
106 $this->mode_stack = array();
107 $this->pap = array();
108 $this->doDump_(0, '', true);
109 }
111 /* Create html results */
112 $smarty = get_smarty();
114 $this -> dump_ = "";
115 foreach($this->pap as $key => $object){
116 if(is_object($object)){
117 $this->dump_ .= preg_replace("/>/",">\n",$object->execute());
118 }
119 }
121 /* Create html results */
122 $smarty = get_smarty();
123 $smarty->assign("Contents",$this->dump_);
124 $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
125 return ($ret);
126 }
129 /* This function walks through the object tree generated by the "Parse" class.
130 * All Commands will be resolved and grouped. So the Commands and their
131 * parameter are combined. Like "IF" and ":comparator"...
132 */
133 function doDump_($node_id, $prefix, $last,$num = 1)
134 {
135 /* Indicates that current comman will only be valid for a single line.
136 * this command type will be removed from mode_stack after displaying it.
137 */
138 $rewoke_last = FALSE;
140 /* Get node */
141 $node = $this->nodes_[$node_id];
143 /* This closes the last mode */
144 if($node['class'] == "block-start"){
145 $tmp = array_pop($this->mode_stack);
146 $this->handle_elements($tmp,$node_id);
147 $this->handle_elements(array("TYPE" => "block_start"),$node_id);
148 }
150 /* This closes the last mode */
151 if($node['class'] == "block-end"){
152 $tmp = array_pop($this->mode_stack);
153 $this->handle_elements($tmp,$node_id);
154 $this->handle_elements(array("TYPE" => "block_end"),$node_id);
155 }
157 /* Semicolon indicates a new command */
158 if($node['class'] == "semicolon"){
159 $tmp =array_pop($this->mode_stack);
160 $this->handle_elements($tmp,$node_id);
161 }
163 /* Handle comments */
164 if($node['class'] == "comment"){
165 $this->mode_stack[] = array("TYPE" => $node['class']);
166 $rewoke_last = TRUE;
167 }
169 /* Handle identifiers */
170 $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
171 if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
172 $this->mode_stack[] = array("TYPE" => $node['text']);
173 }
175 /* Add current node to current command stack */
176 end($this->mode_stack);
177 $key = key($this->mode_stack);
178 $this->mode_stack[$key]['ELEMENTS'][] = $node;
180 /* Remove last mode from mode stack, cause it was only valid for a single line */
181 if($rewoke_last){
182 $tmp =array_pop($this->mode_stack);
183 $this->handle_elements($tmp,$node_id);
184 }
186 /* If this is a sub element, just call this for all childs */
187 if(isset($this->childs_[$node_id])){
188 $childs = $this->childs_[$node_id];
189 for ($i=0; $i<count($childs); ++$i)
190 {
191 $c_last = false;
192 if ($i+1 == count($childs))
193 {
194 $c_last = true;
195 }
196 $this->doDump_($childs[$i], "", $num);
197 }
198 }
199 }
202 /* Create a class for each resolved object.
203 * And append this class to a list of objects.
204 */
205 function handle_elements($data,$id)
206 {
207 if(!isset($data['TYPE'])){
208 return;
209 }
210 $type = $data['TYPE'];
212 $class_name= "sieve_".$type ;
213 if(class_exists($class_name)){
214 $this->pap[] = new $class_name($data,$id);
215 }else{
216 echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
217 }
218 }
220 function save_object()
221 {
222 reset($this->pap);
223 foreach($this->pap as $key => $obj){
225 if(in_array(get_class($obj),array("sieve_if",
226 "sieve_elsif",
227 "sieve_vacation",
228 "sieve_comment",
229 "sieve_reject",
230 "sieve_fileinto",
231 "sieve_require",
232 "sieve_redirect"))){
235 if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
236 $this->pap[$key]->save_object();
237 }
238 }
240 $once = TRUE;
241 foreach($_POST as $name => $value){
243 if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
244 $once = FALSE;
245 $this->remove_object($key);
246 }
247 if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
248 $this->move_up_down($key,"up");
249 $once = FALSE;
250 }
251 if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
252 $this->move_up_down($key,"down");
253 $once = FALSE;
254 }
255 if(isset($obj->object_id) && preg_match("/^Add_Object_Top_".$obj->object_id."_/",$name) && $once){
256 $this->add_new_object($obj->object_id,"top");
257 $once = FALSE;
258 }
259 if(isset($obj->object_id) && preg_match("/^Add_Object_Bottom_".$obj->object_id."_/",$name) && $once){
260 $this->add_new_object($obj->object_id,"bottom");
261 $once = FALSE;
262 }
263 }
264 }
265 }
268 /* Add a new object at the given position */
269 function add_new_object($id,$top_bottom = "bottom")
270 {
271 $this->add_new = TRUE;
272 $this->add_new_id = $id;
273 $this->add_type = $top_bottom;
274 }
277 /* Remove the object at the given position */
278 function remove_object($key_id)
279 {
280 $class = get_class($this->pap[$key_id]);
281 if(in_array($class,array("sieve_if"))){
282 $block_start= $key_id;
283 $block_end = $this->get_block_end($key_id);
284 for($i = $block_start ; $i <= $block_end ; $i ++ ){
285 unset($this->pap[$i]);
286 }
287 }else{
288 unset($this->pap[$key_id]);
289 }
290 $tmp = array();
291 foreach($this->pap as $element){
292 $tmp[] = $element;
293 }
294 $this->pap = $tmp;
295 }
298 /* This function moves a given element to another position.
299 * Single elements like "keep;" will simply be moved one posisition down/up.
300 * Multiple elements like if-elsif-else will be moved as block.
301 *
302 * $key_id specified the element that should be moved.
303 * $direction specifies to move elements "up" or "down"
304 */
305 function move_up_down($key_id,$direction = "down")
306 {
308 /* Get the current element to decide what to move. */
309 $e_class = get_class($this->pap[$key_id]);
311 if(in_array($e_class,array("sieve_if"))){
312 $block_start= $key_id;
313 $block_end = $this->get_block_end($key_id);
315 /* Depending on the direction move up down */
316 if($direction == "down"){
317 $next_free = $this->_get_next_free_move_slot($block_end,$direction);
318 }else{
319 $next_free = $this->_get_next_free_move_slot($block_start,$direction);
320 }
322 /* Move the given block */
323 $this->move_multiple_elements($block_start,$block_end,$next_free);
324 }
326 if(in_array($e_class,array( "sieve_stop",
327 "sieve_keep",
328 "sieve_require",
329 "sieve_stop",
330 "sieve_reject",
331 "sieve_fileinto",
332 "sieve_redirect",
333 "sieve_discard"))){
334 $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
335 }
336 }
339 /* Move the given block to position */
340 function move_multiple_elements($start,$end,$to)
341 {
342 /* Use class names for testing */
343 $data = $this->pap;
345 /* Get block to move */
346 $block_to_move = array_slice($data,$start, ($end - $start +1));
348 /* We want do move this block up */
349 if($end > $to){
351 /* Get start block */
352 $start_block = array_slice($data,0,$to);
354 /* Get Get all elements between the block to move
355 * and next free position
356 */
357 $block_to_free = array_slice($data,$to ,$start - $to );
358 $block_to_end = array_slice($data,$end+1);
359 $new = array();
360 foreach($start_block as $block){
361 $new[] = $block;
362 }
363 foreach($block_to_move as $block){
364 $new[] = $block;
365 }
366 foreach($block_to_free as $block){
367 $new[] = $block;
368 }
369 foreach($block_to_end as $block){
370 $new[] = $block;
371 }
372 $old = $this->pap;
373 $this->pap = $new;
374 }
377 /* We want to move this block down. */
378 if($to > $end){
380 /* Get start block */
381 $start_block = array_slice($data,0,$start);
383 /* Get Get all elements between the block to move
384 * and next free position
385 */
386 $block_to_free = array_slice($data,$end +1,($to - $end ));
388 /* Get the rest
389 */
390 $block_to_end = array_slice($data,$to+1);
392 $new = array();
393 foreach($start_block as $block){
394 $new[] = $block;
395 }
396 foreach($block_to_free as $block){
397 $new[] = $block;
398 }
399 foreach($block_to_move as $block){
400 $new[] = $block;
401 }
402 foreach($block_to_end as $block){
403 $new[] = $block;
404 }
405 $old = $this->pap;
406 $this->pap = $new;
407 }
408 }
411 /* This function returns the id of the element
412 * where the current block ends
413 */
414 function get_block_end($start)
415 {
416 /* Only execute if this is a really a block element.
417 * Block elements is only sieve_if
418 */
419 if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
421 $class = get_class($this->pap[$start]);
422 $next_class = get_class($this->pap[$start+1]);
423 $block_depth = 0;
425 $end = FALSE;
427 while(!$end && $start < count($this->pap)){
429 if($class == "sieve_block_start"){
430 $block_depth ++;
431 }
433 if($class == "sieve_block_end"){
434 $block_depth --;
435 }
437 if( $block_depth == 0 &&
438 $class == "sieve_block_end" &&
439 !in_array($next_class,array("sieve_else","sieve_elsif"))){
440 $end = TRUE;
441 $start --;
442 }
443 $start ++;
444 $class = get_class($this->pap[$start]);
445 $next_class = get_class($this->pap[$start+1]);
446 }
447 }
448 return($start);
449 }
452 /* This function moves the single element at
453 * position $from to position $to.
454 */
455 function move_single_element($from,$to)
456 {
457 if($from == $to) {
458 return;
459 }
461 $ret = array();
462 $tmp = $this->pap;
464 $begin = array();
465 $middle = array();
466 $end = array();
467 $element = $this->pap[$from];
469 if($from > $to ){
471 /* Get all element in fron to element to move */
472 if($from != 0){
473 $begin = array_slice($tmp,0,$to);
474 }
476 /* Get all elements between */
477 $middle = array_slice($tmp,$to , ($from - ($to) ));
479 /* Get the rest */
480 $end = array_slice($tmp,$from+1);
482 foreach($begin as $data){
483 $ret[] = $data;
484 }
485 $ret[] = $element;
486 foreach($middle as $data){
487 $ret[] = $data;
488 }
489 foreach($end as $data){
490 $ret[] = $data;
491 }
492 $this->pap = $ret;
493 }
494 if($from < $to ){
496 /* Get all element in fron to element to move */
497 if($from != 0){
498 $begin = array_slice($tmp,0,$from);
499 }
501 /* Get all elements between */
502 $middle = array_slice($tmp,$from+1 , ($to - ($from)));
504 /* Get the rest */
505 $end = array_slice($tmp,$to+1);
507 foreach($begin as $data){
508 $ret[] = $data;
509 }
510 foreach($middle as $data){
511 $ret[] = $data;
512 }
513 $ret[] = $element;
514 foreach($end as $data){
515 $ret[] = $data;
516 }
517 $this->pap = $ret;
518 }
519 }
522 /* Returns the next free position where we
523 * can add a new sinle element
524 * $key_id = Current position
525 * $direction = Forward or backward.
526 */
527 function _get_next_free_move_slot($key_id,$direction)
528 {
529 $last_class = "";
530 $current_class ="";
531 $next_class = "";
533 /* After this elements we can add new elements
534 * without having any trouble.
535 */
536 $allowed_to_add_after = array("sieve_keep",
537 "sieve_require",
538 "sieve_stop",
539 "sieve_reject",
540 "sieve_fileinto",
541 "sieve_redirect",
542 "sieve_discard",
543 "sieve_comment",
544 "sieve_block_start"
545 );
547 /* Before this elements we can add new elements
548 * without having any trouble.
549 */
550 $allowed_to_add_before = array("sieve_keep",
551 "sieve_require",
552 "sieve_stop",
553 "sieve_reject",
554 "sieve_fileinto",
555 "sieve_comment",
556 "sieve_redirect",
557 "sieve_discard",
558 "sieve_if",
559 "sieve_block_end"
560 );
562 if($direction == "down"){
564 $test = $this->pap;
565 while($key_id < count($test)){
566 if(($key_id+1) == count($test)) {
567 return($key_id);
568 }
569 $key_id ++;
570 $current_class = get_class($test[$key_id]);
571 if(in_array($current_class, $allowed_to_add_after)){
572 return($key_id);
573 }
574 }
575 }else{
577 $test = $this->pap;
578 if($key_id == 0) {
579 return($key_id);
580 }
581 $key_id --;
582 while($key_id >=0 ){
583 $current_class = get_class($test[$key_id]);
584 if(in_array($current_class, $allowed_to_add_before)){
585 return($key_id);
586 }
587 $key_id --;
588 }
589 return(0);
590 }
591 }
594 /* Need to be reviewed */
595 function get_sieve_script()
596 {
597 $tmp ="";
598 if(count($this->pap)){
599 $buffer = "";
600 foreach($this->pap as $part) {
601 if(get_class($part) == "sieve_block_end"){
602 $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
603 }
604 $tmp2 = $part->get_sieve_script_part();
606 if(get_class($part) == "sieve_reject"){
607 $tmp.=$tmp2;
608 }else{
610 $tmp3 = split("\n",$tmp2);
611 foreach($tmp3 as $str){
612 $str2 = trim($str);
613 if(empty($str2)) continue;
614 $tmp.= $buffer.$str."\n";
615 }
616 }
617 if(get_class($part) == "sieve_block_start"){
618 $buffer .= SIEVE_INDENT_TAB;
619 }
620 }
621 }
622 if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
623 $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
624 }
625 return($tmp);
626 }
628 function Add_Element()
629 {
630 $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
631 $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
632 }
634 function check()
635 {
636 $msgs = array();
637 foreach($this->pap as $obj){
639 $o_msgs = $obj->check();
640 foreach($o_msgs as $o_msg){
641 $msgs[] = $o_msg;
642 }
643 }
644 return($msgs);
645 }
646 }
649 /* Create valid sieve string/string-list
650 * out of a given array
651 */
652 function sieve_create_strings($data)
653 {
654 $ret = "";
655 if(is_array($data)){
656 if(count($data) == 1){
657 $ret = "\"";
658 foreach($data as $dat){
659 $ret .=$dat;
660 }
661 $ret.="\"";
662 }else{
663 foreach($data as $dat){
664 $ret.= "\"";
665 $ret.=$dat;
666 $ret.="\", ";
667 }
668 $ret = preg_replace("/,$/","",trim($ret));
669 $ret = "[".$ret."]";
670 }
671 }else{
673 $Multiline = preg_match("/\n/",$data);
674 $data = preg_replace("/\r/","",$data);;
676 if($Multiline){
677 $ret = "text: \r\n".$data."\r\n.\r\n";
678 }else{
679 $ret = "\"".$data."\"";
680 }
681 }
682 $ret = preg_replace("/\"\"/","\"",$ret);
683 $ret = preg_replace("/\n/","\r\n",$ret);
685 return($ret);
686 }
688 /* This checks if there is a string at the current position
689 * in the token array.
690 * If there is a string list at the current position,
691 * this function will return a complete list of all
692 * strings used in this list.
693 * It also returns an offset of the last token position
694 */
695 function sieve_get_strings($data,$id)
696 {
697 $ret = array();
698 if($data[$id]['class'] == "left-bracket"){
699 while($data[$id]['class'] != "right-bracket" && $id < count($data)){
701 if($data[$id]['class'] == "quoted-string"){
702 $ret[] = $data[$id]['text'];
703 }
704 $id ++;
705 }
706 }elseif($data[$id]['class'] == "quoted-string"){
707 $ret[] = $data[$id]['text'];
708 }elseif($data[$id]['class'] == "number"){
709 $ret[] = $data[$id]['text'];
710 }
711 return(array("OFFSET" => $id, "STRINGS" => $ret));
712 }
714 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
715 ?>