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 foreach($this->pap as $key => $obj){
148 if(in_array(get_class($obj),array("sieve_if","sieve_elsif","sieve_vacation","sieve_comment","sieve_reject","sieve_fileinto","sieve_require","sieve_redirect"))){
149 $this->pap[$key]->save_object();
150 }
152 $once = TRUE;
153 foreach($_POST as $name => $value){
155 if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
156 $once = FALSE;
157 $this->remove_object($key);
158 }
159 if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
160 $this->move_up_down($key,"up");
161 $once = FALSE;
162 }
163 if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
164 $this->move_up_down($key,"down");
165 $once = FALSE;
166 }
167 }
168 }
169 }
172 function remove_object($key_id){
173 unset($this->pap[$key_id]);
174 }
177 /* This function moves a given element to another position.
178 * Single elements like "keep;" will simply be moved one posisition down/up.
179 * Multiple elements like if-elsif-else will be moved as block.
180 *
181 * $key_id specified the element that should be moved.
182 * $direction specifies to move elements "up" or "down"
183 */
184 function move_up_down($key_id,$direction = "down")
185 {
187 /* Get the current element to decide what to move. */
188 $e_class = get_class($this->pap[$key_id]);
190 if(in_array($e_class,array("sieve_if"))){
191 $block_start= $key_id;
192 $block_end = $this->get_block_end($key_id);
194 /* Depending on the direction move up down */
195 if($direction == "down"){
196 $next_free = $this->_get_next_free_move_slot($block_end,$direction);
197 }else{
198 $next_free = $this->_get_next_free_move_slot($block_start,$direction);
199 }
201 /* Move the given block */
202 $this->move_multiple_elements($block_start,$block_end,$next_free);
203 }
205 if(in_array($e_class,array( "sieve_stop",
206 "sieve_keep",
207 "sieve_require",
208 "sieve_stop",
209 "sieve_reject",
210 "sieve_fileinto",
211 "sieve_redirect",
212 "sieve_discard"))){
213 $this->move_single_element($key_id,$this->_get_next_free_move_slot($key_id,$direction));
214 }
215 }
218 /* Move the given block to position */
219 function move_multiple_elements($start,$end,$to)
220 {
221 /* Use class names for testing */
222 $data = $this->pap;
224 /* Get block to move */
225 $block_to_move = array_slice($data,$start, ($end - $start +1));
227 /* We want do move this block up */
228 if($end > $to){
230 /* Get start block */
231 $start_block = array_slice($data,0,$to);
233 /* Get Get all elements between the block to move
234 * and next free position
235 */
236 $block_to_free = array_slice($data,$to ,$start - $to );
237 $block_to_end = array_slice($data,$end+1);
238 $new = array();
239 foreach($start_block as $block){
240 $new[] = $block;
241 }
242 foreach($block_to_move as $block){
243 $new[] = $block;
244 }
245 foreach($block_to_free as $block){
246 $new[] = $block;
247 }
248 foreach($block_to_end as $block){
249 $new[] = $block;
250 }
251 $old = $this->pap;
252 $this->pap = $new;
253 }
256 /* We want to move this block down. */
257 if($to > $end){
259 /* Get start block */
260 $start_block = array_slice($data,0,$start);
262 /* Get Get all elements between the block to move
263 * and next free position
264 */
265 $block_to_free = array_slice($data,$end +1,($to - $end ));
267 /* Get the rest
268 */
269 $block_to_end = array_slice($data,$to+1);
271 $new = array();
272 foreach($start_block as $block){
273 $new[] = $block;
274 }
275 foreach($block_to_free as $block){
276 $new[] = $block;
277 }
278 foreach($block_to_move as $block){
279 $new[] = $block;
280 }
281 foreach($block_to_end as $block){
282 $new[] = $block;
283 }
284 $old = $this->pap;
285 $this->pap = $new;
286 }
287 }
290 /* This function returns the id of the element
291 * where the current block ends
292 */
293 function get_block_end($start)
294 {
295 /* Only execute if this is a really a block element.
296 * Block elements is only sieve_if
297 */
298 if(in_array(get_class($this->pap[$start]),array("sieve_if"))){
300 $class = get_class($this->pap[$start]);
301 $next_class = get_class($this->pap[$start+1]);
302 $block_depth = 0;
304 $end = FALSE;
306 while(!$end && $start < count($this->pap)){
308 if($class == "sieve_block_start"){
309 $block_depth ++;
310 }
312 if($class == "sieve_block_end"){
313 $block_depth --;
314 }
316 if( $block_depth == 0 &&
317 $class == "sieve_block_end" &&
318 !in_array($next_class,array("sieve_else","sieve_elsif"))){
319 $end = TRUE;
320 $start --;
321 }
322 $start ++;
323 $class = get_class($this->pap[$start]);
324 $next_class = get_class($this->pap[$start+1]);
325 }
326 }
327 return($start);
328 }
331 /* This function moves the single element at
332 * position $from to position $to.
333 */
334 function move_single_element($from,$to)
335 {
336 if($from == $to) {
337 return;
338 }
340 $ret = array();
341 $tmp = $this->pap;
343 $begin = array();
344 $middle = array();
345 $end = array();
346 $element = $this->pap[$from];
348 if($from > $to ){
350 /* Get all element in fron to element to move */
351 if($from != 0){
352 $begin = array_slice($tmp,0,$to);
353 }
355 /* Get all elements between */
356 $middle = array_slice($tmp,$to , ($from - ($to) ));
358 /* Get the rest */
359 $end = array_slice($tmp,$from+1);
361 foreach($begin as $data){
362 $ret[] = $data;
363 }
364 $ret[] = $element;
365 foreach($middle as $data){
366 $ret[] = $data;
367 }
368 foreach($end as $data){
369 $ret[] = $data;
370 }
371 $this->pap = $ret;
372 }
373 if($from < $to ){
375 /* Get all element in fron to element to move */
376 if($from != 0){
377 $begin = array_slice($tmp,0,$from);
378 }
380 /* Get all elements between */
381 $middle = array_slice($tmp,$from+1 , ($to - ($from)));
383 /* Get the rest */
384 $end = array_slice($tmp,$to+1);
386 foreach($begin as $data){
387 $ret[] = $data;
388 }
389 foreach($middle as $data){
390 $ret[] = $data;
391 }
392 $ret[] = $element;
393 foreach($end as $data){
394 $ret[] = $data;
395 }
396 $this->pap = $ret;
397 }
398 }
401 /* Returns the next free position where we
402 * can add a new sinle element
403 * $key_id = Current position
404 * $direction = Forward or backward.
405 */
406 function _get_next_free_move_slot($key_id,$direction)
407 {
408 $last_class = "";
409 $current_class ="";
410 $next_class = "";
412 /* After this elements we can add new elements
413 * without having any trouble.
414 */
415 $allowed_to_add_after = array("sieve_keep",
416 "sieve_require",
417 "sieve_stop",
418 "sieve_reject",
419 "sieve_fileinto",
420 "sieve_redirect",
421 "sieve_discard",
422 "sieve_comment",
423 "sieve_block_start"
424 );
426 /* Before this elements we can add new elements
427 * without having any trouble.
428 */
429 $allowed_to_add_before = array("sieve_keep",
430 "sieve_require",
431 "sieve_stop",
432 "sieve_reject",
433 "sieve_fileinto",
434 "sieve_comment",
435 "sieve_redirect",
436 "sieve_discard",
437 "sieve_if",
438 "sieve_block_end"
439 );
441 if($direction == "down"){
443 $test = $this->pap;
444 while($key_id < count($test)){
445 if(($key_id+1) == count($test)) {
446 return($key_id);
447 }
448 $key_id ++;
449 $current_class = get_class($test[$key_id]);
450 if(in_array($current_class, $allowed_to_add_after)){
451 return($key_id);
452 }
453 }
454 }else{
456 $test = $this->pap;
457 if($key_id == 0) {
458 return($key_id);
459 }
460 $key_id --;
461 while($key_id >=0 ){
462 $current_class = get_class($test[$key_id]);
463 if(in_array($current_class, $allowed_to_add_before)){
464 return($key_id);
465 }
466 $key_id --;
467 }
468 return(0);
469 }
470 }
473 /* Need to be reviewed */
474 function get_sieve_script()
475 {
476 $tmp ="";
477 if(count($this->pap)){
478 $buffer = "";
479 foreach($this->pap as $part) {
480 if(get_class($part) == "sieve_block_end"){
481 $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
482 }
483 $tmp2 = $part->get_sieve_script_part();
485 if(get_class($part) == "sieve_reject"){
486 $tmp.=$tmp2;
487 }else{
489 $tmp3 = split("\n",$tmp2);
490 foreach($tmp3 as $str){
491 $str2 = trim($str);
492 if(empty($str2)) continue;
493 $tmp.= $buffer.$str."\n";
494 }
495 }
496 if(get_class($part) == "sieve_block_start"){
497 $buffer .= SIEVE_INDENT_TAB;
498 }
499 }
500 }
501 if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
502 $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
503 }
504 return($tmp);
505 }
507 function Add_Element()
508 {
509 $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
510 $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
511 }
512 }
515 /* Create valid sieve string/string-list
516 * out of a given array
517 */
518 function sieve_create_strings($data)
519 {
520 $ret = "";
521 if(is_array($data)){
522 if(count($data) == 1){
523 $ret = "\"";
524 foreach($data as $dat){
525 $ret .=$dat;
526 }
527 $ret.="\"";
528 }else{
529 foreach($data as $dat){
530 $ret.= "\"";
531 $ret.=$dat;
532 $ret.="\", ";
533 }
534 $ret = preg_replace("/,$/","",trim($ret));
535 $ret = "[".$ret."]";
536 }
537 }else{
539 $Multiline = preg_match("/\n/",$data);
540 $data = preg_replace("/\r/","",$data);;
542 if($Multiline){
543 $ret = "text: \r\n".$data."\r\n.\r\n";
544 }else{
545 $ret = "\"".$data."\"";
546 }
547 }
548 $ret = preg_replace("/\"\"/","\"",$ret);
549 $ret = preg_replace("/\n/","\r\n",$ret);
551 return($ret);
552 }
554 /* This checks if there is a string at the current position
555 * in the token array.
556 * If there is a string list at the current position,
557 * this function will return a complete list of all
558 * strings used in this list.
559 * It also returns an offset of the last token position
560 */
561 function sieve_get_strings($data,$id)
562 {
563 $ret = array();
564 if($data[$id]['class'] == "left-bracket"){
565 while($data[$id]['class'] != "right-bracket" && $id < count($data)){
567 if($data[$id]['class'] == "quoted-string"){
568 $ret[] = $data[$id]['text'];
569 }
570 $id ++;
571 }
572 }elseif($data[$id]['class'] == "quoted-string"){
573 $ret[] = $data[$id]['text'];
574 }elseif($data[$id]['class'] == "number"){
575 $ret[] = $data[$id]['text'];
576 }
577 return(array("OFFSET" => $id, "STRINGS" => $ret));
578 }
580 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
581 ?>