From 6af79f1517d4a5c34088eaf6d0e5af69733f3eef Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Wed, 14 Jan 2015 07:30:31 -0500
Subject: [PATCH] Support "not allof" test as a negation of all sub-tests

---
 plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php |   56 ++++++++++++++++++++++------
 plugins/managesieve/Changelog                            |    1 
 plugins/managesieve/lib/Roundcube/rcube_sieve_script.php |   18 ++++++--
 3 files changed, 58 insertions(+), 17 deletions(-)

diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog
index 13ef577..af22bcc 100644
--- a/plugins/managesieve/Changelog
+++ b/plugins/managesieve/Changelog
@@ -1,4 +1,5 @@
 - Fix bug where actions without if/elseif/else in sieve scripts were skipped
+- Support "not allof" test as a negation of all sub-tests
 
 * version 8.1 [2014-12-09]
 -----------------------------------------------------------
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
index 6ed8762..035b267 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -1270,7 +1270,7 @@
         $out .= $hiddenfields->show();
 
         // 'any' flag
-        if ((!isset($this->form) && empty($scr['tests']) && !empty($src))
+        if ((!isset($this->form) && empty($scr['tests']) && !empty($scr))
             || (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not'])
         ) {
             $any = true;
@@ -1335,7 +1335,7 @@
         $out .= sprintf("%s<label for=\"%s\">%s</label>\n",
             $input_join, $field_id, rcube::Q($this->plugin->gettext('filterany')));
 
-        $rows_num = isset($scr) ? sizeof($scr['tests']) : 1;
+        $rows_num = !empty($scr['tests']) ? sizeof($scr['tests']) : 1;
 
         $out .= '<div id="rules"'.($any ? ' style="display: none"' : '').'>';
         for ($x=0; $x<$rows_num; $x++)
@@ -1466,30 +1466,25 @@
             $select_op->add(rcube::Q($this->plugin->gettext('valuenotequals')), 'value-ne');
         }
 
+        $test   = self::rule_test($rule);
+        $target = '';
+
         // target(s) input
         if (in_array($rule['test'], array('header', 'address', 'envelope'))) {
-            $test   = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
             $target = $rule['arg2'];
         }
         else if (in_array($rule['test'], array('body', 'date', 'currentdate'))) {
-            $test   = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
             $target = $rule['arg'];
         }
         else if ($rule['test'] == 'size') {
-            $test   = '';
-            $target = '';
             if (preg_match('/^([0-9]+)(K|M|G)?$/', $rule['arg'], $matches)) {
                 $sizetarget = $matches[1];
-                $sizeitem = $matches[2];
+                $sizeitem   = $matches[2];
             }
             else {
                 $sizetarget = $rule['arg'];
-                $sizeitem = $rule['item'];
+                $sizeitem   = $rule['item'];
             }
-        }
-        else {
-            $test   = ($rule['not'] ? 'not' : '').$rule['test'];
-            $target =  '';
         }
 
         // (current)date part select
@@ -1640,6 +1635,43 @@
         return $out;
     }
 
+    private static function rule_test(&$rule)
+    {
+        // first modify value/count tests with 'not' keyword
+        // we'll revert the meaning of operators
+        if ($rule['not'] && preg_match('/^(count|value)-([gteqnl]{2})/', $rule['type'], $m)) {
+            $rule['not'] = false;
+
+            switch ($m[2]) {
+            case 'gt': $rule['type'] = $m[1] . '-le'; break;
+            case 'ge': $rule['type'] = $m[1] . '-lt'; break;
+            case 'lt': $rule['type'] = $m[1] . '-ge'; break;
+            case 'le': $rule['type'] = $m[1] . '-gt'; break;
+            case 'eq': $rule['type'] = $m[1] . '-ne'; break;
+            case 'ne': $rule['type'] = $m[1] . '-eq'; break;
+            }
+        }
+        else if ($rule['not'] && $rule['test'] == 'size') {
+            $rule['not']  = false;
+            $rule['type'] = $rule['type'] == 'over' ? 'under' : 'over';
+        }
+
+        $set = array('header', 'address', 'envelope', 'body', 'date', 'currentdate');
+
+        // build test string supported by select element
+        if ($rule['size']) {
+            $test = $rule['type'];
+        }
+        else if (in_array($rule['test'], $set)) {
+            $test = ($rule['not'] ? 'not' : '') . ($rule['type'] ? $rule['type'] : 'is');
+        }
+        else {
+            $test = ($rule['not'] ? 'not' : '') . $rule['test'];
+        }
+
+        return $test;
+    }
+
     function action_div($fid, $id, $div=true)
     {
         $action   = isset($this->form) ? $this->form['actions'][$id] : $this->script[$fid]['actions'][$id];
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
index bc62d2f..518d79d 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
@@ -622,6 +622,7 @@
 
         $disabled = false;
         $join     = false;
+        $join_not = false;
 
         // disabled rule (false + comment): if false # .....
         if (preg_match('/^\s*false\s+#/i', $content)) {
@@ -650,15 +651,22 @@
                 $not = false;
             }
 
+            // we support "not allof" as a negation of allof sub-tests
+            if ($join_not) {
+                $not = !$not;
+            }
+
             switch ($token) {
             case 'allof':
-                $join = true;
+                $join     = true;
+                $join_not = $not;
                 break;
+
             case 'anyof':
                 break;
 
             case 'size':
-                $test = array('test' => 'size', 'not'  => $not);
+                $test = array('test' => 'size', 'not' => $not);
 
                 $test['arg'] = array_pop($tokens);
 
@@ -740,16 +748,16 @@
                 break;
 
             case 'exists':
-                $tests[] = array('test' => 'exists', 'not'  => $not,
+                $tests[] = array('test' => 'exists', 'not' => $not,
                     'arg'  => array_pop($tokens));
                 break;
 
             case 'true':
-                $tests[] = array('test' => 'true', 'not'  => $not);
+                $tests[] = array('test' => 'true', 'not' => $not);
                 break;
 
             case 'false':
-                $tests[] = array('test' => 'true', 'not'  => !$not);
+                $tests[] = array('test' => 'true', 'not' => !$not);
                 break;
             }
 

--
Gitblit v1.9.1