X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=include%2Fsieve%2Fclass_My_Tree.inc;h=ee1276a36ac3c1aba3c85281c0f2aa6b0bd2696b;hb=4ef5a0ec2c799536446dd29d5d828488cc14608f;hp=942f64e4c03fb970a2fb94195e09dc87775a5adf;hpb=fa5d4f7116f6592143cf24fbea8eb4d342ef368d;p=gosa.git diff --git a/include/sieve/class_My_Tree.inc b/include/sieve/class_My_Tree.inc index 942f64e4c..ee1276a36 100644 --- a/include/sieve/class_My_Tree.inc +++ b/include/sieve/class_My_Tree.inc @@ -15,13 +15,26 @@ class My_Tree extends Tree var $mode_stack = array(); var $pap = array(); + var $parent = NULL; + function My_Tree(&$root,$parent) + { + $this->parent = $parent; + $this->_construct($root); + } + + function execute() + { + return($this->dump()); + } /* Create a html interface for the current sieve filter */ function dump() { - error_reporting(E_ALL); + /************** + * Handle new elements + **************/ /* Only parse the tokens once */ if(!count($this->pap)){ @@ -29,17 +42,52 @@ class My_Tree extends Tree $this->mode_stack = array(); $this->pap = array(); $this->doDump_(0, '', true); + + /* Add left elements */ + if(count($this->mode_stack)){ + foreach($this->mode_stack as $element){ + $this->handle_elements( $element,preg_replace("/[^0-9]/","",microtime())); + } + } } /* Create html results */ - $this->dump_ ="
"; - foreach($this->pap as $object){ + $smarty = get_smarty(); + + $block_indent_start = $smarty->fetch(get_template_path("templates/block_indent_start.tpl",TRUE,dirname(__FILE__))); + $block_indent_stop = $smarty->fetch(get_template_path("templates/block_indent_stop.tpl",TRUE,dirname(__FILE__))); + + $this -> dump_ = ""; + $ends = array(); + $ends_complete_block = array(); + + foreach($this->pap as $key => $object){ if(is_object($object)){ + + $end = $this->get_block_end($key,false); + $end2 = $this->get_block_end($key); + if($end != $key && in_array(get_class($object),array("sieve_if"))){ + $ends_complete_block[$end2] = $end2; + $this->dump_ .= "
"; + $this->dump_ .= "
"; + } + if(isset($ends[$key])){ + $this->dump_ .= $block_indent_stop; + } $this->dump_ .= preg_replace("/>/",">\n",$object->execute()); + if($end != $key && in_array(get_class($object),array("sieve_if","sieve_else","sieve_elsif"))) { + $ends[$end] = $end; + $this->dump_ .= $block_indent_start; + } + + if(isset($ends_complete_block[$key])){ + $this->dump_ .= "
"; + $this->dump_ .= "
"; + } } } - $this->dump_ .= "
"; - return $this->dump_; + + return($this->dump_); } @@ -57,18 +105,31 @@ class My_Tree extends Tree /* Get node */ $node = $this->nodes_[$node_id]; + /* Get last element class and type */ + $last_class = ""; + $last_type = ""; + if(count($this->mode_stack)){ + $key = key($this->mode_stack); + $tmp = array_reverse($this->mode_stack[$key]['ELEMENTS']); + $last_class = $tmp[key($tmp)]['class']; + + if(isset($this->mode_stack[$key]['TYPE'])){ + $last_type = $this->mode_stack[$key]['TYPE']; + } + } + /* This closes the last mode */ if($node['class'] == "block-start"){ $tmp = array_pop($this->mode_stack); $this->handle_elements($tmp,$node_id); - $this->handle_elements(array("TYPE" => "block_start"),$node_id); + $this->handle_elements(array("TYPE" => "block_start"),preg_replace("/[^0-9]/","",microtime())); } /* This closes the last mode */ if($node['class'] == "block-end"){ $tmp = array_pop($this->mode_stack); $this->handle_elements($tmp,$node_id); - $this->handle_elements(array("TYPE" => "block_end"),$node_id); + $this->handle_elements(array("TYPE" => "block_end"),preg_replace("/[^0-9]/","",microtime())); } /* Semicolon indicates a new command */ @@ -77,22 +138,42 @@ class My_Tree extends Tree $this->handle_elements($tmp,$node_id); } - /* Handle comments */ - if($node['class'] == "comment"){ - $this->mode_stack[] = array("TYPE" => $node['class']); - $rewoke_last = TRUE; + /* We can't handle comments within if tag right now */ + if(!in_array_ics($last_type,array("if","elsif"))){ + + /* Comments require special attention. + * We do not want to create a single comment element + * foreach each "#comment" string found in the script. + * Sometimes comments are used like this + * # This is a comment + * # and it still is a comment + * # ... + * So we combine them to one single comment. + */ + if($last_class != "comment" && $node['class'] == "comment"){ + $tmp =array_pop($this->mode_stack); + $this->handle_elements($tmp,$node_id); + $this->mode_stack[] = array("TYPE" => $node['class']); + } + + if($last_class == "comment" && $node['class'] != "comment"){ + $tmp =array_pop($this->mode_stack); + $this->handle_elements($tmp,$node_id); + } } /* Handle identifiers */ - $identifiers = array("if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop"); + $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","fileinto","require","stop"); if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){ $this->mode_stack[] = array("TYPE" => $node['text']); } - /* Add current node to current command stack */ - end($this->mode_stack); - $key = key($this->mode_stack); - $this->mode_stack[$key]['ELEMENTS'][] = $node; + if(!($last_type == "if" && $node['class'] == "comment")){ + /* Add current node to current command stack */ + end($this->mode_stack); + $key = key($this->mode_stack); + $this->mode_stack[$key]['ELEMENTS'][] = $node; + } /* Remove last mode from mode stack, cause it was only valid for a single line */ if($rewoke_last){ @@ -128,7 +209,7 @@ class My_Tree extends Tree $class_name= "sieve_".$type ; if(class_exists($class_name)){ - $this->pap[] = new $class_name($data,$id); + $this->pap[] = new $class_name($data,$id,$this); }else{ echo "Missing : ".$class_name.""."
"; } @@ -136,16 +217,528 @@ class My_Tree extends Tree function save_object() { + reset($this->pap); + foreach($this->pap as $key => $obj){ + + if(in_array(get_class($obj),array("sieve_if", + "sieve_elsif", + "sieve_vacation", + "sieve_comment", + "sieve_reject", + "sieve_fileinto", + "sieve_require", + "sieve_redirect"))){ + + + if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){ + $this->pap[$key]->save_object(); + } + } + } + } + + + /* Remove the object at the given position */ + function remove_object($key_id) + { + if(count($this->pap) == 1){ + print_red(_("Can't remove last element.")); + return; + } + + if(!isset($this->pap[$key_id])){ + trigger_error(_("Can't remove element with object_id=".$key_id.", there is no object with this identifier. Remove aborted.")); + return(false); + } + + $class = get_class($this->pap[$key_id]); + if(in_array($class,array("sieve_if","sieve_elsif","sieve_else"))){ + $block_start= $key_id; + $block_end = $this->get_block_end($key_id); + + for($i = $block_start ; $i <= $block_end ; $i ++ ){ + unset($this->pap[$i]); + } + }else{ + unset($this->pap[$key_id]); + } + $tmp = array(); + foreach($this->pap as $element){ + $tmp[] = $element; + } + $this->pap = $tmp; + } + + + /* This function moves a given element to another position. + * Single elements like "keep;" will simply be moved one posisition down/up. + * Multiple elements like if-elsif-else will be moved as block. + * + * $key_id specified the element that should be moved. + * $direction specifies to move elements "up" or "down" + */ + function move_up_down($key_id,$direction = "down") + { + + /* Get the current element to decide what to move. */ + $e_class = get_class($this->pap[$key_id]); + + if(in_array($e_class,array("sieve_if"))){ + $block_start= $key_id; + $block_end = $this->get_block_end($key_id); + + /* Depending on the direction move up down */ + if($direction == "down"){ + $next_free = $this->_get_next_free_move_slot($block_end,$direction); + }else{ + $next_free = $this->_get_next_free_move_slot($block_start,$direction); + } + + /* Move the given block */ + $this->move_multiple_elements($block_start,$block_end,$next_free); + } + + if(in_array($e_class,array( "sieve_stop", + "sieve_keep", + "sieve_require", + "sieve_comment", + "sieve_vacation", + "sieve_stop", + "sieve_reject", + "sieve_fileinto", + "sieve_redirect", + "sieve_discard"))){ + $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction)); + } + } + + + /* Move the given block to position */ + function move_multiple_elements($start,$end,$to) + { + /* Use class names for testing */ + $data = $this->pap; + + /* Get block to move */ + $block_to_move = array_slice($data,$start, ($end - $start +1)); + + /* We want do move this block up */ + if($end > $to){ + + /* Get start block */ + $start_block = array_slice($data,0,$to); + + /* Get Get all elements between the block to move + * and next free position + */ + $block_to_free = array_slice($data,$to ,$start - $to ); + $block_to_end = array_slice($data,$end+1); + $new = array(); + foreach($start_block as $block){ + $new[] = $block; + } + foreach($block_to_move as $block){ + $new[] = $block; + } + foreach($block_to_free as $block){ + $new[] = $block; + } + foreach($block_to_end as $block){ + $new[] = $block; + } + $old = $this->pap; + $this->pap = $new; + } + + + /* We want to move this block down. */ + if($to > $end){ + + /* Get start block */ + $start_block = array_slice($data,0,$start); + + /* Get Get all elements between the block to move + * and next free position + */ + $block_to_free = array_slice($data,$end +1,($to - $end )); + + /* Get the rest + */ + $block_to_end = array_slice($data,$to+1); + + $new = array(); + foreach($start_block as $block){ + $new[] = $block; + } + foreach($block_to_free as $block){ + $new[] = $block; + } + foreach($block_to_move as $block){ + $new[] = $block; + } + foreach($block_to_end as $block){ + $new[] = $block; + } + $old = $this->pap; + $this->pap = $new; + } + } + + + /* This function returns the id of the element + * where the current block ends + */ + function get_block_end($start,$complete = TRUE) + { + /* Only execute if this is a really a block element. + * Block elements is only sieve_if + */ + if(in_array(get_class($this->pap[$start]),array("sieve_if","sieve_elsif","sieve_else"))){ + + $class = get_class($this->pap[$start]); + $next_class = get_class($this->pap[$start+1]); + $block_depth = 0; + + $end = FALSE; + + while(!$end && $start < count($this->pap)){ + + if($class == "sieve_block_start"){ + $block_depth ++; + } + + if($class == "sieve_block_end"){ + $block_depth --; + } + + if($complete){ + if( $block_depth == 0 && + $class == "sieve_block_end" && + !in_array($next_class,array("sieve_else","sieve_elsif"))){ + $end = TRUE; + $start --; + } + }else{ + + if( $block_depth == 0 && + $class == "sieve_block_end" ){ + $end = TRUE; + $start --; + } + } + + $start ++; + $class = get_class($this->pap[$start]); + + if(isset($this->pap[$start+1])){ + $next_class = get_class($this->pap[$start+1]); + }else{ + $next_class =""; + } + } + } + return($start); + } + + + /* This function moves the single element at + * position $from to position $to. + */ + function move_single_element($from,$to) + { + if($from == $to) { + return; + } + + $ret = array(); + $tmp = $this->pap; + + $begin = array(); + $middle = array(); + $end = array(); + $element = $this->pap[$from]; + + if($from > $to ){ + + /* Get all element in fron to element to move */ + if($from != 0){ + $begin = array_slice($tmp,0,$to); + } + + /* Get all elements between */ + $middle = array_slice($tmp,$to , ($from - ($to) )); + + /* Get the rest */ + $end = array_slice($tmp,$from+1); + + foreach($begin as $data){ + $ret[] = $data; + } + $ret[] = $element; + foreach($middle as $data){ + $ret[] = $data; + } + foreach($end as $data){ + $ret[] = $data; + } + $this->pap = $ret; + } + if($from < $to ){ + + /* Get all element in fron to element to move */ + if($from != 0){ + $begin = array_slice($tmp,0,$from); + } + + /* Get all elements between */ + $middle = array_slice($tmp,$from+1 , ($to - ($from))); + + /* Get the rest */ + $end = array_slice($tmp,$to+1); + + foreach($begin as $data){ + $ret[] = $data; + } + foreach($middle as $data){ + $ret[] = $data; + } + $ret[] = $element; + foreach($end as $data){ + $ret[] = $data; + } + $this->pap = $ret; + } + } + + + /* Returns the next free position where we + * can add a new sinle element + * $key_id = Current position + * $direction = Forward or backward. + */ + function _get_next_free_move_slot($key_id,$direction,$include_self = FALSE) + { + $last_class = ""; + $current_class =""; + $next_class = ""; + + /* After this elements we can add new elements + * without having any trouble. + */ + $allowed_to_add_after = array("sieve_keep", + "sieve_require", + "sieve_stop", + "sieve_reject", + "sieve_fileinto", + "sieve_redirect", + "sieve_discard", + "sieve_comment", + "sieve_block_start" + ); + + /* Before this elements we can add new elements + * without having any trouble. + */ + $allowed_to_add_before = array("sieve_keep", + "sieve_require", + "sieve_stop", + "sieve_reject", + "sieve_fileinto", + "sieve_comment", + "sieve_redirect", + "sieve_discard", + "sieve_if", + "sieve_block_end" + ); + + if($direction == "down"){ + + $test = $this->pap; + while($key_id < count($test)){ + if(($key_id+1) == count($test)) { + return($key_id); + } + + if(!$include_self){ + $key_id ++; + } + $include_self = FALSE; + $current_class = get_class($test[$key_id]); + if(in_array($current_class, $allowed_to_add_after)){ + return($key_id); + } + } + }else{ + + $test = $this->pap; + if($key_id == 0) { + return($key_id); + } + if(!$include_self){ + $key_id --; + } + while($key_id >=0 ){ + $current_class = get_class($test[$key_id]); + if(in_array($current_class, $allowed_to_add_before)){ + return($key_id); + } + $key_id --; + } + return(0); + } + } + + + /* Need to be reviewed */ + function get_sieve_script() + { + $tmp =""; + if(count($this->pap)){ + $buffer = ""; + foreach($this->pap as $part) { + if(get_class($part) == "sieve_block_end"){ + $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB))); + } + $tmp2 = $part->get_sieve_script_part(); + + $tmp3 = split("\n",$tmp2); + foreach($tmp3 as $str){ + $str2 = trim($str); + + /* If the current line only contains an '.' + * we must skip the line indent. + * The text: statement uses a single '.' to mark the text end. + * This '.' must be the only char in the current line, no + * whitespaces are allowed here. + */ + if($str2 == "."){ + $tmp.=$str."\n"; + }else{ + $tmp.= $buffer.$str."\n"; + } + } + if(get_class($part) == "sieve_block_start"){ + $buffer .= SIEVE_INDENT_TAB; + } + } + } + if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){ +# $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp; + } + return($tmp); + } + + function check() + { + $msgs = array(); + + /* Some logical checks. + * like : only sieve_comment can appear before require. + */ + + /* Ensure that there are no command before require + * - Get id of last require tag + * - Collect object types in from of this tag. + * - Check if there are tags collected that are not allowed + */ + $last_found_at = -1; + $objs = array(); + foreach($this->pap as $key => $obj){ + if(get_class($obj) == "sieve_require"){ + $last_found_at = $key; + } + } foreach($this->pap as $key => $obj){ + if($key == $last_found_at) break; + if(!in_array(get_class($obj),array("sieve_comment","sieve_require"))){ + $objs[] = get_class($obj); + } + } + if(count($objs) && $last_found_at != -1){ + $str = _("Require must be the first command in the script."); + $msgs[] = $str; + print_red($str);; + } - if(in_array(get_class($obj),array("sieve_if"))){ - $this->pap[$key]->save_object(); + foreach($this->pap as $obj){ + $o_msgs = $obj->check(); + foreach($o_msgs as $o_msg){ + $msgs[] = $o_msg; } } + return($msgs); + } + + + /* We are forced to add a new require. + * This function is called by the + * sieveElement_Classes->parent->add_require() + */ + function add_require($str) + { + $require_id = -1; + foreach($this->pap as $key => $obj){ + if(get_class($obj) == "sieve_require"){ + $require_id = $key; + } + } + + /* No require found, add one */ + if($require_id == -1){ + $require = new sieve_require(NULL,preg_replace("/[^0-9]/","",microtime()),$this); + $require -> Add_Require($str); + $new = array(); + $new[] = $require; + foreach($this->pap as $obj){ + $new[] = $obj; + } + $this->pap = $new; + } else { + $this->pap[$require_id]->Add_Require($str); + } } } +/* Create valid sieve string/string-list + * out of a given array + */ +function sieve_create_strings($data,$force_string = FALSE) +{ + $ret = ""; + if(is_array($data)){ + if(count($data) == 1){ + $ret = "\""; + foreach($data as $dat){ + $ret .=$dat; + } + $ret.="\""; + }else{ + foreach($data as $dat){ + $ret.= "\""; + $ret.=$dat; + $ret.="\", "; + } + $ret = preg_replace("/,$/","",trim($ret)); + $ret = "[".$ret."]"; + } + }else{ + + $Multiline = preg_match("/\n/",$data); + $data = preg_replace("/\r/","",$data);; + + if($Multiline && !$force_string){ + $ret = "text: \r\n".$data."\r\n.\r\n"; + }else{ + $ret = "\"".$data."\""; + } + } + $ret = preg_replace("/\"\"/","\"",$ret); + $ret = preg_replace("/\n/","\r\n",$ret); + + return($ret); +} + /* This checks if there is a string at the current position * in the token array. * If there is a string list at the current position, @@ -157,18 +750,24 @@ function sieve_get_strings($data,$id) { $ret = array(); if($data[$id]['class'] == "left-bracket"){ - while($data[$id]['class'] != "right-bracket" && $id < count($data)){ - + while(isset($data[$id]) && $data[$id]['class'] != "right-bracket" && $id < count($data)){ + if($data[$id]['class'] == "quoted-string"){ $ret[] = $data[$id]['text']; } + $id ++; } }elseif($data[$id]['class'] == "quoted-string"){ $ret[] = $data[$id]['text']; }elseif($data[$id]['class'] == "number"){ $ret[] = $data[$id]['text']; + }elseif($data[$id]['class'] == "multi-line"){ + $str = trim(preg_replace("/^text:/","",$data[$id]['text'])); + $str = trim(preg_replace("/\.$/","",$str)); + $ret[] = $str; } + return(array("OFFSET" => $id, "STRINGS" => $ret)); }