diff options
Diffstat (limited to 'plugins/managesieve/lib')
-rw-r--r-- | plugins/managesieve/lib/rcube_sieve.php | 15 | ||||
-rw-r--r-- | plugins/managesieve/lib/rcube_sieve_script.php | 183 |
2 files changed, 168 insertions, 30 deletions
diff --git a/plugins/managesieve/lib/rcube_sieve.php b/plugins/managesieve/lib/rcube_sieve.php index 7b7ea6eb6..2ed2e54bf 100644 --- a/plugins/managesieve/lib/rcube_sieve.php +++ b/plugins/managesieve/lib/rcube_sieve.php @@ -44,7 +44,6 @@ class rcube_sieve public $script; // rcube_sieve_script object public $current; // name of currently loaded script - private $disabled; // array of disabled extensions private $exts; // array of supported extensions @@ -89,7 +88,17 @@ class rcube_sieve } $this->exts = $this->get_extensions(); - $this->disabled = $disabled; + + // disable features by config + if (!empty($disabled)) { + // we're working on lower-cased names + $disabled = array_map('strtolower', (array) $disabled); + foreach ($disabled as $ext) { + if (($idx = array_search($ext, $this->exts)) !== false) { + unset($this->exts[$idx]); + } + } + } } public function __destruct() { @@ -301,7 +310,7 @@ class rcube_sieve private function _parse($txt) { // parse - $script = new rcube_sieve_script($txt, $this->disabled, $this->exts); + $script = new rcube_sieve_script($txt, $this->exts); // fix/convert to Roundcube format if (!empty($script->content)) { diff --git a/plugins/managesieve/lib/rcube_sieve_script.php b/plugins/managesieve/lib/rcube_sieve_script.php index 3c6993dcc..f5ad62c3f 100644 --- a/plugins/managesieve/lib/rcube_sieve_script.php +++ b/plugins/managesieve/lib/rcube_sieve_script.php @@ -29,9 +29,9 @@ class rcube_sieve_script private $vars = array(); // "global" variables private $prefix = ''; // script header (comments) - private $capabilities = array(); // Sieve extensions supported by server private $supported = array( // Sieve extensions supported by class - 'fileinto', // RFC3028 + 'fileinto', // RFC5228 + 'envelope', // RFC5228 'reject', // RFC5429 'ereject', // RFC5429 'copy', // RFC3894 @@ -42,30 +42,30 @@ class rcube_sieve_script 'imap4flags', // RFC5232 'include', // draft-ietf-sieve-include-12 'variables', // RFC5229 - // TODO: body, notify + 'body', // RFC5173 + 'subaddress', // RFC5233 + // @TODO: enotify/notify, spamtest+virustest, mailbox, date ); /** * Object constructor * * @param string Script's text content - * @param array List of disabled extensions * @param array List of capabilities supported by server */ - public function __construct($script, $disabled=array(), $capabilities=array()) + public function __construct($script, $capabilities=array()) { - if (!empty($disabled)) { - // we're working on lower-cased names - $disabled = array_map('strtolower', (array) $disabled); - foreach ($disabled as $ext) { - if (($idx = array_search($ext, $this->supported)) !== false) { + $capabilities = array_map('strtolower', (array) $capabilities); + + // disable features by server capabilities + if (!empty($capabilities)) { + foreach ($this->supported as $idx => $ext) { + if (!in_array($ext, $capabilities)) { unset($this->supported[$idx]); } } } - $this->capabilities = array_map('strtolower', (array) $capabilities); - // Parse text content of the script $this->_parse_text($script); } @@ -182,7 +182,7 @@ class rcube_sieve_script $idx = 0; if (!empty($this->vars)) { - if (in_array('variables', (array)$this->capabilities)) { + if (in_array('variables', (array)$this->supported)) { $has_vars = true; array_push($exts, 'variables'); } @@ -222,34 +222,96 @@ class rcube_sieve_script $tests[$i] .= ($test['not'] ? 'not ' : ''); $tests[$i] .= 'size :' . ($test['type']=='under' ? 'under ' : 'over ') . $test['arg']; break; + case 'true': $tests[$i] .= ($test['not'] ? 'false' : 'true'); break; + case 'exists': $tests[$i] .= ($test['not'] ? 'not ' : ''); $tests[$i] .= 'exists ' . self::escape_string($test['arg']); break; + case 'header': $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= 'header'; - // relational operator + comparator - if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) { - array_push($exts, 'relational'); - array_push($exts, 'comparator-i;ascii-numeric'); + if (!empty($test['type'])) { + // relational operator + comparator + if (preg_match('/^(value|count)-([gteqnl]{2})/', $test['type'], $m)) { + array_push($exts, 'relational'); + array_push($exts, 'comparator-i;ascii-numeric'); - $tests[$i] .= 'header :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"'; + $tests[$i] .= ' :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"'; + } + else { + $this->add_comparator($test, $tests[$i], $exts); + + if ($test['type'] == 'regex') { + array_push($exts, 'regex'); + } + + $tests[$i] .= ' :' . $test['type']; + } } - else { + + $tests[$i] .= ' ' . self::escape_string($test['arg1']); + $tests[$i] .= ' ' . self::escape_string($test['arg2']); + break; + + case 'address': + case 'envelope': + if ($test['test'] == 'envelope') { + array_push($exts, 'envelope'); + } + + $tests[$i] .= ($test['not'] ? 'not ' : ''); + $tests[$i] .= $test['test']; + + if (!empty($test['part'])) { + $tests[$i] .= ' :' . $test['part']; + if ($test['part'] == 'user' || $test['part'] == 'detail') { + array_push($exts, 'subaddress'); + } + } + + $this->add_comparator($test, $tests[$i], $exts); + + if (!empty($test['type'])) { if ($test['type'] == 'regex') { array_push($exts, 'regex'); } - - $tests[$i] .= 'header :' . $test['type']; + $tests[$i] .= ' :' . $test['type']; } $tests[$i] .= ' ' . self::escape_string($test['arg1']); $tests[$i] .= ' ' . self::escape_string($test['arg2']); break; + + case 'body': + array_push($exts, 'body'); + + $tests[$i] .= ($test['not'] ? 'not ' : '') . 'body'; + + $this->add_comparator($test, $tests[$i], $exts); + + if (!empty($test['part'])) { + $tests[$i] .= ' :' . $test['part']; + + if (!empty($test['content']) && $test['part'] == 'content') { + $tests[$i] .= ' ' . self::escape_string($test['content']); + } + } + + if (!empty($test['type'])) { + if ($test['type'] == 'regex') { + array_push($exts, 'regex'); + } + $tests[$i] .= ' :' . $test['type']; + } + + $tests[$i] .= ' ' . self::escape_string($test['arg']); + break; } $i++; } @@ -311,7 +373,7 @@ class rcube_sieve_script case 'addflag': case 'setflag': case 'removeflag': - if (is_array($this->capabilities) && in_array('imap4flags', $this->capabilities)) + if (in_array('imap4flags', $this->supported)) array_push($exts, 'imap4flags'); else array_push($exts, 'imapflags'); @@ -448,7 +510,7 @@ class rcube_sieve_script // handle script header if (empty($options['prefix'])) { $options['prefix'] = true; - if ($prefix && strpos($prefix, 'Generated by Ingo')) { + if ($prefix && strpos($prefix, 'horde.org/ingo')) { $options['format'] = 'INGO'; } } @@ -559,7 +621,7 @@ class rcube_sieve_script $header = array('test' => 'header', 'not' => $not, 'arg1' => '', 'arg2' => ''); for ($i=0, $len=count($tokens); $i<$len; $i++) { if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { - $i++; + $header['comparator'] = $tokens[++$i]; } else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) { $header['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i]; @@ -576,6 +638,52 @@ class rcube_sieve_script $tests[] = $header; break; + case 'address': + case 'envelope': + $header = array('test' => $token, 'not' => $not, 'arg1' => '', 'arg2' => ''); + for ($i=0, $len=count($tokens); $i<$len; $i++) { + if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { + $header['comparator'] = $tokens[++$i]; + } + else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { + $header['type'] = strtolower(substr($tokens[$i], 1)); + } + else if (!is_array($tokens[$i]) && preg_match('/^:(localpart|domain|all|user|detail)$/i', $tokens[$i])) { + $header['part'] = strtolower(substr($tokens[$i], 1)); + } + else { + $header['arg1'] = $header['arg2']; + $header['arg2'] = $tokens[$i]; + } + } + + $tests[] = $header; + break; + + case 'body': + $header = array('test' => 'body', 'not' => $not, 'arg' => ''); + for ($i=0, $len=count($tokens); $i<$len; $i++) { + if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) { + $header['comparator'] = $tokens[++$i]; + } + else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) { + $header['type'] = strtolower(substr($tokens[$i], 1)); + } + else if (!is_array($tokens[$i]) && preg_match('/^:(raw|content|text)$/i', $tokens[$i])) { + $header['part'] = strtolower(substr($tokens[$i], 1)); + + if ($header['part'] == 'content') { + $header['content'] = $tokens[++$i]; + } + } + else { + $header['arg'] = $tokens[$i]; + } + } + + $tests[] = $header; + break; + case 'exists': $tests[] = array('test' => 'exists', 'not' => $not, 'arg' => array_pop($tokens)); @@ -597,9 +705,7 @@ class rcube_sieve_script } // ...and actions block - if ($tests) { - $actions = $this->_parse_actions($content); - } + $actions = $this->_parse_actions($content); if ($tests && $actions) { $result = array( @@ -747,6 +853,29 @@ class rcube_sieve_script } /** + * + */ + private function add_comparator($test, &$out, &$exts) + { + if (empty($test['comparator'])) { + return; + } + + if ($test['comparator'] == 'i;ascii-numeric') { + array_push($exts, 'relational'); + array_push($exts, 'comparator-i;ascii-numeric'); + } + else if (!in_array($test['comparator'], array('i;octet', 'i;ascii-casemap'))) { + array_push($exts, 'comparator-' . $test['comparator']); + } + + // skip default comparator + if ($test['comparator'] != 'i;ascii-casemap') { + $out .= ' :comparator ' . self::escape_string($test['comparator']); + } + } + + /** * Escape special chars into quoted string value or multi-line string * or list of strings * |