diff options
Diffstat (limited to 'plugins/managesieve/lib/Roundcube')
3 files changed, 256 insertions, 80 deletions
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php index 25016c878..69ae4b8a6 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php @@ -63,7 +63,7 @@ class rcube_sieve_engine 1 => 'notifyimportancehigh' ); - const VERSION = '8.1'; + const VERSION = '8.2'; const PROGNAME = 'Roundcube (Managesieve)'; const PORT = 4190; @@ -220,14 +220,14 @@ class rcube_sieve_engine * * @return int Connection status: 0 on success, >0 on failure */ - public function load_script($script_name = null) + protected function load_script($script_name = null) { // Get list of scripts $list = $this->list_scripts(); if ($script_name === null || $script_name === '') { // get (first) active script - if (!empty($this->active[0])) { + if (!empty($this->active)) { $script_name = $this->active[0]; } else if ($list) { @@ -1270,8 +1270,11 @@ class rcube_sieve_engine $out .= $hiddenfields->show(); // 'any' flag - if (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not']) + if ((!isset($this->form) && empty($scr['tests']) && !empty($scr)) + || (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not']) + ) { $any = true; + } // filter name input $field_id = '_name'; @@ -1332,7 +1335,7 @@ class rcube_sieve_engine $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++) @@ -1463,31 +1466,26 @@ class rcube_sieve_engine $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 if (in_array('date', $this->exts) || in_array('currentdate', $this->exts)) { @@ -1637,6 +1635,43 @@ class rcube_sieve_engine 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]; @@ -2043,7 +2078,6 @@ class rcube_sieve_engine // Handle active script(s) and list of scripts according to Kolab's KEP:14 if ($this->rc->config->get('managesieve_kolab_master')) { - // Skip protected names foreach ((array)$this->list as $idx => $name) { $_name = strtoupper($name); @@ -2071,7 +2105,10 @@ class rcube_sieve_engine foreach ($rules['actions'] as $action) { if ($action['type'] == 'include' && empty($action['global'])) { $name = preg_replace($filename_regex, '', $action['target']); - $this->active[] = $name; + // make sure the script exist + if (in_array($name, $this->list)) { + $this->active[] = $name; + } } } } @@ -2105,6 +2142,11 @@ class rcube_sieve_engine } } + // reindex + if (!empty($this->list)) { + $this->list = array_values($this->list); + } + return $this->list; } @@ -2282,7 +2324,7 @@ class rcube_sieve_engine $i = 1; foreach ($this->script as $idx => $filter) { - if ($filter['type'] != 'if') { + if (empty($filter['actions'])) { continue; } $fname = $filter['name'] ? $filter['name'] : "#$i"; @@ -2302,12 +2344,12 @@ class rcube_sieve_engine */ protected function init_script() { - $this->script = $this->sieve->script->as_array(); - - if (!$this->script) { + if (!$this->sieve->script) { return; } + $this->script = $this->sieve->script->as_array(); + $headers = array(); $exceptions = array('date', 'currentdate', 'size', 'body'); diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php index bc62d2ff4..518d79d35 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php @@ -622,6 +622,7 @@ class rcube_sieve_script $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 @@ class rcube_sieve_script $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 @@ class rcube_sieve_script 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; } diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php index 10aaea0e9..8d865008f 100644 --- a/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php +++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php @@ -24,6 +24,8 @@ class rcube_sieve_vacation extends rcube_sieve_engine { protected $error; + protected $script_name; + protected $vacation = array(); function actions() { @@ -34,6 +36,7 @@ class rcube_sieve_vacation extends rcube_sieve_engine $this->vacation_rule(); $this->vacation_post(); } + $this->plugin->add_label('vacation.saving'); $this->rc->output->add_handlers(array( 'vacationform' => array($this, 'vacation_form'), @@ -43,15 +46,90 @@ class rcube_sieve_vacation extends rcube_sieve_engine $this->rc->output->send('managesieve.vacation'); } - private function vacation_rule() + /** + * Find and load sieve script with/for vacation rule + * + * @return int Connection status: 0 on success, >0 on failure + */ + protected function load_script() { - $this->vacation = array(); + if ($this->script_name !== null) { + return 0; + } + + $list = $this->list_scripts(); + $master = $this->rc->config->get('managesieve_kolab_master'); + $included = array(); + + $this->script_name = false; + + // first try the active script(s)... + if (!empty($this->active)) { + // Note: there can be more than one active script on KEP:14-enabled server + foreach ($this->active as $script) { + if ($this->sieve->load($script)) { + foreach ($this->sieve->script->as_array() as $rule) { + if (!empty($rule['actions'])) { + if ($rule['actions'][0]['type'] == 'vacation') { + $this->script_name = $script; + return 0; + } + else if (empty($master) && $rule['actions'][0]['type'] == 'include') { + $included[] = $rule['actions'][0]['target']; + } + } + } + } + } + + // ...else try scripts included in active script (not for KEP:14) + foreach ($included as $script) { + if ($this->sieve->load($script)) { + foreach ($this->sieve->script->as_array() as $rule) { + if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') { + $this->script_name = $script; + return 0; + } + } + } + } + } + + // try all other scripts + if (!empty($list)) { + // else try included scripts + foreach (array_diff($list, $included, $this->active) as $script) { + if ($this->sieve->load($script)) { + foreach ($this->sieve->script->as_array() as $rule) { + if (!empty($rule['actions']) && $rule['actions'][0]['type'] == 'vacation') { + $this->script_name = $script; + return 0; + } + } + } + } + + // none of the scripts contains existing vacation rule + // use any (first) active or just existing script (in that order) + if (!empty($this->active)) { + $this->sieve->load($this->script_name = $this->active[0]); + } + else { + $this->sieve->load($this->script_name = $list[0]); + } + } - if (empty($this->active)) { + return $this->sieve->error(); + } + + private function vacation_rule() + { + if ($this->script_name === false || $this->script_name === null || !$this->sieve->load($this->script_name)) { return; } - $list = array(); + $list = array(); + $active = in_array($this->script_name, $this->active); // find (first) vacation rule foreach ($this->script as $idx => $rule) { @@ -68,14 +146,14 @@ class rcube_sieve_vacation extends rcube_sieve_engine $this->vacation = array_merge($rule['actions'][0], array( 'idx' => $idx, - 'disabled' => $rule['disabled'], + 'disabled' => $rule['disabled'] || !$active, 'name' => $rule['name'], 'tests' => $rule['tests'], 'action' => $action ?: 'keep', 'target' => $target, )); } - else { + else if ($active) { $list[$idx] = $rule['name']; } } @@ -202,8 +280,6 @@ class rcube_sieve_vacation extends rcube_sieve_engine $vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true'))); } - // @TODO: handle situation when there's no active script - if (!$error) { $rule = $this->vacation; $rule['type'] = 'if'; @@ -212,6 +288,7 @@ class rcube_sieve_vacation extends rcube_sieve_engine $rule['tests'] = $vacation_tests; $rule['join'] = $date_extension ? count($vacation_tests) > 1 : false; $rule['actions'] = array($vacation_action); + $rule['after'] = $after; if ($action && $action != 'keep') { $rule['actions'][] = array( @@ -221,40 +298,7 @@ class rcube_sieve_vacation extends rcube_sieve_engine ); } - // reset original vacation rule - if (isset($this->vacation['idx'])) { - $this->script[$this->vacation['idx']] = null; - } - - // re-order rules if needed - if (isset($after) && $after !== '') { - // add at target position - if ($after >= count($this->script) - 1) { - $this->script[] = $rule; - } - else { - $script = array(); - - foreach ($this->script as $idx => $r) { - if ($r) { - $script[] = $r; - } - - if ($idx == $after) { - $script[] = $rule; - } - } - - $this->script = $script; - } - } - else { - array_unshift($this->script, $rule); - } - - $this->sieve->script->content = array_values(array_filter($this->script)); - - if ($this->save_script()) { + if ($this->save_vacation_script($rule)) { $this->rc->output->show_message('managesieve.vacationsaved', 'confirmation'); $this->rc->output->send(); } @@ -507,6 +551,99 @@ class rcube_sieve_vacation extends rcube_sieve_engine } /** + * Saves vacation script (adding some variables) + */ + protected function save_vacation_script($rule) + { + // if script does not exist create a new one + if ($this->script_name === null || $this->script_name === false) { + $this->script_name = $this->rc->config->get('managesieve_script_name'); + if (empty($this->script_name)) { + $this->script_name = 'roundcube'; + } + + // use default script contents + if (!$this->rc->config->get('managesieve_kolab_master')) { + $script_file = $this->rc->config->get('managesieve_default'); + if ($script_file && is_readable($script_file)) { + $content = file_get_contents($script_file); + } + } + + // create and load script + if ($this->sieve->save_script($this->script_name, $content)) { + $this->sieve->load($this->script_name); + } + } + + $script_active = in_array($this->script_name, $this->active); + + // re-order rules if needed + if (isset($rule['after']) && $rule['after'] !== '') { + // reset original vacation rule + if (isset($this->vacation['idx'])) { + $this->script[$this->vacation['idx']] = null; + } + + // add at target position + if ($rule['after'] >= count($this->script) - 1) { + $this->script[] = $rule; + } + else { + $script = array(); + + foreach ($this->script as $idx => $r) { + if ($r) { + $script[] = $r; + } + + if ($idx == $rule['after']) { + $script[] = $rule; + } + } + + $this->script = $script; + } + + $this->script = array_values(array_filter($this->script)); + } + // update original vacation rule if it exists + else if (isset($this->vacation['idx'])) { + $this->script[$this->vacation['idx']] = $rule; + } + // otherwise put vacation rule on top + else { + array_unshift($this->script, $rule); + } + + // if the script was not active, we need to de-activate + // all rules except the vacation rule, but only if it is not disabled + if (!$script_active && !$rule['disabled']) { + foreach ($this->script as $idx => $r) { + if (empty($r['actions']) || $r['actions'][0]['type'] != 'vacation') { + $this->script[$idx]['disabled'] = true; + } + } + } + + if (!$this->sieve->script) { + return false; + } + + $this->sieve->script->content = $this->script; + + // save the script + $saved = $this->save_script($this->script_name); + + // activate the script + if ($saved && !$script_active && !$rule['disabled']) { + $this->activate_script($this->script_name); + } + + return $saved; + } + + /** * API: get vacation rule * * @return array Vacation rule information @@ -684,8 +821,6 @@ class rcube_sieve_vacation extends rcube_sieve_engine $vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true'))); } - // @TODO: handle situation when there's no active script - $rule = $this->vacation; $rule['type'] = 'if'; $rule['name'] = $rule['name'] ?: 'Out-of-Office'; @@ -702,16 +837,7 @@ class rcube_sieve_vacation extends rcube_sieve_engine ); } - // reset original vacation rule - if (isset($this->vacation['idx'])) { - $this->script[$this->vacation['idx']] = null; - } - - array_unshift($this->script, $rule); - - $this->sieve->script->content = array_values(array_filter($this->script)); - - return $this->save_script(); + return $this->save_vacation_script($rule); } /** |