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();
20 /* Create a html interface for the current sieve filter
21 */
22 function dump()
23 {
24 error_reporting(E_ALL);
26 /* Only parse the tokens once */
27 if(!count($this->pap)){
28 $this->dump_ = "";
29 $this->mode_stack = array();
30 $this->pap = array();
31 $this->doDump_(0, '', true);
32 }
34 /* Create html results */
35 $smarty = get_smarty();
36 $smarty->fetch(get_template_path("templates/element_stop.tpl",TRUE,dirname(__FILE__)));
38 $this -> dump_ = "";
39 foreach($this->pap as $key => $object){
40 if(is_object($object)){
41 $this->dump_ .= preg_replace("/>/",">\n",$object->execute());
42 }
43 }
45 /* Create html results */
46 $smarty = get_smarty();
47 $smarty->assign("Contents",$this->dump_);
48 $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
49 return ($ret);
50 }
53 /* This function walks through the object tree generated by the "Parse" class.
54 * All Commands will be resolved and grouped. So the Commands and their
55 * parameter are combined. Like "IF" and ":comparator"...
56 */
57 function doDump_($node_id, $prefix, $last,$num = 1)
58 {
59 /* Indicates that current comman will only be valid for a single line.
60 * this command type will be removed from mode_stack after displaying it.
61 */
62 $rewoke_last = FALSE;
64 /* Get node */
65 $node = $this->nodes_[$node_id];
67 /* This closes the last mode */
68 if($node['class'] == "block-start"){
69 $tmp = array_pop($this->mode_stack);
70 $this->handle_elements($tmp,$node_id);
71 $this->handle_elements(array("TYPE" => "block_start"),$node_id);
72 }
74 /* This closes the last mode */
75 if($node['class'] == "block-end"){
76 $tmp = array_pop($this->mode_stack);
77 $this->handle_elements($tmp,$node_id);
78 $this->handle_elements(array("TYPE" => "block_end"),$node_id);
79 }
81 /* Semicolon indicates a new command */
82 if($node['class'] == "semicolon"){
83 $tmp =array_pop($this->mode_stack);
84 $this->handle_elements($tmp,$node_id);
85 }
87 /* Handle comments */
88 if($node['class'] == "comment"){
89 $this->mode_stack[] = array("TYPE" => $node['class']);
90 $rewoke_last = TRUE;
91 }
93 /* Handle identifiers */
94 $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
95 if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
96 $this->mode_stack[] = array("TYPE" => $node['text']);
97 }
99 /* Add current node to current command stack */
100 end($this->mode_stack);
101 $key = key($this->mode_stack);
102 $this->mode_stack[$key]['ELEMENTS'][] = $node;
104 /* Remove last mode from mode stack, cause it was only valid for a single line */
105 if($rewoke_last){
106 $tmp =array_pop($this->mode_stack);
107 $this->handle_elements($tmp,$node_id);
108 }
110 /* If this is a sub element, just call this for all childs */
111 if(isset($this->childs_[$node_id])){
112 $childs = $this->childs_[$node_id];
113 for ($i=0; $i<count($childs); ++$i)
114 {
115 $c_last = false;
116 if ($i+1 == count($childs))
117 {
118 $c_last = true;
119 }
120 $this->doDump_($childs[$i], "", $num);
121 }
122 }
123 }
126 /* Create a class for each resolved object.
127 * And append this class to a list of objects.
128 */
129 function handle_elements($data,$id)
130 {
131 if(!isset($data['TYPE'])){
132 return;
133 }
134 $type = $data['TYPE'];
136 $class_name= "sieve_".$type ;
137 if(class_exists($class_name)){
138 $this->pap[] = new $class_name($data,$id);
139 }else{
140 echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
141 }
142 }
144 function save_object()
145 {
146 reset($this->pap);
147 foreach($this->pap as $key => $obj){
149 if(in_array(get_class($obj),array("sieve_if","sieve_elsif","sieve_vacation","sieve_comment","sieve_reject","sieve_fileinto","sieve_require","sieve_redirect"))){
152 if(isset($this->pap[$key]) && method_exists($this->pap[$key],"save_object")){
153 $this->pap[$key]->save_object();
154 }
155 }
157 $once = TRUE;
158 foreach($_POST as $name => $value){
160 if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
161 $once = FALSE;
162 $this->remove_object($key);
163 }
164 if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
165 $this->move_up_down($key,"up");
166 $once = FALSE;
167 }
168 if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
169 $this->move_up_down($key,"down");
170 $once = FALSE;
171 }
172 }
173 }
174 }
177 function remove_object($key_id)
178 {
180 $class = get_class($this->pap[$key_id]);
182 if(in_array($class,array("sieve_if"))){
184 $block_start= $key_id;
185 $block_end = $this->get_block_end($key_id);
187 for($i = $block_start ; $i <= $block_end ; $i ++ ){
188 unset($this->pap[$i]);
189 }
191 }else{
192 unset($this->pap[$key_id]);
193 }
195 $tmp = array();
196 foreach($this->pap as $element){
197 $tmp[] = $element;
198 }
199 $this->pap = $tmp;
200 }
203 /* This function moves a given element to another position.
204 * Single elements like "keep;" will simply be moved one posisition down/up.
205 * Multiple elements like if-elsif-else will be moved as block.
206 *
207 * $key_id specified the element that should be moved.
208 * $direction specifies to move elements "up" or "down"
209 */
210 function move_up_down($key_id,$direction = "down")
211 {
213 /* Get the current element to decide what to move. */
214 $e_class = get_class($this->pap[$key_id]);
216 if(in_array($e_class,array("sieve_if"))){
217 $block_start= $key_id;
218 $block_end = $this->get_block_end($key_id);
220 /* Depending on the direction move up down */
221 if($direction == "down"){
222 $next_free = $this->_get_next_free_move_slot($block_end,$direction);
223 }else{
224 $next_free = $this->_get_next_free_move_slot($block_start,$direction);
225 }
227 /* Move the given block */
228 $this->move_multiple_elements($block_start,$block_end,$next_free);
229 }
231 if(in_array($e_class,array( "sieve_stop",
232 "sieve_keep",
233 "sieve_require",
234 "sieve_stop",
235 "sieve_reject",
236 "sieve_fileinto",
237 "sieve_redirect",
238 "sieve_discard"))){
239 $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
240 }
241 }
244 /* Move the given block to position */
245 function move_multiple_elements($start,$end,$to)
246 {
247 /* Use class names for testing */
248 $data = $this->pap;
250 /* Get block to move */
251 $block_to_move = array_slice($data,$start, ($end - $start +1));
253 /* We want do move this block up */
254 if($end > $to){
256 /* Get start block */
257 $start_block = array_slice($data,0,$to);
259 /* Get Get all elements between the block to move
260 * and next free position
261 */
262 $block_to_free = array_slice($data,$to ,$start - $to );
263 $block_to_end = array_slice($data,$end+1);
264 $new = array();
265 foreach($start_block as $block){
266 $new[] = $block;
267 }
268 foreach($block_to_move as $block){
269 $new[] = $block;
270 }
271 foreach($block_to_free as $block){
272 $new[] = $block;
273 }
274 foreach($block_to_end as $block){
275 $new[] = $block;
276 }
277 $old = $this->pap;
278 $this->pap = $new;
279 }
282 /* We want to move this block down. */
283 if($to > $end){
285 /* Get start block */
286 $start_block = array_slice($data,0,$start);
288 /* Get Get all elements between the block to move
289 * and next free position
290 */
291 $block_to_free = array_slice($data,$end +1,($to - $end ));
293 /* Get the rest
294 */
295 $block_to_end = array_slice($data,$to+1);
297 $new = array();
298 foreach($start_block as $block){
299 $new[] = $block;
300 }
301 foreach($block_to_free as $block){
302 $new[] = $block;
303 }
304 foreach($block_to_move as $block){
305 $new[] = $block;
306 }
307 foreach($block_to_end as $block){
308 $new[] = $block;
309 }
310 $old = $this->pap;
311 $this->pap = $new;
312 }
313 }
316 /* This function returns the id of the element
317 * where the current block ends
318 */
319 function get_block_end($start)
320 {
321 /* Only execute if this is a really a block element.
322 * Block elements is only sieve_if
323 */
324 if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
326 $class = get_class($this->pap[$start]);
327 $next_class = get_class($this->pap[$start+1]);
328 $block_depth = 0;
330 $end = FALSE;
332 while(!$end && $start < count($this->pap)){
334 if($class == "sieve_block_start"){
335 $block_depth ++;
336 }
338 if($class == "sieve_block_end"){
339 $block_depth --;
340 }
342 if( $block_depth == 0 &&
343 $class == "sieve_block_end" &&
344 !in_array($next_class,array("sieve_else","sieve_elsif"))){
345 $end = TRUE;
346 $start --;
347 }
348 $start ++;
349 $class = get_class($this->pap[$start]);
350 $next_class = get_class($this->pap[$start+1]);
351 }
352 }
353 return($start);
354 }
357 /* This function moves the single element at
358 * position $from to position $to.
359 */
360 function move_single_element($from,$to)
361 {
362 if($from == $to) {
363 return;
364 }
366 $ret = array();
367 $tmp = $this->pap;
369 $begin = array();
370 $middle = array();
371 $end = array();
372 $element = $this->pap[$from];
374 if($from > $to ){
376 /* Get all element in fron to element to move */
377 if($from != 0){
378 $begin = array_slice($tmp,0,$to);
379 }
381 /* Get all elements between */
382 $middle = array_slice($tmp,$to , ($from - ($to) ));
384 /* Get the rest */
385 $end = array_slice($tmp,$from+1);
387 foreach($begin as $data){
388 $ret[] = $data;
389 }
390 $ret[] = $element;
391 foreach($middle as $data){
392 $ret[] = $data;
393 }
394 foreach($end as $data){
395 $ret[] = $data;
396 }
397 $this->pap = $ret;
398 }
399 if($from < $to ){
401 /* Get all element in fron to element to move */
402 if($from != 0){
403 $begin = array_slice($tmp,0,$from);
404 }
406 /* Get all elements between */
407 $middle = array_slice($tmp,$from+1 , ($to - ($from)));
409 /* Get the rest */
410 $end = array_slice($tmp,$to+1);
412 foreach($begin as $data){
413 $ret[] = $data;
414 }
415 foreach($middle as $data){
416 $ret[] = $data;
417 }
418 $ret[] = $element;
419 foreach($end as $data){
420 $ret[] = $data;
421 }
422 $this->pap = $ret;
423 }
424 }
427 /* Returns the next free position where we
428 * can add a new sinle element
429 * $key_id = Current position
430 * $direction = Forward or backward.
431 */
432 function _get_next_free_move_slot($key_id,$direction)
433 {
434 $last_class = "";
435 $current_class ="";
436 $next_class = "";
438 /* After this elements we can add new elements
439 * without having any trouble.
440 */
441 $allowed_to_add_after = array("sieve_keep",
442 "sieve_require",
443 "sieve_stop",
444 "sieve_reject",
445 "sieve_fileinto",
446 "sieve_redirect",
447 "sieve_discard",
448 "sieve_comment",
449 "sieve_block_start"
450 );
452 /* Before this elements we can add new elements
453 * without having any trouble.
454 */
455 $allowed_to_add_before = array("sieve_keep",
456 "sieve_require",
457 "sieve_stop",
458 "sieve_reject",
459 "sieve_fileinto",
460 "sieve_comment",
461 "sieve_redirect",
462 "sieve_discard",
463 "sieve_if",
464 "sieve_block_end"
465 );
467 if($direction == "down"){
469 $test = $this->pap;
470 while($key_id < count($test)){
471 if(($key_id+1) == count($test)) {
472 return($key_id);
473 }
474 $key_id ++;
475 $current_class = get_class($test[$key_id]);
476 if(in_array($current_class, $allowed_to_add_after)){
477 return($key_id);
478 }
479 }
480 }else{
482 $test = $this->pap;
483 if($key_id == 0) {
484 return($key_id);
485 }
486 $key_id --;
487 while($key_id >=0 ){
488 $current_class = get_class($test[$key_id]);
489 if(in_array($current_class, $allowed_to_add_before)){
490 return($key_id);
491 }
492 $key_id --;
493 }
494 return(0);
495 }
496 }
499 /* Need to be reviewed */
500 function get_sieve_script()
501 {
502 $tmp ="";
503 if(count($this->pap)){
504 $buffer = "";
505 foreach($this->pap as $part) {
506 if(get_class($part) == "sieve_block_end"){
507 $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
508 }
509 $tmp2 = $part->get_sieve_script_part();
511 if(get_class($part) == "sieve_reject"){
512 $tmp.=$tmp2;
513 }else{
515 $tmp3 = split("\n",$tmp2);
516 foreach($tmp3 as $str){
517 $str2 = trim($str);
518 if(empty($str2)) continue;
519 $tmp.= $buffer.$str."\n";
520 }
521 }
522 if(get_class($part) == "sieve_block_start"){
523 $buffer .= SIEVE_INDENT_TAB;
524 }
525 }
526 }
527 if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
528 $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
529 }
530 return($tmp);
531 }
533 function Add_Element()
534 {
535 $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
536 $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
537 }
538 }
541 /* Create valid sieve string/string-list
542 * out of a given array
543 */
544 function sieve_create_strings($data)
545 {
546 $ret = "";
547 if(is_array($data)){
548 if(count($data) == 1){
549 $ret = "\"";
550 foreach($data as $dat){
551 $ret .=$dat;
552 }
553 $ret.="\"";
554 }else{
555 foreach($data as $dat){
556 $ret.= "\"";
557 $ret.=$dat;
558 $ret.="\", ";
559 }
560 $ret = preg_replace("/,$/","",trim($ret));
561 $ret = "[".$ret."]";
562 }
563 }else{
565 $Multiline = preg_match("/\n/",$data);
566 $data = preg_replace("/\r/","",$data);;
568 if($Multiline){
569 $ret = "text: \r\n".$data."\r\n.\r\n";
570 }else{
571 $ret = "\"".$data."\"";
572 }
573 }
574 $ret = preg_replace("/\"\"/","\"",$ret);
575 $ret = preg_replace("/\n/","\r\n",$ret);
577 return($ret);
578 }
580 /* This checks if there is a string at the current position
581 * in the token array.
582 * If there is a string list at the current position,
583 * this function will return a complete list of all
584 * strings used in this list.
585 * It also returns an offset of the last token position
586 */
587 function sieve_get_strings($data,$id)
588 {
589 $ret = array();
590 if($data[$id]['class'] == "left-bracket"){
591 while($data[$id]['class'] != "right-bracket" && $id < count($data)){
593 if($data[$id]['class'] == "quoted-string"){
594 $ret[] = $data[$id]['text'];
595 }
596 $id ++;
597 }
598 }elseif($data[$id]['class'] == "quoted-string"){
599 $ret[] = $data[$id]['text'];
600 }elseif($data[$id]['class'] == "number"){
601 $ret[] = $data[$id]['text'];
602 }
603 return(array("OFFSET" => $id, "STRINGS" => $ret));
604 }
606 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
607 ?>