Files
data
Dockerfiles
assets
conf
web
css
fonts
img
inc
ajax
lib
sieve
extensions
SieveDumpable.php
SieveException.php
SieveKeywordRegistry.php
SieveParser.php
SieveScanner.php
SieveScript.php
SieveSemantics.php
SieveToken.php
SieveTree.php
keywords.xml
vendor
Yubico.php
composer.json
composer.lock
footer.inc.php
functions.address_rewriting.inc.php
functions.autoconfiguration.inc.php
functions.customize.inc.php
functions.dkim.inc.php
functions.docker.inc.php
functions.domain_admin.inc.php
functions.fail2ban.inc.php
functions.fwdhost.inc.php
functions.inc.php
functions.mailbox.inc.php
functions.policy.inc.php
functions.quarantine.inc.php
functions.relayhost.inc.php
functions.rsettings.inc.php
header.inc.php
init_db.inc.php
languages.min.css
languages.png
prerequisites.inc.php
sessions.inc.php
spf.inc.php
triggers.inc.php
vars.inc.php
js
lang
modals
admin.php
autoconfig.php
autodiscover-json.php
autodiscover.php
debug.php
edit.php
favicon.png
index.php
json_api.php
mailbox.php
mobileconfig.php
quarantine.php
robots.txt
user.php
helper-scripts
.gitignore
.travis.yml
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE
README.md
docker-compose.yml
generate_config.sh
update.sh
mailcow-dockerized/data/web/inc/lib/sieve/SieveParser.php

256 lines
7.4 KiB
PHP

<?php namespace Sieve;
include_once 'SieveTree.php';
include_once 'SieveScanner.php';
include_once 'SieveSemantics.php';
include_once 'SieveException.php';
class SieveParser
{
protected $scanner_;
protected $script_;
protected $tree_;
protected $status_;
public function __construct($script = null)
{
if (isset($script))
$this->parse($script);
}
public function GetParseTree()
{
return $this->tree_;
}
public function dumpParseTree()
{
return $this->tree_->dump();
}
public function getScriptText()
{
return $this->tree_->getText();
}
protected function getPrevToken_($parent_id)
{
$childs = $this->tree_->getChilds($parent_id);
for ($i = count($childs); $i > 0; --$i)
{
$prev = $this->tree_->getNode($childs[$i-1]);
if ($prev->is(SieveToken::Comment|SieveToken::Whitespace))
continue;
// use command owning a block or list instead of previous
if ($prev->is(SieveToken::BlockStart|SieveToken::Comma|SieveToken::LeftParenthesis))
$prev = $this->tree_->getNode($parent_id);
return $prev;
}
return $this->tree_->getNode($parent_id);
}
/*******************************************************************************
* methods for recursive descent start below
*/
public function passthroughWhitespaceComment($token)
{
return 0;
}
public function passthroughFunction($token)
{
$this->tree_->addChild($token);
}
public function parse($script)
{
$this->script_ = $script;
$this->scanner_ = new SieveScanner($this->script_);
// Define what happens with passthrough tokens like whitespacs and comments
$this->scanner_->setPassthroughFunc(
array(
$this, 'passthroughWhitespaceComment'
)
);
$this->tree_ = new SieveTree('tree');
$this->commands_($this->tree_->getRoot());
if (!$this->scanner_->nextTokenIs(SieveToken::ScriptEnd)) {
$token = $this->scanner_->nextToken();
throw new SieveException($token, SieveToken::ScriptEnd);
}
}
protected function commands_($parent_id)
{
while (true)
{
if (!$this->scanner_->nextTokenIs(SieveToken::Identifier))
break;
// Get and check a command token
$token = $this->scanner_->nextToken();
$semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
// Process eventual arguments
$this_node = $this->tree_->addChildTo($parent_id, $token);
$this->arguments_($this_node, $semantics);
$token = $this->scanner_->nextToken();
if (!$token->is(SieveToken::Semicolon))
{
// TODO: check if/when semcheck is needed here
$semantics->validateToken($token);
if ($token->is(SieveToken::BlockStart))
{
$this->tree_->addChildTo($this_node, $token);
$this->block_($this_node, $semantics);
continue;
}
throw new SieveException($token, SieveToken::Semicolon);
}
$semantics->done($token);
$this->tree_->addChildTo($this_node, $token);
}
}
protected function arguments_($parent_id, &$semantics)
{
while (true)
{
if ($this->scanner_->nextTokenIs(SieveToken::Number|SieveToken::Tag))
{
// Check if semantics allow a number or tag
$token = $this->scanner_->nextToken();
$semantics->validateToken($token);
$this->tree_->addChildTo($parent_id, $token);
}
else if ($this->scanner_->nextTokenIs(SieveToken::StringList))
{
$this->stringlist_($parent_id, $semantics);
}
else
{
break;
}
}
if ($this->scanner_->nextTokenIs(SieveToken::TestList))
{
$this->testlist_($parent_id, $semantics);
}
}
protected function stringlist_($parent_id, &$semantics)
{
if (!$this->scanner_->nextTokenIs(SieveToken::LeftBracket))
{
$this->string_($parent_id, $semantics);
return;
}
$token = $this->scanner_->nextToken();
$semantics->startStringList($token);
$this->tree_->addChildTo($parent_id, $token);
if($this->scanner_->nextTokenIs(SieveToken::RightBracket)) {
//allow empty lists
$token = $this->scanner_->nextToken();
$this->tree_->addChildTo($parent_id, $token);
$semantics->endStringList();
return;
}
do
{
$this->string_($parent_id, $semantics);
$token = $this->scanner_->nextToken();
if (!$token->is(SieveToken::Comma|SieveToken::RightBracket))
throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightBracket));
if ($token->is(SieveToken::Comma))
$semantics->continueStringList();
$this->tree_->addChildTo($parent_id, $token);
}
while (!$token->is(SieveToken::RightBracket));
$semantics->endStringList();
}
protected function string_($parent_id, &$semantics)
{
$token = $this->scanner_->nextToken();
$semantics->validateToken($token);
$this->tree_->addChildTo($parent_id, $token);
}
protected function testlist_($parent_id, &$semantics)
{
if (!$this->scanner_->nextTokenIs(SieveToken::LeftParenthesis))
{
$this->test_($parent_id, $semantics);
return;
}
$token = $this->scanner_->nextToken();
$semantics->validateToken($token);
$this->tree_->addChildTo($parent_id, $token);
do
{
$this->test_($parent_id, $semantics);
$token = $this->scanner_->nextToken();
if (!$token->is(SieveToken::Comma|SieveToken::RightParenthesis))
{
throw new SieveException($token, array(SieveToken::Comma, SieveToken::RightParenthesis));
}
$this->tree_->addChildTo($parent_id, $token);
}
while (!$token->is(SieveToken::RightParenthesis));
}
protected function test_($parent_id, &$semantics)
{
// Check if semantics allow an identifier
$token = $this->scanner_->nextToken();
$semantics->validateToken($token);
// Get semantics for this test command
$this_semantics = new SieveSemantics($token, $this->getPrevToken_($parent_id));
$this_node = $this->tree_->addChildTo($parent_id, $token);
// Consume eventual argument tokens
$this->arguments_($this_node, $this_semantics);
// Check that all required arguments were there
$token = $this->scanner_->peekNextToken();
$this_semantics->done($token);
}
protected function block_($parent_id, &$semantics)
{
$this->commands_($parent_id, $semantics);
$token = $this->scanner_->nextToken();
if (!$token->is(SieveToken::BlockEnd))
{
throw new SieveException($token, SieveToken::BlockEnd);
}
$this->tree_->addChildTo($parent_id, $token);
}
}