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 }
46 /* Create html results */
47 $smarty = get_smarty();
48 $smarty->assign("Contents",$this->dump_);
49 $ret = $smarty->fetch(get_template_path("templates/edit_frame_base.tpl",TRUE,dirname(__FILE__)));
50 return ($ret);
51 }
54 /* This function walks through the object tree generated by the "Parse" class.
55 * All Commands will be resolved and grouped. So the Commands and their
56 * parameter are combined. Like "IF" and ":comparator"...
57 */
58 function doDump_($node_id, $prefix, $last,$num = 1)
59 {
60 /* Indicates that current comman will only be valid for a single line.
61 * this command type will be removed from mode_stack after displaying it.
62 */
63 $rewoke_last = FALSE;
65 /* Get node */
66 $node = $this->nodes_[$node_id];
68 /* This closes the last mode */
69 if($node['class'] == "block-start"){
70 $tmp = array_pop($this->mode_stack);
71 $this->handle_elements($tmp,$node_id);
72 $this->handle_elements(array("TYPE" => "block_start"),$node_id);
73 }
75 /* This closes the last mode */
76 if($node['class'] == "block-end"){
77 $tmp = array_pop($this->mode_stack);
78 $this->handle_elements($tmp,$node_id);
79 $this->handle_elements(array("TYPE" => "block_end"),$node_id);
80 }
82 /* Semicolon indicates a new command */
83 if($node['class'] == "semicolon"){
84 $tmp =array_pop($this->mode_stack);
85 $this->handle_elements($tmp,$node_id);
86 }
88 /* Handle comments */
89 if($node['class'] == "comment"){
90 $this->mode_stack[] = array("TYPE" => $node['class']);
91 $rewoke_last = TRUE;
92 }
94 /* Handle identifiers */
95 $identifiers = array("else","if","elsif","end","reject","redirect","vacation","keep","discard","comment","fileinto","require","stop");
96 if($node['class'] == "identifier" && in_array($node['text'],$identifiers)){
97 $this->mode_stack[] = array("TYPE" => $node['text']);
98 }
100 /* Add current node to current command stack */
101 end($this->mode_stack);
102 $key = key($this->mode_stack);
103 $this->mode_stack[$key]['ELEMENTS'][] = $node;
105 /* Remove last mode from mode stack, cause it was only valid for a single line */
106 if($rewoke_last){
107 $tmp =array_pop($this->mode_stack);
108 $this->handle_elements($tmp,$node_id);
109 }
111 /* If this is a sub element, just call this for all childs */
112 if(isset($this->childs_[$node_id])){
113 $childs = $this->childs_[$node_id];
114 for ($i=0; $i<count($childs); ++$i)
115 {
116 $c_last = false;
117 if ($i+1 == count($childs))
118 {
119 $c_last = true;
120 }
121 $this->doDump_($childs[$i], "", $num);
122 }
123 }
124 }
127 /* Create a class for each resolved object.
128 * And append this class to a list of objects.
129 */
130 function handle_elements($data,$id)
131 {
132 if(!isset($data['TYPE'])){
133 return;
134 }
135 $type = $data['TYPE'];
137 $class_name= "sieve_".$type ;
138 if(class_exists($class_name)){
139 $this->pap[] = new $class_name($data,$id);
140 }else{
141 echo "<font color='red'>Missing : ".$class_name."</font>"."<br>";
142 }
143 }
145 function save_object()
146 {
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"))){
150 $this->pap[$key]->save_object();
151 }
153 $once = TRUE;
154 foreach($_POST as $name => $value){
156 if(isset($obj->object_id) && preg_match("/^Remove_Object_".$obj->object_id."_/",$name) && $once){
157 $once = FALSE;
158 $this->remove_object($key);
159 }
160 if(isset($obj->object_id) && preg_match("/^Move_Up_Object_".$obj->object_id."_/",$name) && $once){
161 $this->move_up_down($key,"up");
162 $once = FALSE;
163 }
164 if(isset($obj->object_id) && preg_match("/^Move_Down_Object_".$obj->object_id."_/",$name) && $once){
165 $this->move_up_down($key,"down");
166 $once = FALSE;
167 }
168 }
169 }
170 }
173 function remove_object($key_id){
174 unset($this->pap[$key_id]);
175 }
178 /* This function moves a given element to another position.
179 * Single elements like "keep;" will simply be moved one posisition down/up.
180 * Multiple elements like if-elsif-else will be moved as block.
181 *
182 * $key_id specified the element that should be moved.
183 * $direction specifies to move elements "up" or "down"
184 */
185 function move_up_down($key_id,$direction = "down")
186 {
188 /* Get the current element to decide what to move. */
189 $e_class = get_class($this->pap[$key_id]);
192 if(in_array($e_class,array("sieve_if"))){
193 echo "move block";
194 }
196 if(in_array($e_class,array("sieve_stop","sieve_keep","sieve_require", "sieve_stop", "sieve_reject", "sieve_fileinto", "sieve_redirect", "sieve_discard"))){
197 echo "move single ".$key_id." to ".$this->_get_next_free_move_slot($key_id,$direction)."<br>";
198 $this->move_single_element_to($key_id,$this->_get_next_free_move_slot($key_id,$direction));
199 }
200 }
203 function move_single_element_to($from,$to)
204 {
205 if($from == $to) return;
207 $ret = array();
210 $tmp = $this->pap;
212 # $tmp = array();
213 # foreach($this->pap as $class){
214 # $tmp[] = get_class($class);
215 # }
217 if($from > $to ){
219 $element = $this->pap[$from];
221 $begin = array();
222 $middle = array();
223 $end = array();
225 /* Get all element in fron to element to move */
226 if($from != 0){
227 $begin = array_slice($tmp,0,$to);
228 }
230 /* Get all elements between */
231 $middle = array_slice($tmp,$to , ($from - ($to) ));
233 /* Get the rest */
234 $end = array_slice($tmp,$from+1);
236 foreach($begin as $data){
237 $ret[] = $data;
238 }
239 $ret[] = $element;
240 foreach($middle as $data){
241 $ret[] = $data;
242 }
243 foreach($end as $data){
244 $ret[] = $data;
245 }
247 # print_a(array("Anfang" => $begin ,$element, "middle" => $middle, "end" => $end));
249 $this->pap = $ret;
250 }
251 if($from < $to ){
253 $element = $this->pap[$from];
255 $begin = array();
256 $middle = array();
257 $end = array();
259 /* Get all element in fron to element to move */
260 if($from != 0){
261 $begin = array_slice($tmp,0,$from);
262 }
264 /* Get all elements between */
265 $middle = array_slice($tmp,$from+1 , ($to - ($from)));
267 /* Get the rest */
268 $end = array_slice($tmp,$to+1);
270 foreach($begin as $data){
271 $ret[] = $data;
272 }
273 foreach($middle as $data){
274 $ret[] = $data;
275 }
276 $ret[] = $element;
277 foreach($end as $data){
278 $ret[] = $data;
279 }
280 $this->pap = $ret;
281 }
282 }
285 function _get_next_free_move_slot($key_id,$direction)
286 {
287 $last_class = "";
288 $current_class ="";
289 $next_class = "";
291 $allowed_to_add_after = array("sieve_keep",
292 "sieve_require",
293 "sieve_stop",
294 "sieve_reject",
295 "sieve_fileinto",
296 "sieve_redirect",
297 "sieve_discard",
298 "sieve_block_end");
300 if($direction == "down"){
301 $test = $this->pap;
304 while($key_id < count($test)){
306 if(($key_id+1) == count($test)) return($key_id);
308 $key_id ++;
310 $current_class = get_class($test[$key_id]);
311 if(in_array($current_class, $allowed_to_add_after)){
312 return($key_id);
313 }
314 }
315 }else{
317 $test = $this->pap;
319 if($key_id == 0) return($key_id);
321 $key_id --;
322 while($key_id >=0 ){
324 $current_class = get_class($test[$key_id]);
325 if(in_array($current_class, $allowed_to_add_after)){
326 return($key_id);
327 }
328 $key_id --;
329 }
330 }
331 }
333 function move_object_up($key_id)
334 {
335 $new = array();
336 $add_now = NULL;
337 $key_id --;
339 if(!$key_id < 0) return;
341 foreach($this->pap as $key => $data){
342 if($key == $key_id){
343 $add_now = $data;
344 continue;
345 }else{
346 $new[] = $data;
347 }
349 if($add_now != NULL){
351 $new[] = $add_now;
352 $add_now = NULL;
353 }
354 }
355 if($add_now != NULL){
356 $new[] = $add_now;
357 }
358 $this->pap = $new;
359 }
362 /* This function moves the given element one position down
363 * if the next position is between
364 * '}' 'else[if]' or 'if' '{' use next available
365 */
366 function move_object_down($key_id)
367 {
368 $new = array();
369 $add_now = NULL;
371 /* Walk through all elements, till we found the given id.
372 * If we found it, skip adding the current element,
373 * first add the next element followed by the current.
374 */
375 foreach($this->pap as $key => $data){
377 /* Have we found the given id */
378 if($key == $key_id){
379 $add_now = $data;
380 $last_class = get_class($data);
381 continue;
382 }else{
384 /* Add entry */
385 $new[] = $data;
386 }
388 /* We have skipped adding an element before,
389 * try to add it now, if the position allows this.
390 */
391 if($add_now != NULL){
393 /* Don't allow adding an element directly after
394 * if/else/elsif
395 */
396 if(in_array(get_class($data),array("sieve_if","sieve_elsif","sieve_else"))){
397 continue;
398 }
400 /* If this is an block end, check if there
401 * follows an if/else/elsif and skip adding the element in this case.
402 */
403 $next ="";
404 if(isset($this->pap[$key+1])){
405 $next = get_class($this->pap[$key+1]);
406 }
407 if(in_array(get_class($data),array("sieve_block_end")) && in_array($next,array("sieve_elsif","sieve_else"))){
408 continue;
409 }
411 /* Add element, position seems to be ok */
412 $new[] = $add_now;
413 $add_now = NULL;
414 }
415 }
417 /* Element wasn't added, add it as last element */
418 if($add_now != NULL){
419 $new[] = $add_now;
420 }
421 $this->pap = $new;
422 }
425 /* Need to be reviewed */
426 function get_sieve_script()
427 {
428 $tmp ="";
429 if(count($this->pap)){
430 $buffer = "";
431 foreach($this->pap as $part) {
432 if(get_class($part) == "sieve_block_end"){
433 $buffer = substr($buffer,0,strlen($buffer)-(strlen(SIEVE_INDENT_TAB)));
434 }
435 $tmp2 = $part->get_sieve_script_part();
437 if(get_class($part) == "sieve_reject"){
438 $tmp.=$tmp2;
439 }else{
441 $tmp3 = split("\n",$tmp2);
442 foreach($tmp3 as $str){
443 $str2 = trim($str);
444 if(empty($str2)) continue;
445 $tmp.= $buffer.$str."\n";
446 }
447 }
448 if(get_class($part) == "sieve_block_start"){
449 $buffer .= SIEVE_INDENT_TAB;
450 }
451 }
452 }
453 if(!preg_match("/Generated by GOsa - Gonicus System Administrator/",$tmp)){
454 $tmp = "#Generated by GOsa - Gonicus System Administrator \n ".$tmp;
455 }
456 return($tmp);
457 }
459 function Add_Element()
460 {
461 $tmp = array("ELEMENTS" => array(array("class" => "qouted-string","text"=> "Bla bla, later more")));
462 $this->pap[] = new sieve_comment($tmp,rand(1000,100000));
463 }
464 }
467 /* Create valid sieve string/string-list
468 * out of a given array
469 */
470 function sieve_create_strings($data)
471 {
472 $ret = "";
473 if(is_array($data)){
474 if(count($data) == 1){
475 $ret = "\"";
476 foreach($data as $dat){
477 $ret .=$dat;
478 }
479 $ret.="\"";
480 }else{
481 foreach($data as $dat){
482 $ret.= "\"";
483 $ret.=$dat;
484 $ret.="\", ";
485 }
486 $ret = preg_replace("/,$/","",trim($ret));
487 $ret = "[".$ret."]";
488 }
489 }else{
491 $Multiline = preg_match("/\n/",$data);
492 $data = preg_replace("/\r/","",$data);;
494 if($Multiline){
495 $ret = "text: \r\n".$data."\r\n.\r\n";
496 }else{
497 $ret = "\"".$data."\"";
498 }
499 }
500 $ret = preg_replace("/\"\"/","\"",$ret);
501 $ret = preg_replace("/\n/","\r\n",$ret);
503 return($ret);
504 }
506 /* This checks if there is a string at the current position
507 * in the token array.
508 * If there is a string list at the current position,
509 * this function will return a complete list of all
510 * strings used in this list.
511 * It also returns an offset of the last token position
512 */
513 function sieve_get_strings($data,$id)
514 {
515 $ret = array();
516 if($data[$id]['class'] == "left-bracket"){
517 while($data[$id]['class'] != "right-bracket" && $id < count($data)){
519 if($data[$id]['class'] == "quoted-string"){
520 $ret[] = $data[$id]['text'];
521 }
522 $id ++;
523 }
524 }elseif($data[$id]['class'] == "quoted-string"){
525 $ret[] = $data[$id]['text'];
526 }elseif($data[$id]['class'] == "number"){
527 $ret[] = $data[$id]['text'];
528 }
529 return(array("OFFSET" => $id, "STRINGS" => $ret));
530 }
532 // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler:
533 ?>