3
/* This class is inherited from the original 'Tree'
4
* class written by Heiko Hund.
5
* It is partly rewritten to create a useable html interface
6
* for each single sieve token.
7
* This gives us the ability to edit existing sieve filters.
9
class My_Tree extends Tree
14
var $mode_stack = array();
18
function My_Tree(&$root,$parent)
20
$this->parent = $parent;
21
$this->_construct($root);
27
return($this->dump());
31
/* Create a html interface for the current sieve filter
39
/* Only parse the tokens once */
40
if(!count($this->pap)){
42
$this->mode_stack = array();
44
$this->doDump_(0, '', true);
46
/* Add left elements */
47
if(count($this->mode_stack)){
48
foreach($this->mode_stack as $element){
49
$this->handle_elements( $element,preg_replace("/[^0-9]/","",microtime()));
54
/* Create html results */
55
$smarty = get_smarty();
57
$block_indent_start = $smarty->fetch(get_template_path("templates/block_indent_start.tpl",TRUE,dirname(__FILE__)));
58
$block_indent_stop = $smarty->fetch(get_template_path("templates/block_indent_stop.tpl",TRUE,dirname(__FILE__)));
62
$ends_complete_block = array();
64
foreach($this->pap as $key => $object){
65
if(is_object($object)){
67
$end = $this->get_block_end($key,false);
68
$end2 = $this->get_block_end($key);
69
if($end != $key && in_array(get_class($object),array("sieve_if"))){
70
$ends_complete_block[$end2] = $end2;
71
$this->dump_ .= "<div style='height:10px;'></div>";
72
$this->dump_ .= "<div class='container_'>";
74
if(isset($ends[$key])){
75
$this->dump_ .= $block_indent_stop;
77
$this->dump_ .= preg_replace("/>/",">\n",$object->execute());
78
if($end != $key && in_array(get_class($object),array("sieve_if","sieve_else","sieve_elsif"))) {
80
$this->dump_ .= $block_indent_start;
83
if(isset($ends_complete_block[$key])){
84
$this->dump_ .= "</div>";
85
$this->dump_ .= "<div style='height:10px;'></div>";
94
/* This function walks through the object tree generated by the "Parse" class.
95
* All Commands will be resolved and grouped. So the Commands and their
96
* parameter are combined. Like "IF" and ":comparator"...
98
function doDump_($node_id, $prefix, $last,$num = 1)
100
/* Indicates that current comman will only be valid for a single line.
101
* this command type will be removed from mode_stack after displaying it.
103
$rewoke_last = FALSE;
106
$node = $this->nodes_[$node_id];
108
/* Get last element class and type */
111
if(count($this->mode_stack)){
112
$key = key($this->mode_stack);
113
$tmp = array_reverse($this->mode_stack[$key]['ELEMENTS']);
114
$last_class = $tmp[key($tmp)]['class'];
116
if(isset($this->mode_stack[$key]['TYPE'])){
117
$last_type = $this->mode_stack[$key]['TYPE'];
121
/* This closes the last mode */
122
if($node['class'] == "block-start"){
123
$tmp = array_pop($this->mode_stack);
124
$this->handle_elements($tmp,$node_id);
125
$this->handle_elements(array("TYPE" => "block_start"),preg_replace("/[^0-9]/","",microtime()));
128
/* This closes the last mode */
129
if($node['class'] == "block-end"){
130
$tmp = array_pop($this->mode_stack);
131
$this->handle_elements($tmp,$node_id);
132
$this->handle_elements(array("TYPE" => "block_end"),preg_replace("/[^0-9]/","",microtime()));
135
/* Semicolon indicates a new command */
136
if($node['class'] == "semicolon"){
137
$tmp =array_pop($this->mode_stack);
138
$this->handle_elements($tmp,$node_id);
141
/* We can't handle comments within if tag right now */
142
if(!in_array_ics($last_type,array("if","elsif"))){
144
/* Comments require special attention.
145
* We do not want to create a single comment element
146
* foreach each "#comment" string found in the script.
147
* Sometimes comments are used like this
148
* # This is a comment
149
* # and it still is a comment
151
* So we combine them to one single comment.
153
if($last_class != "comment" && $node['class'] == "comment"){
154
$tmp =array_pop($this->mode_stack);
155
$this->handle_elements($tmp,$node_id);
156
$this->mode_stack[] = array("TYPE" => $node['class']);
159
if($last_class == "comment" && $node['class'] != "comment"){
160
$tmp =array_pop($this->mode_stack);
161
$this->handle_elements($tmp,$node_id);
165
/* Handle identifiers */
166
$identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","fileinto","require","stop");
167
if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
168
$this->mode_stack[] = array("TYPE" => $node['text']);
171
if(!($last_type == "if" && $node['class'] == "comment")){
172
/* Add current node to current command stack */
173
end($this->mode_stack);
174
$key = key($this->mode_stack);
175
$this->mode_stack[$key]['ELEMENTS'][] = $node;
178
/* Remove last mode from mode stack, cause it was only valid for a single line */
180
$tmp =array_pop($this->mode_stack);
181
$this->handle_elements($tmp,$node_id);
184
/* If this is a sub element, just call this for all childs */
185
if(isset($this->childs_[$node_id])){
186
$childs = $this->childs_[$node_id];
187
for ($i=0; $i<count($childs); ++$i)
190
if ($i+1 == count($childs))
194
$this->doDump_($childs[$i], "", $num);
200
/* Create a class for each resolved object.
201
* And append this class to a list of objects.
203
function handle_elements($data,$id)
205
if(!isset($data['TYPE'])){
208
$type = $data['TYPE'];
210
$class_name= "sieve_".$type ;
211
if(class_exists($class_name)){
212
$this->pap[] = new $class_name($data,$id,$this);
214
echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
218
function save_object()
221
foreach($this->pap as $key => $obj){
223
if(in_array(get_class($obj),array("sieve_if",
233
if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
234
$this->pap[$key]->save_object();
241
/* Remove the object at the given position */
242
function remove_object($key_id)
244
if(count($this->pap) == 1){
245
msg_dialog::display(_("Warning"), _("Cannot remove last element!"), ERROR_DIALOG);
249
if(!isset($this->pap[$key_id])){
250
trigger_error("Can't remove element with object_id=".$key_id.", there is no object with this identifier. Remove aborted.");
254
$class = get_class($this->pap[$key_id]);
255
if(in_array($class,array("sieve_if","sieve_elsif","sieve_else"))){
256
$block_start= $key_id;
257
$block_end = $this->get_block_end($key_id);
259
for($i = $block_start ; $i <= $block_end ; $i ++ ){
260
unset($this->pap[$i]);
263
unset($this->pap[$key_id]);
266
foreach($this->pap as $element){
273
/* This function moves a given element to another position.
274
* Single elements like "keep;" will simply be moved one posisition down/up.
275
* Multiple elements like if-elsif-else will be moved as block.
277
* $key_id specified the element that should be moved.
278
* $direction specifies to move elements "up" or "down"
280
function move_up_down($key_id,$direction = "down")
283
/* Get the current element to decide what to move. */
284
$e_class = get_class($this->pap[$key_id]);
286
if(in_array($e_class,array("sieve_if"))){
287
$block_start= $key_id;
288
$block_end = $this->get_block_end($key_id);
290
/* Depending on the direction move up down */
291
if($direction == "down"){
292
$next_free = $this->_get_next_free_move_slot($block_end,$direction);
294
$next_free = $this->_get_next_free_move_slot($block_start,$direction);
297
/* Move the given block */
298
$this->move_multiple_elements($block_start,$block_end,$next_free);
301
if(in_array($e_class,array( "sieve_stop",
311
$this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
316
/* Move the given block to position */
317
function move_multiple_elements($start,$end,$to)
319
/* Use class names for testing */
322
/* Get block to move */
323
$block_to_move = array_slice($data,$start, ($end - $start +1));
325
/* We want do move this block up */
328
/* Get start block */
329
$start_block = array_slice($data,0,$to);
331
/* Get Get all elements between the block to move
332
* and next free position
334
$block_to_free = array_slice($data,$to ,$start - $to );
335
$block_to_end = array_slice($data,$end+1);
337
foreach($start_block as $block){
340
foreach($block_to_move as $block){
343
foreach($block_to_free as $block){
346
foreach($block_to_end as $block){
354
/* We want to move this block down. */
357
/* Get start block */
358
$start_block = array_slice($data,0,$start);
360
/* Get Get all elements between the block to move
361
* and next free position
363
$block_to_free = array_slice($data,$end +1,($to - $end ));
367
$block_to_end = array_slice($data,$to+1);
370
foreach($start_block as $block){
373
foreach($block_to_free as $block){
376
foreach($block_to_move as $block){
379
foreach($block_to_end as $block){
388
/* This function returns the id of the element
389
* where the current block ends
391
function get_block_end($start,$complete = TRUE)
393
/* Only execute if this is a really a block element.
394
* Block elements is only sieve_if
396
if(in_array(get_class($this->pap[$start]),array("sieve_if","sieve_elsif","sieve_else"))){
398
$class = get_class($this->pap[$start]);
399
$next_class = get_class($this->pap[$start+1]);
404
while(!$end && $start < count($this->pap)){
406
if($class == "sieve_block_start"){
410
if($class == "sieve_block_end"){
415
if( $block_depth == 0 &&
416
$class == "sieve_block_end" &&
417
!in_array($next_class,array("sieve_else","sieve_elsif"))){
423
if( $block_depth == 0 &&
424
$class == "sieve_block_end" ){
431
$class = get_class($this->pap[$start]);
433
if(isset($this->pap[$start+1])){
434
$next_class = get_class($this->pap[$start+1]);
444
/* This function moves the single element at
445
* position $from to position $to.
447
function move_single_element($from,$to)
459
$element = $this->pap[$from];
463
/* Get all element in fron to element to move */
465
$begin = array_slice($tmp,0,$to);
468
/* Get all elements between */
469
$middle = array_slice($tmp,$to , ($from - ($to) ));
472
$end = array_slice($tmp,$from+1);
474
foreach($begin as $data){
478
foreach($middle as $data){
481
foreach($end as $data){
488
/* Get all element in fron to element to move */
490
$begin = array_slice($tmp,0,$from);
493
/* Get all elements between */
494
$middle = array_slice($tmp,$from+1 , ($to - ($from)));
497
$end = array_slice($tmp,$to+1);
499
foreach($begin as $data){
502
foreach($middle as $data){
506
foreach($end as $data){
514
/* Returns the next free position where we
515
* can add a new sinle element
516
* $key_id = Current position
517
* $direction = Forward or backward.
519
function _get_next_free_move_slot($key_id,$direction,$include_self = FALSE)
525
/* After this elements we can add new elements
526
* without having any trouble.
528
$allowed_to_add_after = array("sieve_keep",
539
/* Before this elements we can add new elements
540
* without having any trouble.
542
$allowed_to_add_before = array("sieve_keep",
554
if($direction == "down"){
557
while($key_id < count($test)){
558
if(($key_id+1) == count($test)) {
565
$include_self = FALSE;
566
$current_class = get_class($test[$key_id]);
567
if(in_array($current_class, $allowed_to_add_after)){
581
$current_class = get_class($test[$key_id]);
582
if(in_array($current_class, $allowed_to_add_before)){
592
/* Need to be reviewed */
593
function get_sieve_script()
596
if(count($this->pap)){
598
foreach($this->pap as $part) {
599
if(get_class($part) == "sieve_block_end"){
600
$buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
602
$tmp2 = $part->get_sieve_script_part();
604
$tmp3 = preg_split("/\n/",$tmp2);
605
foreach($tmp3 as $str){
608
/* If the current line only contains an '.'
609
* we must skip the line indent.
610
* The text: statement uses a single '.' to mark the text end.
611
* This '.' must be the only char in the current line, no
612
* whitespaces are allowed here.
617
$tmp.= $buffer.$str."\n";
620
if(get_class($part) == "sieve_block_start"){
621
$buffer .= SIEVE_INDENT_TAB;
625
if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
626
# $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
635
/* Some logical checks.
636
* like : only sieve_comment can appear before require.
639
/* Ensure that there are no command before require
640
* - Get id of last require tag
641
* - Collect object types in from of this tag.
642
* - Check if there are tags collected that are not allowed
646
foreach($this->pap as $key => $obj){
647
if(get_class($obj) == "sieve_require"){
648
$last_found_at = $key;
651
foreach($this->pap as $key => $obj){
652
if($key == $last_found_at) break;
653
if(!in_array(get_class($obj),array("sieve_comment","sieve_require"))){
654
$objs[] = get_class($obj);
657
if(count($objs) && $last_found_at != -1){
658
$str = _("Require must be the first command in the script.");
660
msg_dialog::display(_("Error"), $str, ERROR_DIALOG);
663
foreach($this->pap as $obj){
664
$o_msgs = $obj->check();
665
foreach($o_msgs as $o_msg){
673
/* We are forced to add a new require.
674
* This function is called by the
675
* sieveElement_Classes->parent->add_require()
677
function add_require($str)
680
foreach($this->pap as $key => $obj){
681
if(get_class($obj) == "sieve_require"){
686
/* No require found, add one */
687
if($require_id == -1){
688
$require = new sieve_require(NULL,preg_replace("/[^0-9]/","",microtime()),$this);
689
$require -> Add_Require($str);
692
foreach($this->pap as $obj){
697
$this->pap[$require_id]->Add_Require($str);
703
/* Create valid sieve string/string-list
704
* out of a given array
706
function sieve_create_strings($data,$force_string = FALSE)
710
if(count($data) == 1){
712
foreach($data as $dat){
717
foreach($data as $dat){
722
$ret = preg_replace("/,$/","",trim($ret));
725
# $ret = preg_replace("/\"\"/","\"",$ret);
728
$Multiline = preg_match("/\n/",$data);
729
$data = preg_replace("/\r/","",$data);;
731
if($Multiline && !$force_string){
732
$ret = "text: \r\n".$data."\r\n.\r\n";
734
$ret = "\"".$data."\"";
735
$ret = preg_replace("/\"\"/","\"",$ret);
738
$ret = preg_replace("/\n/","\r\n",$ret);
743
/* This checks if there is a string at the current position
744
* in the token array.
745
* If there is a string list at the current position,
746
* this function will return a complete list of all
747
* strings used in this list.
748
* It also returns an offset of the last token position
750
function sieve_get_strings($data,$id)
753
if($data[$id]['class'] == "left-bracket"){
754
while(isset($data[$id]) && $data[$id]['class'] != "right-bracket" && $id < count($data)){
756
if($data[$id]['class'] == "quoted-string"){
757
$text = $data[$id]['text'];
758
$text= preg_replace("/^\"/","",$text);
759
$text= preg_replace("/\"$/","",$text);
765
}elseif($data[$id]['class'] == "quoted-string"){
766
$text = $data[$id]['text'];
767
$text= preg_replace("/^\"/","",$text);
768
$text= preg_replace("/\"$/","",$text);
770
}elseif($data[$id]['class'] == "number"){
771
$ret[] = $data[$id]['text'];
772
}elseif($data[$id]['class'] == "multi-line"){
773
$str = trim(preg_replace("/^text:/","",$data[$id]['text']));
774
$str = trim(preg_replace("/\.$/","",$str));
778
return(array("OFFSET" => $id, "STRINGS" => $ret));
781
// vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler: