Code

Fixed script output
[gosa.git] / include / sieve / class_sievescript.php
1 <?php
3 /*
4         requirestring: envelope, fileinto, reject, comparator-*
5         address-part: localpart, domain, all*
6         match-type: is*, contains, matches
7         comperator: i;octet, i;ascii-casemap
9         <if>            <test:+>                <block:1>
10         <elsif>         <test:+>                <block:1>
11         <else>          <block:1>
12         <require>       <requirestring:+>
13         <reject>        <string:?>
14         <fileinto>      <string:1>
15         <redirect>      <string:1>
16         <stop>
17         <keep>
18         <discard>
20         <address>       <address-part,comperator,match-type:?>  <string:+>      <string:+>
21         <envelope>      <address-part,comperator,match-type:?>  <string:+>      <string:+>
22         <header>        <comperator,match-type:?>       <string:+>      <string:+>
23         <size>          <:over|:under:1>        <number:1>
24         <allof>         <test:+>
25         <anyof>         <test:+>
26         <exists>        <string:+>
27         <not>           <test:1>
28         <true>
29         <false>
30 */
32 class SieveScript
33 {
34         var $_scanner;
35         var $_script;
36         var $_tree;
37         var $_status;
38         var $_noMoreRequires;
40         var $_test_identifier = array('address', 'allof', 'anyof', 'envelope', 'exists', 'false', 'header', 'not', 'size', 'true');
42         var $status_text;
44         function parse($script)
45         {
46                 $this->status_text = "incomplete";
48                 $root = 'script_start';
49                 $this->_noMoreRequires = false;
50                 $this->_script = $script;
51                 $this->_tree = new Tree($root);
52                 $this->_tree->setDumpFunc(array($this, '_dumpToken'));
53                 $this->_scanner = new Scanner($this->_script);
54                 $this->_scanner->setCommentFunc(array($this, '_comment'));
56                 if ($this->_commands($this->_tree->getRoot()) &&
57                     $this->_scanner->nextTokenIs('script-end'))
58                 {
59                         return $this->_success('success');
60                 }
62                 return $this->_status;
63         }
65         function _dumpToken(&$token)
66         {
67                 if (is_array($token))
68                 {
69                         $str = "&lt;" . chop(mb_substr($this->_script, $token['pos'], $token['len'])) . "&gt; ";
70                         foreach ($token as $k => $v)
71                         {
72                                 $str .= " $k:$v";
73                         }
74                         return $str;
75                 }
77                 return strval($token);
78         }
80         function _tokenStringIs($token, $text)
81         {
82                 return mb_substr($this->_script, $token['pos'], $token['len']) == $text;
83         }
85         function _success($text = null)
86         {
87                 if ($text != null)
88                 {
89                         $this->status_text = $text;
90                 }
92                 return $this->_status = true;
93         }
95         function _error($text, $token = null)
96         {
97                 if ($token != null)
98                 {
99                         $text = 'line '. $token['line'] .': '. $token['class'] . " where $text expected near ".
100                                 '"'. mb_substr($this->_script, $token['pos'], $token['len']) .'"';
101                 }
103                 $this->status_text = $text;
104                 return $this->_status = false;
105         }
107         function _done()
108         {
109                 return false;
110         }
112         function _comment($token)
113         {
114                 $this->_tree->addChild($token);
115         }
117         function _commands($parent_id)
118         {
119                 while ($this->_command($parent_id));
121                 return $this->_status;
122         }
124         function _command($parent_id)
125         {
126                 if (!$this->_scanner->nextTokenIs('identifier'))
127                 {
128                         if ($this->_scanner->nextTokenIs(array('right-curly', 'script-end')))
129                         {
130                                 return $this->_done();
131                         }
132                         return $this->_error('identifier', $this->_scanner->peekNextToken());
133                 }
135                 // Get and check a command token
136                 $token = $this->_scanner->nextToken();
137                 $command = mb_substr($this->_script, $token['pos'], $token['len']);
139                 if (!in_array($command, array('if', 'elsif', 'else', 'require', 'stop', 'reject', 'fileinto', 'redirect', 'keep', 'discard')))
140                 {
141                         return $this->_error('unknown command: '. $command);
142                 }
144                 if ($command != 'require')
145                 {
146                         $this->_noMoreRequires = true;
147                 }
148                 else if ($this->_noMoreRequires)
149                 {
150                         return $this->_error('misplaced require');
151                 }
153                 $this_node = $this->_tree->addChildTo($parent_id, $token);
155                 if (in_array($command, array('if', 'elsif', 'require', 'reject', 'fileinto', 'redirect')))
156                 {
157                         // TODO: handle optional arguments in reject
158                         if ($this->_arguments($this_node) == false)
159                         {
160                                 return false;
161                         }
162                 }
164                 $token = $this->_scanner->nextToken();
165                 if (in_array($command, array('if', 'elsif', 'else')))
166                 {
167                         if ($token['class'] != 'left-curly')
168                         {
169                                 return $this->_error('block', $token);
170                         }
171                         $this->_tree->addChildTo($this_node, $token);
172                         return $this->_block($this_node);
173                 }
174                 else if ($token['class'] != 'semicolon')
175                 {
176                         return $this->_error('semicolon', $token);
177                 }
179                 $this->_tree->addChildTo($this_node, $token);
180                 return $this->_success();
181         }
183         function _block($parent_id)
184         {
185                 //TODO: test if cmd is ok w/ block
186                 if ($this->_commands($parent_id))
187                 {
188                         $token = $this->_scanner->nextToken();
189         
190                         if ($token['class'] != 'right-curly')
191                         {
192                                 return $this->_error('closing curly brace', $token);
193                         }
194         
195                         $this->_tree->addChildTo($parent_id, $token);
196                         return $this->_success();
197                 }
198                 return $this->_status;
199         }
201         function _arguments($parent_id)
202         {
203                 while ($this->_argument($parent_id));
204                 if ($this->_status == true)
205                 {
206                         $this->_testlist($parent_id);
207                 }
208                 return $this->_status;
209         }
211         function _argument($parent_id)
212         {
213                 if (!$this->_scanner->nextTokenIs(array('number', 'tag')))
214                 {
215                         return $this->_stringlist($parent_id);
216                 }
217                 else
218                 {
219                         if ($this->_tokenStringIs($this->_tree->getNode($parent_id), 'require'))
220                         {
221                                 return $this->_error('stringlist', $this->_scanner->nextToken());
222                         }
223                 }
225                 $token = $this->_scanner->nextToken();
226                 $this->_tree->addChildTo($parent_id, $token);
228                 return $this->_success();
229         }
231         function _test($parent_id)
232         {
233                 if (!$this->_scanner->nextTokenIs('identifier'))
234                 {
235                         return $this->_done();
236                 }
238                 $token = $this->_scanner->nextToken();
239                 $this_node = $this->_tree->addChildTo($parent_id, $token);
241                 $this->_arguments($this_node);
243                 //TODO: check test for validity here
245                 return $this->_status;
246         }
248         function _testlist($parent_id)
249         {
250                 if (!$this->_scanner->nextTokenIs('left-parant'))
251                 {
252                         return $this->_test($parent_id);
253                 }
255                 $token = $this->_scanner->nextToken();
256                 $this->_tree->addChildTo($parent_id, $token);
258                 while ($token['class'] != 'right-parant')
259                 {
260                         if (!$this->_test($parent_id))
261                         {
262                                 return $this->_status;
263                         }
265                         $token = $this->_scanner->nextToken();
267                         if ($token['class'] != 'comma' && $token['class'] != 'right-parant')
268                         {
269                                 return $this->_error('comma or closing paranthesis', $token);
270                         }
272                         $this->_tree->addChildTo($parent_id, $token);
273                 }
275                 return $this->_success();
276         }
278         function _string($parent_id)
279         {
280                 if (!$this->_scanner->nextTokenIs(array('quoted-string', 'multi-line')))
281                 {
282                         return $this->_done();
283                 }
285                 $this->_tree->addChildTo($parent_id, $this->_scanner->nextToken());
287                 return $this->_success();
288         }
290         function _stringlist($parent_id)
291         {
292                 if (!$this->_scanner->nextTokenIs('left-bracket'))
293                 {
294                         return $this->_string($parent_id);
295                 }
297                 $token = $this->_scanner->nextToken();
298                 $this->_tree->addChildTo($parent_id, $token);
300                 while ($token['class'] != 'right-bracket')
301                 {
302                         if (!$this->_string($parent_id))
303                         {
304                                 return $this->_status;
305                         }
307                         $token = $this->_scanner->nextToken();
309                         if ($token['class'] != 'comma' && $token['class'] != 'right-bracket')
310                         {
311                                 return $this->_error('comma or closing bracket', $token);
312                         }
314                         $this->_tree->addChildTo($parent_id, $token);
315                 }
317                 return $this->_success();
318         }
321 ?>