summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG1
-rw-r--r--plugins/managesieve/Changelog12
-rw-r--r--plugins/managesieve/lib/rcube_sieve.php15
-rw-r--r--plugins/managesieve/lib/rcube_sieve_script.php183
-rw-r--r--plugins/managesieve/localization/en_US.inc19
-rw-r--r--plugins/managesieve/localization/pl_PL.inc19
-rw-r--r--plugins/managesieve/managesieve.js41
-rw-r--r--plugins/managesieve/managesieve.php349
-rw-r--r--plugins/managesieve/package.xml100
-rw-r--r--plugins/managesieve/skins/default/images/down_small.gifbin0 -> 106 bytes
-rw-r--r--plugins/managesieve/skins/default/images/toolbar.pngbin12093 -> 0 bytes
-rw-r--r--plugins/managesieve/skins/default/images/up_small.gifbin0 -> 106 bytes
-rw-r--r--plugins/managesieve/skins/default/managesieve.css38
-rw-r--r--plugins/managesieve/tests/parser.phpt90
-rw-r--r--plugins/managesieve/tests/parser_body.phpt49
-rw-r--r--plugins/managesieve/tests/parser_imapflags.phpt28
-rw-r--r--plugins/managesieve/tests/parser_include.phpt30
-rw-r--r--plugins/managesieve/tests/parser_kep14.phpt2
-rw-r--r--plugins/managesieve/tests/parser_prefix.phpt25
-rw-r--r--plugins/managesieve/tests/parser_relational.phpt25
-rw-r--r--plugins/managesieve/tests/parser_vacation.phpt39
-rw-r--r--plugins/managesieve/tests/parser_variables.phpt39
-rw-r--r--plugins/managesieve/tests/parset_subaddress.phpt38
-rw-r--r--plugins/newmail_notifier/newmail_notifier.php2
-rw-r--r--program/include/rcube_imap.php2
-rw-r--r--program/js/app.js2
-rw-r--r--program/js/googiespell.js2
27 files changed, 971 insertions, 179 deletions
diff --git a/CHANGELOG b/CHANGELOG
index c0e4586e7..328865dfb 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
+
- Fix handling of HTML form elements in messages (#1485137)
- Fix regression in setting recipient to self when replying to a Sent message (#1487074)
- Fix listing of folders in hidden namespaces (#1486796)
diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog
index 185510410..d7ef8e561 100644
--- a/plugins/managesieve/Changelog
+++ b/plugins/managesieve/Changelog
@@ -1,4 +1,16 @@
+- Fixed setting test type to :is when none is specified
+
+* version 5.0-rc1 [2011-11-17]
+-----------------------------------------------------------
- Fixed sorting of scripts, scripts including aware of the sort order
+- Fixed import of rules with unsupported tests
+- Added 'address' and 'envelope' tests support
+- Added 'body' extension support (RFC5173)
+- Added 'subaddress' extension support (RFC5233)
+- Added comparators support
+- Changed Sender/Recipient labels to From/To
+- Fixed importing rule names from Ingo
+- Fixed handling of extensions disabled in config
* version 5.0-beta [2011-10-17]
-----------------------------------------------------------
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
*
diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc
index 24cd72a86..94e0ba60c 100644
--- a/plugins/managesieve/localization/en_US.inc
+++ b/plugins/managesieve/localization/en_US.inc
@@ -82,6 +82,25 @@ $labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';
$labels['...'] = '...';
+$labels['advancedopts'] = 'Advanced options';
+$labels['body'] = 'Body';
+$labels['address'] = 'address';
+$labels['envelope'] = 'envelope';
+$labels['modifier'] = 'modifier:';
+$labels['text'] = 'text';
+$labels['undecoded'] = 'undecoded (raw)';
+$labels['contenttype'] = 'content type';
+$labels['modtype'] = 'type:';
+$labels['allparts'] = 'all';
+$labels['domain'] = 'domain';
+$labels['localpart'] = 'local part';
+$labels['user'] = 'user';
+$labels['detail'] = 'detail';
+$labels['comparator'] = 'comparator:';
+$labels['default'] = 'default';
+$labels['octet'] = 'strict (octet)';
+$labels['asciicasemap'] = 'case insensitive (ascii-casemap)';
+$labels['asciinumeric'] = 'numeric (ascii-numeric)';
$messages = array();
$messages['filterunknownerror'] = 'Unknown server error.';
diff --git a/plugins/managesieve/localization/pl_PL.inc b/plugins/managesieve/localization/pl_PL.inc
index eaa09e198..c0ec2ede2 100644
--- a/plugins/managesieve/localization/pl_PL.inc
+++ b/plugins/managesieve/localization/pl_PL.inc
@@ -81,6 +81,25 @@ $labels['filtercreate'] = 'Utwóż filtr';
$labels['usedata'] = 'Użyj następujących danych do utworzenia filtra:';
$labels['nextstep'] = 'Następny krok';
$labels['...'] = '...';
+$labels['advancedopts'] = 'Zaawansowane opcje';
+$labels['body'] = 'Treść';
+$labels['address'] = 'adres';
+$labels['envelope'] = 'koperta (envelope)';
+$labels['modifier'] = 'modyfikator:';
+$labels['text'] = 'tekst';
+$labels['undecoded'] = 'nie (raw)';
+$labels['contenttype'] = 'typ części (content type)';
+$labels['modtype'] = 'typ:';
+$labels['allparts'] = 'wszystkie';
+$labels['domain'] = 'domena';
+$labels['localpart'] = 'część lokalna';
+$labels['user'] = 'użytkownik';
+$labels['detail'] = 'detal';
+$labels['comparator'] = 'komparator:';
+$labels['default'] = 'domyślny';
+$labels['octet'] = 'dokładny (octet)';
+$labels['asciicasemap'] = 'nierozróżniający wielkości liter (ascii-casemap)';
+$labels['asciinumeric'] = 'numeryczny (ascii-numeric)';
$messages = array();
$messages['filterunknownerror'] = 'Nieznany błąd serwera.';
diff --git a/plugins/managesieve/managesieve.js b/plugins/managesieve/managesieve.js
index 5f9c67004..a7b15f7b5 100644
--- a/plugins/managesieve/managesieve.js
+++ b/plugins/managesieve/managesieve.js
@@ -542,19 +542,28 @@ function rule_header_select(id)
size = document.getElementById('rule_size' + id),
op = document.getElementById('rule_op' + id),
target = document.getElementById('rule_target' + id),
- header = document.getElementById('custom_header' + id);
+ header = document.getElementById('custom_header' + id),
+ mod = document.getElementById('rule_mod' + id),
+ trans = document.getElementById('rule_trans' + id),
+ comp = document.getElementById('rule_comp' + id);
if (obj.value == 'size') {
size.style.display = 'inline';
op.style.display = 'none';
target.style.display = 'none';
header.style.display = 'none';
+ mod.style.display = 'none';
+ trans.style.display = 'none';
+ comp.style.display = 'none';
}
else {
header.style.display = obj.value != '...' ? 'none' : 'inline';
size.style.display = 'none';
op.style.display = 'inline';
+ comp.style.display = '';
rule_op_select(id);
+ mod.style.display = obj.value == 'body' ? 'none' : 'block';
+ trans.style.display = obj.value == 'body' ? 'block' : 'none';
}
obj.style.width = obj.value == '...' ? '40px' : '';
@@ -568,11 +577,41 @@ function rule_op_select(id)
target.style.display = obj.value == 'exists' || obj.value == 'notexists' ? 'none' : 'inline';
};
+function rule_trans_select(id)
+{
+ var obj = document.getElementById('rule_trans_op' + id),
+ target = document.getElementById('rule_trans_type' + id);
+
+ target.style.display = obj.value != 'content' ? 'none' : 'inline';
+};
+
+function rule_mod_select(id)
+{
+ var obj = document.getElementById('rule_mod_op' + id),
+ target = document.getElementById('rule_mod_type' + id);
+
+ target.style.display = obj.value != 'address' && obj.value != 'envelope' ? 'none' : 'inline';
+};
+
function rule_join_radio(value)
{
$('#rules').css('display', value == 'any' ? 'none' : 'block');
};
+function rule_adv_switch(id, elem)
+{
+ var elem = $(elem), enabled = elem.hasClass('hide'), adv = $('#rule_advanced'+id);
+
+ if (enabled) {
+ adv.hide();
+ elem.removeClass('hide').addClass('show');
+ }
+ else {
+ adv.show();
+ elem.removeClass('show').addClass('hide');
+ }
+}
+
function action_type_select(id)
{
var obj = document.getElementById('action_type' + id),
diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php
index 0787c7642..5ac406ade 100644
--- a/plugins/managesieve/managesieve.php
+++ b/plugins/managesieve/managesieve.php
@@ -45,9 +45,23 @@ class managesieve extends rcube_plugin
private $list;
private $active = array();
private $headers = array(
- 'subject' => 'Subject',
- 'sender' => 'From',
- 'recipient' => 'To',
+ 'subject' => 'Subject',
+ 'from' => 'From',
+ 'to' => 'To',
+ );
+ private $addr_headers = array(
+ // Required
+ "from", "to", "cc", "bcc", "sender", "resent-from", "resent-to",
+ // Additional (RFC 822 / RFC 2822)
+ "reply-to", "resent-reply-to", "resent-sender", "resent-cc", "resent-bcc",
+ // Non-standard (RFC 2076, draft-palme-mailext-headers-08.txt)
+ "for-approval", "for-handling", "for-comment", "apparently-to", "errors-to",
+ "delivered-to", "return-receipt-to", "x-admin", "read-receipt-to",
+ "x-confirm-reading-to", "return-receipt-requested",
+ "registered-mail-reply-requested-by", "mail-followup-to", "mail-reply-to",
+ "abuse-reports-to", "x-complaints-to", "x-report-abuse-to",
+ // Undocumented
+ "x-beenthere",
);
const VERSION = '5.0';
@@ -588,6 +602,11 @@ class managesieve extends rcube_plugin
$sizeitems = get_input_value('_rule_size_item', RCUBE_INPUT_POST);
$sizetargets = get_input_value('_rule_size_target', RCUBE_INPUT_POST);
$targets = get_input_value('_rule_target', RCUBE_INPUT_POST, true);
+ $mods = get_input_value('_rule_mod', RCUBE_INPUT_POST);
+ $mod_types = get_input_value('_rule_mod_type', RCUBE_INPUT_POST);
+ $body_trans = get_input_value('_rule_trans', RCUBE_INPUT_POST);
+ $body_types = get_input_value('_rule_trans_type', RCUBE_INPUT_POST, true);
+ $comparators = get_input_value('_rule_comp', RCUBE_INPUT_POST);
$act_types = get_input_value('_action_type', RCUBE_INPUT_POST, true);
$mailboxes = get_input_value('_action_mailbox', RCUBE_INPUT_POST, true);
$act_targets = get_input_value('_action_target', RCUBE_INPUT_POST, true);
@@ -625,23 +644,101 @@ class managesieve extends rcube_plugin
}
else {
foreach ($headers as $idx => $header) {
- $header = $this->strip_value($header);
- $target = $this->strip_value($targets[$idx], true);
- $op = $this->strip_value($ops[$idx]);
+ $header = $this->strip_value($header);
+ $target = $this->strip_value($targets[$idx], true);
+ $operator = $this->strip_value($ops[$idx]);
+ $comparator = $this->strip_value($comparators[$idx]);
+
+ if ($header == 'size') {
+ $sizeop = $this->strip_value($sizeops[$idx]);
+ $sizeitem = $this->strip_value($items[$idx]);
+ $sizetarget = $this->strip_value($sizetargets[$idx]);
+
+ $this->form['tests'][$i]['test'] = 'size';
+ $this->form['tests'][$i]['type'] = $sizeop;
+ $this->form['tests'][$i]['arg'] = $sizetarget;
+
+ if ($sizetarget == '')
+ $this->errors['tests'][$i]['sizetarget'] = $this->gettext('cannotbeempty');
+ else if (!preg_match('/^[0-9]+(K|M|G)?$/i', $sizetarget.$sizeitem, $m)) {
+ $this->errors['tests'][$i]['sizetarget'] = $this->gettext('forbiddenchars');
+ $this->form['tests'][$i]['item'] = $sizeitem;
+ }
+ else
+ $this->form['tests'][$i]['arg'] .= $m[1];
+ }
+ else if ($header == 'body') {
+ $trans = $this->strip_value($body_trans[$idx]);
+ $trans_type = $this->strip_value($body_types[$idx], true);
- // normal header
- if (in_array($header, $this->headers)) {
- if (preg_match('/^not/', $op))
+ if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
- $type = preg_replace('/^not/', '', $op);
+ $type = preg_replace('/^not/', '', $operator);
+
+ if ($type == 'exists') {
+ $this->errors['tests'][$i]['op'] = true;
+ }
+
+ $this->form['tests'][$i]['test'] = 'body';
+ $this->form['tests'][$i]['type'] = $type;
+ $this->form['tests'][$i]['arg'] = $target;
+
+ if ($target == '' && $type != 'exists')
+ $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
+ else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
+ $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
+
+ $this->form['tests'][$i]['part'] = $trans;
+ if ($trans == 'content') {
+ $this->form['tests'][$i]['content'] = $trans_type;
+ }
+ }
+ else {
+ $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
+ $mod = $this->strip_value($mods[$idx]);
+ $mod_type = $this->strip_value($mod_types[$idx]);
+
+ if (preg_match('/^not/', $operator))
+ $this->form['tests'][$i]['not'] = true;
+ $type = preg_replace('/^not/', '', $operator);
+
+ if ($header == '...') {
+ $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
+
+ if (!count($headers))
+ $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
+ else {
+ foreach ($headers as $hr)
+ if (!preg_match('/^[a-z0-9-]+$/i', $hr))
+ $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
+ }
+
+ if (empty($this->errors['tests'][$i]['header']))
+ $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
+ }
if ($type == 'exists') {
$this->form['tests'][$i]['test'] = 'exists';
- $this->form['tests'][$i]['arg'] = $header;
+ $this->form['tests'][$i]['arg'] = $header == '...' ? $cust_header : $header;
}
else {
+ $test = 'header';
+ $header = $header == '...' ? $cust_header : $header;
+
+ if ($mod == 'address' || $mod == 'envelope') {
+ $found = false;
+ if (empty($this->errors['tests'][$i]['header'])) {
+ foreach ((array)$header as $hdr) {
+ if (!in_array(strtolower(trim($hdr)), $this->addr_headers))
+ $found = true;
+ }
+ }
+ if (!$found)
+ $test = $mod;
+ }
+
$this->form['tests'][$i]['type'] = $type;
- $this->form['tests'][$i]['test'] = 'header';
+ $this->form['tests'][$i]['test'] = $test;
$this->form['tests'][$i]['arg1'] = $header;
$this->form['tests'][$i]['arg2'] = $target;
@@ -649,65 +746,20 @@ class managesieve extends rcube_plugin
$this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
$this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
+
+ if ($mod) {
+ $this->form['tests'][$i]['part'] = $mod_type;
+ }
}
}
- else
- switch ($header) {
- case 'size':
- $sizeop = $this->strip_value($sizeops[$idx]);
- $sizeitem = $this->strip_value($items[$idx]);
- $sizetarget = $this->strip_value($sizetargets[$idx]);
-
- $this->form['tests'][$i]['test'] = 'size';
- $this->form['tests'][$i]['type'] = $sizeop;
- $this->form['tests'][$i]['arg'] = $sizetarget.$sizeitem;
-
- if ($sizetarget == '')
- $this->errors['tests'][$i]['sizetarget'] = $this->gettext('cannotbeempty');
- else if (!preg_match('/^[0-9]+(K|M|G)*$/i', $sizetarget))
- $this->errors['tests'][$i]['sizetarget'] = $this->gettext('forbiddenchars');
- break;
- case '...':
- $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
-
- if (preg_match('/^not/', $op))
- $this->form['tests'][$i]['not'] = true;
- $type = preg_replace('/^not/', '', $op);
-
- if ($cust_header == '')
- $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
- else {
- $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
-
- if (!count($headers))
- $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
- else {
- foreach ($headers as $hr)
- if (!preg_match('/^[a-z0-9-]+$/i', $hr))
- $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
- }
- }
- if (empty($this->errors['tests'][$i]['header']))
- $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
+ if ($header != 'size' && $comparator) {
+ if (preg_match('/^(value|count)/', $this->form['tests'][$i]['type']))
+ $comparator = 'i;ascii-numeric';
+
+ $this->form['tests'][$i]['comparator'] = $comparator;
+ }
- if ($type == 'exists') {
- $this->form['tests'][$i]['test'] = 'exists';
- $this->form['tests'][$i]['arg'] = $cust_header;
- }
- else {
- $this->form['tests'][$i]['test'] = 'header';
- $this->form['tests'][$i]['type'] = $type;
- $this->form['tests'][$i]['arg1'] = $cust_header;
- $this->form['tests'][$i]['arg2'] = $target;
-
- if ($target == '')
- $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
- else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
- $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
- }
- break;
- }
$i++;
}
}
@@ -1140,43 +1192,52 @@ class managesieve extends rcube_plugin
$rule = isset($this->form) ? $this->form['tests'][$id] : $this->script[$fid]['tests'][$id];
$rows_num = isset($this->form) ? sizeof($this->form['tests']) : sizeof($this->script[$fid]['tests']);
- $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
-
- $out .= '<table><tr><td class="rowactions">';
-
// headers select
$select_header = new html_select(array('name' => "_header[]", 'id' => 'header'.$id,
'onchange' => 'rule_header_select(' .$id .')'));
foreach($this->headers as $name => $val)
$select_header->add(Q($this->gettext($name)), Q($val));
+ if (in_array('body', $this->exts))
+ $select_header->add(Q($this->gettext('body')), 'body');
$select_header->add(Q($this->gettext('size')), 'size');
$select_header->add(Q($this->gettext('...')), '...');
// TODO: list arguments
+ $aout = '';
- if ((isset($rule['test']) && $rule['test'] == 'header')
- && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers))
- $out .= $select_header->show($rule['arg1']);
+ if ((isset($rule['test']) && in_array($rule['test'], array('header', 'address', 'envelope')))
+ && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers)
+ ) {
+ $aout .= $select_header->show($rule['arg1']);
+ }
else if ((isset($rule['test']) && $rule['test'] == 'exists')
- && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers))
- $out .= $select_header->show($rule['arg']);
+ && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers)
+ ) {
+ $aout .= $select_header->show($rule['arg']);
+ }
else if (isset($rule['test']) && $rule['test'] == 'size')
- $out .= $select_header->show('size');
+ $aout .= $select_header->show('size');
+ else if (isset($rule['test']) && $rule['test'] == 'body')
+ $aout .= $select_header->show('body');
else if (isset($rule['test']) && $rule['test'] != 'true')
- $out .= $select_header->show('...');
+ $aout .= $select_header->show('...');
else
- $out .= $select_header->show();
+ $aout .= $select_header->show();
- $out .= '</td><td class="rowtargets">';
-
- if ((isset($rule['test']) && $rule['test'] == 'header')
- && (is_array($rule['arg1']) || !in_array($rule['arg1'], $this->headers)))
- $custom = is_array($rule['arg1']) ? implode(', ', $rule['arg1']) : $rule['arg1'];
- else if ((isset($rule['test']) && $rule['test'] == 'exists')
- && (is_array($rule['arg']) || !in_array($rule['arg'], $this->headers)))
- $custom = is_array($rule['arg']) ? implode(', ', $rule['arg']) : $rule['arg'];
+ if (isset($rule['test']) && in_array($rule['test'], array('header', 'address', 'envelope'))) {
+ if (is_array($rule['arg1']))
+ $custom = implode(', ', $rule['arg1']);
+ else if (!in_array($rule['arg1'], $this->headers))
+ $custom = $rule['arg1'];
+ }
+ else if (isset($rule['test']) && $rule['test'] == 'exists') {
+ if (is_array($rule['arg']))
+ $custom = implode(', ', $rule['arg']);
+ else if (!in_array($rule['arg'], $this->headers))
+ $custom = $rule['arg'];
+ }
- $out .= '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
+ $tout = '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
<input type="text" name="_custom_header[]" id="custom_header_i'.$id.'" '
. $this->error_class($id, 'test', 'header', 'custom_header_i')
.' value="' .Q($custom). '" size="15" />&nbsp;</div>' . "\n";
@@ -1215,33 +1276,43 @@ class managesieve extends rcube_plugin
// target input (TODO: lists)
- if ($rule['test'] == 'header') {
- $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['type']);
+ if (in_array($rule['test'], array('header', 'address', 'envelope'))) {
+ $test = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
$target = $rule['arg2'];
}
+ else if ($rule['test'] == 'body') {
+ $test = ($rule['not'] ? 'not' : '').($rule['type'] ? $rule['type'] : 'is');
+ $target = $rule['arg'];
+ }
else if ($rule['test'] == 'size') {
- $out .= $select_op->show();
- if (preg_match('/^([0-9]+)(K|M|G)*$/', $rule['arg'], $matches)) {
+ $test = '';
+ $target = '';
+ if (preg_match('/^([0-9]+)(K|M|G)?$/', $rule['arg'], $matches)) {
$sizetarget = $matches[1];
$sizeitem = $matches[2];
}
+ else {
+ $sizetarget = $rule['arg'];
+ $sizeitem = $rule['item'];
+ }
}
else {
- $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['test']);
- $target = '';
+ $test = ($rule['not'] ? 'not' : '').$rule['test'];
+ $target = '';
}
- $out .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
+ $tout .= $select_op->show($test);
+ $tout .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
value="' .Q($target). '" size="20" ' . $this->error_class($id, 'test', 'target', 'rule_target')
. ' style="display:' . ($rule['test']!='size' && $rule['test'] != 'exists' ? 'inline' : 'none') . '" />'."\n";
$select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id));
- $select_size_op->add(Q($this->gettext('filterunder')), 'under');
$select_size_op->add(Q($this->gettext('filterover')), 'over');
+ $select_size_op->add(Q($this->gettext('filterunder')), 'under');
- $out .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
- $out .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
- $out .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" '
+ $tout .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
+ $tout .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
+ $tout .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" '
. $this->error_class($id, 'test', 'sizetarget', 'rule_size_i') .' />
<input type="radio" name="_rule_size_item['.$id.']" value=""'
. (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('B').'
@@ -1251,7 +1322,82 @@ class managesieve extends rcube_plugin
. ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('MB').'
<input type="radio" name="_rule_size_item['.$id.']" value="G"'
. ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('GB');
- $out .= '</div>';
+ $tout .= '</div>';
+
+ // Advanced modifiers (address, envelope)
+ $select_mod = new html_select(array('name' => "_rule_mod[]", 'id' => 'rule_mod_op'.$id,
+ 'onchange' => 'rule_mod_select(' .$id .')'));
+ $select_mod->add(Q($this->gettext('none')), '');
+ $select_mod->add(Q($this->gettext('address')), 'address');
+ if (in_array('envelope', $this->exts))
+ $select_mod->add(Q($this->gettext('envelope')), 'envelope');
+
+ $select_type = new html_select(array('name' => "_rule_mod_type[]", 'id' => 'rule_mod_type'.$id));
+ $select_type->add(Q($this->gettext('allparts')), 'all');
+ $select_type->add(Q($this->gettext('domain')), 'domain');
+ $select_type->add(Q($this->gettext('localpart')), 'localpart');
+ if (in_array('subaddress', $this->exts)) {
+ $select_type->add(Q($this->gettext('user')), 'user');
+ $select_type->add(Q($this->gettext('detail')), 'detail');
+ }
+
+ $need_mod = $rule['test'] != 'size' && $rule['test'] != 'body';
+ $mout = '<div id="rule_mod' .$id. '" class="adv" style="display:' . ($need_mod ? 'block' : 'none') .'">';
+ $mout .= ' <span>';
+ $mout .= Q($this->gettext('modifier')) . ' ';
+ $mout .= $select_mod->show($rule['test']);
+ $mout .= '</span>';
+ $mout .= ' <span id="rule_mod_type' . $id . '"';
+ $mout .= ' style="display:' . (in_array($rule['test'], array('address', 'envelope')) ? 'inline' : 'none') .'">';
+ $mout .= Q($this->gettext('modtype')) . ' ';
+ $mout .= $select_type->show($rule['part']);
+ $mout .= '</span>';
+ $mout .= '</div>';
+
+ // Advanced modifiers (body transformations)
+ $select_mod = new html_select(array('name' => "_rule_trans[]", 'id' => 'rule_trans_op'.$id,
+ 'onchange' => 'rule_trans_select(' .$id .')'));
+ $select_mod->add(Q($this->gettext('text')), 'text');
+ $select_mod->add(Q($this->gettext('undecoded')), 'raw');
+ $select_mod->add(Q($this->gettext('contenttype')), 'content');
+
+ $mout .= '<div id="rule_trans' .$id. '" class="adv" style="display:' . ($rule['test'] == 'body' ? 'block' : 'none') .'">';
+ $mout .= ' <span>';
+ $mout .= Q($this->gettext('modifier')) . ' ';
+ $mout .= $select_mod->show($rule['part']);
+ $mout .= '<input type="text" name="_rule_trans_type[]" id="rule_trans_type'.$id
+ . '" value="'.(is_array($rule['content']) ? implode(',', $rule['content']) : $rule['content'])
+ .'" size="20" style="display:' . ($rule['part'] == 'content' ? 'inline' : 'none') .'"'
+ . $this->error_class($id, 'test', 'part', 'rule_trans_type') .' />';
+ $mout .= '</span>';
+ $mout .= '</div>';
+
+ // Advanced modifiers (body transformations)
+ $select_comp = new html_select(array('name' => "_rule_comp[]", 'id' => 'rule_comp_op'.$id));
+ $select_comp->add(Q($this->gettext('default')), '');
+ $select_comp->add(Q($this->gettext('octet')), 'i;octet');
+ $select_comp->add(Q($this->gettext('asciicasemap')), 'i;ascii-casemap');
+ if (in_array('comparator-i;ascii-numeric', $this->exts)) {
+ $select_comp->add(Q($this->gettext('asciinumeric')), 'i;ascii-numeric');
+ }
+
+ $mout .= '<div id="rule_comp' .$id. '" class="adv" style="display:' . ($rule['test'] != 'size' ? 'block' : 'none') .'">';
+ $mout .= ' <span>';
+ $mout .= Q($this->gettext('comparator')) . ' ';
+ $mout .= $select_comp->show($rule['comparator']);
+ $mout .= '</span>';
+ $mout .= '</div>';
+
+ // Build output table
+ $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
+ $out .= '<table><tr>';
+ $out .= '<td class="advbutton">';
+ $out .= '<a href="#" id="ruleadv' . $id .'" title="'. Q($this->gettext('advancedopts')). '"
+ onclick="rule_adv_switch(' . $id .', this)" class="show">&nbsp;&nbsp;</a>';
+ $out .= '</td>';
+ $out .= '<td class="rowactions">' . $aout . '</td>';
+ $out .= '<td class="rowtargets">' . $tout . "\n";
+ $out .= '<div id="rule_advanced' .$id. '" style="display:none">' . $mout . '</div>';
$out .= '</td>';
// add/del buttons
@@ -1260,7 +1406,8 @@ class managesieve extends rcube_plugin
onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button add"></a>';
$out .= '<a href="#" id="ruledel' . $id .'" title="'. Q($this->gettext('del')). '"
onclick="rcmail.managesieve_ruledel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
- $out .= '</td></tr></table>';
+ $out .= '</td>';
+ $out .= '</tr></table>';
$out .= $div ? "</div>\n" : '';
diff --git a/plugins/managesieve/package.xml b/plugins/managesieve/package.xml
new file mode 100644
index 000000000..56655d248
--- /dev/null
+++ b/plugins/managesieve/package.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
+ http://pear.php.net/dtd/tasks-1.0.xsd
+ http://pear.php.net/dtd/package-2.0
+ http://pear.php.net/dtd/package-2.0.xsd">
+ <name>managesieve</name>
+ <channel>pear.roundcube.net</channel>
+ <summary>Sieve filters manager for Roundcube</summary>
+ <description>
+ Adds a possibility to manage Sieve scripts (incoming mail filters).
+ It's clickable interface which operates on text scripts and communicates
+ with server using managesieve protocol. Adds Filters tab in Settings.
+ </description>
+ <lead>
+ <name>Aleksander Machniak</name>
+ <user>alec</user>
+ <email>alec@alec.pl</email>
+ <active>yes</active>
+ </lead>
+ <date>2011-11-17</date>
+ <version>
+ <release>5.0</release>
+ <api>5.0</api>
+ </version>
+ <stability>
+ <release>stable</release>
+ <api>stable</api>
+ </stability>
+ <license uri="http://www.gnu.org/licenses/gpl-2.0.html">GNU GPLv2</license>
+ <notes>-</notes>
+ <contents>
+ <dir baseinstalldir="/" name="/">
+ <file name="managesieve.php" role="php">
+ <tasks:replace from="@name@" to="name" type="package-info"/>
+ <tasks:replace from="@package_version@" to="version" type="package-info"/>
+ </file>
+ <file name="managesieve.js" role="data">
+ <tasks:replace from="@name@" to="name" type="package-info"/>
+ <tasks:replace from="@package_version@" to="version" type="package-info"/>
+ </file>
+ <file name="localization/bg_BG.inc" role="data"></file>
+ <file name="localization/cs_CZ.inc" role="data"></file>
+ <file name="localization/de_CH.inc" role="data"></file>
+ <file name="localization/de_DE.inc" role="data"></file>
+ <file name="localization/el_GR.inc" role="data"></file>
+ <file name="localization/en_GB.inc" role="data"></file>
+ <file name="localization/en_US.inc" role="data"></file>
+ <file name="localization/es_AR.inc" role="data"></file>
+ <file name="localization/es_ES.inc" role="data"></file>
+ <file name="localization/et_EE.inc" role="data"></file>
+ <file name="localization/fi_FI.inc" role="data"></file>
+ <file name="localization/fr_FR.inc" role="data"></file>
+ <file name="localization/gl_ES.inc" role="data"></file>
+ <file name="localization/hr_HR.inc" role="data"></file>
+ <file name="localization/hu_HU.inc" role="data"></file>
+ <file name="localization/it_IT.inc" role="data"></file>
+ <file name="localization/ja_JP.inc" role="data"></file>
+ <file name="localization/lv_LV.inc" role="data"></file>
+ <file name="localization/nb_NO.inc" role="data"></file>
+ <file name="localization/nl_NL.inc" role="data"></file>
+ <file name="localization/pl_PL.inc" role="data"></file>
+ <file name="localization/pt_BR.inc" role="data"></file>
+ <file name="localization/pt_PT.inc" role="data"></file>
+ <file name="localization/ru_RU.inc" role="data"></file>
+ <file name="localization/sk_SK.inc" role="data"></file>
+ <file name="localization/sl_SI.inc" role="data"></file>
+ <file name="localization/sv_SE.inc" role="data"></file>
+ <file name="localization/uk_UA.inc" role="data"></file>
+ <file name="localization/zh_CN.inc" role="data"></file>
+ <file name="localization/zh_TW.inc" role="data"></file>
+ <file name="skins/default/managesieve.css" role="data"></file>
+ <file name="skins/default/managesieve_mail.css" role="data"></file>
+ <file name="skins/default/templates/filteredit.html" role="data"></file>
+ <file name="skins/default/templates/managesieve.html" role="data"></file>
+ <file name="skins/default/templates/setedit.html" role="data"></file>
+ <file name="skins/default/images/add.png" role="data"></file>
+ <file name="skins/default/images/del.png" role="data"></file>
+ <file name="skins/default/images/down_small.gif" role="data"></file>
+ <file name="skins/default/images/filter.png" role="data"></file>
+ <file name="skins/default/images/up_small.gif" role="data"></file>
+ <file name="managesieve.php" role="php"></file>
+ <file name="lib/rcube_sieve.php" role="php"></file>
+ <file name="lib/rcube_sieve_script.php" role="php"></file>
+ <file name="lib/Net/Sieve.php" role="php"></file>
+ <file name="config.inc.php.dist" role="data"></file>
+ </dir>
+ <!-- / -->
+ </contents>
+ <dependencies>
+ <required>
+ <php>
+ <min>5.2.1</min>
+ </php>
+ <pearinstaller>
+ <min>1.7.0</min>
+ </pearinstaller>
+ </required>
+ </dependencies>
+ <phprelease/>
+</package>
diff --git a/plugins/managesieve/skins/default/images/down_small.gif b/plugins/managesieve/skins/default/images/down_small.gif
new file mode 100644
index 000000000..f865893f4
--- /dev/null
+++ b/plugins/managesieve/skins/default/images/down_small.gif
Binary files differ
diff --git a/plugins/managesieve/skins/default/images/toolbar.png b/plugins/managesieve/skins/default/images/toolbar.png
deleted file mode 100644
index 473dbc8df..000000000
--- a/plugins/managesieve/skins/default/images/toolbar.png
+++ /dev/null
Binary files differ
diff --git a/plugins/managesieve/skins/default/images/up_small.gif b/plugins/managesieve/skins/default/images/up_small.gif
new file mode 100644
index 000000000..40deb891f
--- /dev/null
+++ b/plugins/managesieve/skins/default/images/up_small.gif
Binary files differ
diff --git a/plugins/managesieve/skins/default/managesieve.css b/plugins/managesieve/skins/default/managesieve.css
index cef26d4f3..0b82be6f3 100644
--- a/plugins/managesieve/skins/default/managesieve.css
+++ b/plugins/managesieve/skins/default/managesieve.css
@@ -149,6 +149,35 @@ div.rulerow table, div.actionrow table
min-width: 620px;
}
+td
+{
+ vertical-align: top;
+}
+
+td.advbutton
+{
+ width: 1%;
+}
+
+td.advbutton a
+{
+ display: block;
+ padding-top: 14px;
+ height: 6px;
+ width: 12px;
+ text-decoration: none;
+}
+
+td.advbutton a.show
+{
+ background: url(images/down_small.gif) center no-repeat;
+}
+
+td.advbutton a.hide
+{
+ background: url(images/up_small.gif) center no-repeat;
+}
+
td.rowbuttons
{
text-align: right;
@@ -169,6 +198,11 @@ td.rowtargets
padding-left: 3px;
}
+td.rowtargets div.adv
+{
+ padding-top: 3px;
+}
+
input.disabled, input.disabled:hover
{
color: #999999;
@@ -183,6 +217,7 @@ input.box,
input.radio
{
border: 0;
+ margin-top: 0;
}
select.operator_selector
@@ -190,6 +225,7 @@ select.operator_selector
width: 200px;
}
+td.rowtargets span,
span.label
{
color: #666666;
@@ -243,7 +279,7 @@ a.button.add
background: url(images/add.png) no-repeat;
width: 30px;
height: 20px;
- margin-right: 4px;
+ margin-right: 4px;
display: inline-block;
}
diff --git a/plugins/managesieve/tests/parser.phpt b/plugins/managesieve/tests/parser.phpt
index d70353459..aec042187 100644
--- a/plugins/managesieve/tests/parser.phpt
+++ b/plugins/managesieve/tests/parser.phpt
@@ -6,7 +6,7 @@ Main test of script parser
include '../lib/rcube_sieve_script.php';
$txt = '
-require ["fileinto","vacation","reject","relational","comparator-i;ascii-numeric","imapflags"];
+require ["fileinto","reject","envelope"];
# rule:[spam]
if anyof (header :contains "X-DSPAM-Result" "Spam")
{
@@ -14,28 +14,17 @@ if anyof (header :contains "X-DSPAM-Result" "Spam")
stop;
}
# rule:[test1]
-if anyof (header :contains ["From","To"] "test@domain.tld")
+if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld")
{
discard;
stop;
}
# rule:[test2]
-if anyof (not header :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
+if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
{
fileinto "test";
stop;
}
-# rule:[test-vacation]
-if anyof (header :contains "Subject" "vacation")
-{
- vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
- stop;
-}
# rule:[comments]
if anyof (true) /* comment
* "comment" #comment */ {
@@ -44,24 +33,40 @@ if anyof (true) /* comment
}
# rule:[reject]
if size :over 5000K {
- reject "Message over 5MB size limit. Please contact me before sending this.";
+ reject "Message over 5MB size limit. Please contact me before sending this.";
+}
+# rule:[false]
+if false # size :over 5000K
+{
+ stop; /* rule disabled */
+}
+# rule:[true]
+if true
+{
+ stop;
}
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric"
- ["X-Spam-score"] ["14"] {redirect "test@test.tld";}
-# rule:[imapflags]
-if header :matches "Subject" "^Test$" {
- setflag "\\\\Seen";
- addflag ["\\\\Answered","\\\\Deleted"];
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+ fileinto "domain.tld";
+ stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+ fileinto "domain.tld";
+ stop;
}
';
$s = new rcube_sieve_script($txt);
echo $s->as_text();
+// -------------------------------------------------------------------------------
?>
--EXPECT--
-require ["fileinto","vacation","reject","relational","comparator-i;ascii-numeric","imapflags"];
+require ["fileinto","reject","envelope"];
# rule:[spam]
if header :contains "X-DSPAM-Result" "Spam"
{
@@ -75,22 +80,11 @@ if header :contains ["From","To"] "test@domain.tld"
stop;
}
# rule:[test2]
-if anyof (not header :contains "Subject" "[test]", header :contains "Subject" "[test2]")
+if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]")
{
fileinto "test";
stop;
}
-# rule:[test-vacation]
-if header :contains "Subject" "vacation"
-{
- vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
- stop;
-}
# rule:[comments]
if true
{
@@ -101,14 +95,26 @@ if size :over 5000K
{
reject "Message over 5MB size limit. Please contact me before sending this.";
}
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
+# rule:[false]
+if false # size :over 5000K
{
- redirect "test@test.tld";
+ stop;
}
-# rule:[imapflags]
-if header :matches "Subject" "^Test$"
+# rule:[true]
+if true
{
- setflag "\\Seen";
- addflag ["\\Answered","\\Deleted"];
+ stop;
+}
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+ fileinto "domain.tld";
+ stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+ fileinto "domain.tld";
+ stop;
}
diff --git a/plugins/managesieve/tests/parser_body.phpt b/plugins/managesieve/tests/parser_body.phpt
new file mode 100644
index 000000000..08ad54959
--- /dev/null
+++ b/plugins/managesieve/tests/parser_body.phpt
@@ -0,0 +1,49 @@
+--TEST--
+Test of Sieve body extension (RFC5173)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["body","fileinto"];
+if body :raw :contains "MAKE MONEY FAST"
+{
+ stop;
+}
+if body :content "text" :contains ["missile","coordinates"]
+{
+ fileinto "secrets";
+}
+if body :content "audio/mp3" :contains ""
+{
+ fileinto "jukebox";
+}
+if body :text :contains "project schedule"
+{
+ fileinto "project/schedule";
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["body","fileinto"];
+if body :raw :contains "MAKE MONEY FAST"
+{
+ stop;
+}
+if body :content "text" :contains ["missile","coordinates"]
+{
+ fileinto "secrets";
+}
+if body :content "audio/mp3" :contains ""
+{
+ fileinto "jukebox";
+}
+if body :text :contains "project schedule"
+{
+ fileinto "project/schedule";
+}
diff --git a/plugins/managesieve/tests/parser_imapflags.phpt b/plugins/managesieve/tests/parser_imapflags.phpt
new file mode 100644
index 000000000..a4bc465a3
--- /dev/null
+++ b/plugins/managesieve/tests/parser_imapflags.phpt
@@ -0,0 +1,28 @@
+--TEST--
+Test of Sieve vacation extension (RFC5232)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["imapflags"];
+# rule:[imapflags]
+if header :matches "Subject" "^Test$" {
+ setflag "\\\\Seen";
+ addflag ["\\\\Answered","\\\\Deleted"];
+}
+';
+
+$s = new rcube_sieve_script($txt, array('imapflags'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["imapflags"];
+# rule:[imapflags]
+if header :matches "Subject" "^Test$"
+{
+ setflag "\\Seen";
+ addflag ["\\Answered","\\Deleted"];
+}
diff --git a/plugins/managesieve/tests/parser_include.phpt b/plugins/managesieve/tests/parser_include.phpt
new file mode 100644
index 000000000..addc0d449
--- /dev/null
+++ b/plugins/managesieve/tests/parser_include.phpt
@@ -0,0 +1,30 @@
+--TEST--
+Test of Sieve include extension
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["include"];
+
+include "script.sieve";
+# rule:[two]
+if true
+{
+ include :optional "second.sieve";
+}
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["include"];
+include "script.sieve";
+# rule:[two]
+if true
+{
+ include :optional "second.sieve";
+}
diff --git a/plugins/managesieve/tests/parser_kep14.phpt b/plugins/managesieve/tests/parser_kep14.phpt
index 06beaeda1..dcdbd48a0 100644
--- a/plugins/managesieve/tests/parser_kep14.phpt
+++ b/plugins/managesieve/tests/parser_kep14.phpt
@@ -10,7 +10,7 @@ $txt = '
# EDITOR_VERSION 123
';
-$s = new rcube_sieve_script($txt, array());
+$s = new rcube_sieve_script($txt, array('body'));
echo $s->as_text();
?>
diff --git a/plugins/managesieve/tests/parser_prefix.phpt b/plugins/managesieve/tests/parser_prefix.phpt
new file mode 100644
index 000000000..c87e9658f
--- /dev/null
+++ b/plugins/managesieve/tests/parser_prefix.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Test of prefix comments handling
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+# this is a comment
+# and the second line
+
+require ["variables"];
+set "b" "c";
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+# this is a comment
+# and the second line
+
+require ["variables"];
+set "b" "c";
diff --git a/plugins/managesieve/tests/parser_relational.phpt b/plugins/managesieve/tests/parser_relational.phpt
new file mode 100644
index 000000000..6b6f29f4c
--- /dev/null
+++ b/plugins/managesieve/tests/parser_relational.phpt
@@ -0,0 +1,25 @@
+--TEST--
+Test of Sieve relational extension (RFC5231)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["relational","comparator-i;ascii-numeric"];
+# rule:[redirect]
+if header :value "ge" :comparator "i;ascii-numeric"
+ ["X-Spam-score"] ["14"] {redirect "test@test.tld";}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["relational","comparator-i;ascii-numeric"];
+# rule:[redirect]
+if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
+{
+ redirect "test@test.tld";
+}
diff --git a/plugins/managesieve/tests/parser_vacation.phpt b/plugins/managesieve/tests/parser_vacation.phpt
new file mode 100644
index 000000000..a603ff6c1
--- /dev/null
+++ b/plugins/managesieve/tests/parser_vacation.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Test of Sieve vacation extension (RFC5230)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["vacation"];
+# rule:[test-vacation]
+if anyof (header :contains "Subject" "vacation")
+{
+ vacation :days 1 text:
+# test
+test test /* test */
+test
+.
+;
+ stop;
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["vacation"];
+# rule:[test-vacation]
+if header :contains "Subject" "vacation"
+{
+ vacation :days 1 text:
+# test
+test test /* test */
+test
+.
+;
+ stop;
+}
diff --git a/plugins/managesieve/tests/parser_variables.phpt b/plugins/managesieve/tests/parser_variables.phpt
new file mode 100644
index 000000000..cf1f8fcad
--- /dev/null
+++ b/plugins/managesieve/tests/parser_variables.phpt
@@ -0,0 +1,39 @@
+--TEST--
+Test of Sieve variables extension
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["variables"];
+set "honorific" "Mr";
+set "vacation" text:
+Dear ${HONORIFIC} ${last_name},
+I am out, please leave a message after the meep.
+.
+;
+set :length "b" "${a}";
+set :lower "b" "${a}";
+set :upperfirst "b" "${a}";
+set :upperfirst :lower "b" "${a}";
+set :quotewildcard "b" "Rock*";
+';
+
+$s = new rcube_sieve_script($txt, array(), array('variables'));
+echo $s->as_text();
+
+?>
+--EXPECT--
+require ["variables"];
+set "honorific" "Mr";
+set "vacation" text:
+Dear ${HONORIFIC} ${last_name},
+I am out, please leave a message after the meep.
+.
+;
+set :length "b" "${a}";
+set :lower "b" "${a}";
+set :upperfirst "b" "${a}";
+set :upperfirst :lower "b" "${a}";
+set :quotewildcard "b" "Rock*";
diff --git a/plugins/managesieve/tests/parset_subaddress.phpt b/plugins/managesieve/tests/parset_subaddress.phpt
new file mode 100644
index 000000000..6d4d03c6e
--- /dev/null
+++ b/plugins/managesieve/tests/parset_subaddress.phpt
@@ -0,0 +1,38 @@
+--TEST--
+Test of Sieve subaddress extension (RFC5233)
+--SKIPIF--
+--FILE--
+<?php
+include '../lib/rcube_sieve_script.php';
+
+$txt = '
+require ["envelope","subaddress","fileinto"];
+if envelope :user "To" "postmaster"
+{
+ fileinto "postmaster";
+ stop;
+}
+if envelope :detail :is "To" "mta-filters"
+{
+ fileinto "mta-filters";
+ stop;
+}
+';
+
+$s = new rcube_sieve_script($txt);
+echo $s->as_text();
+
+// -------------------------------------------------------------------------------
+?>
+--EXPECT--
+require ["envelope","subaddress","fileinto"];
+if envelope :user "To" "postmaster"
+{
+ fileinto "postmaster";
+ stop;
+}
+if envelope :detail :is "To" "mta-filters"
+{
+ fileinto "mta-filters";
+ stop;
+}
diff --git a/plugins/newmail_notifier/newmail_notifier.php b/plugins/newmail_notifier/newmail_notifier.php
index 1f1df9e75..01e25984d 100644
--- a/plugins/newmail_notifier/newmail_notifier.php
+++ b/plugins/newmail_notifier/newmail_notifier.php
@@ -132,7 +132,7 @@ class newmail_notifier extends rcube_plugin
*/
function notify($args)
{
- if ($this->notified) {
+ if ($this->notified || !empty($_GET['_refresh'])) {
return $args;
}
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 002730f4b..a0a5f8189 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -3178,7 +3178,7 @@ class rcube_imap
if (is_array($ns)) {
foreach ($ns as $ns_data) {
if (strlen($ns_data[0])) {
- $search = $ns_data[0];
+ $search[] = $ns_data[0];
}
}
}
diff --git a/program/js/app.js b/program/js/app.js
index 5902b1d74..8b98a4268 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -5738,7 +5738,7 @@ function rcube_webmail()
this.plain2html = function(plainText, id)
{
var lock = this.set_busy(true, 'converting');
- $(document.getElementById(id)).val('<pre>'+plainText+'</pre>');
+ $(document.getElementById(id)).val(plainText ? '<pre>'+plainText+'</pre>' : '');
this.set_busy(false, null, lock);
};
diff --git a/program/js/googiespell.js b/program/js/googiespell.js
index e3fdf7d3b..96d612ca2 100644
--- a/program/js/googiespell.js
+++ b/program/js/googiespell.js
@@ -1,6 +1,8 @@
/*
SpellCheck
jQuery'fied spell checker based on GoogieSpell 4.0
+ (which was published under GPL "version 2 or any later version")
+
Copyright (C) 2006 Amir Salihefendic
Copyright (C) 2009 Aleksander Machniak
Copyright (C) 2011 Kolab Systems AG