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) 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] ----------------------------------------------------------- plugins/managesieve/lib/rcube_sieve.php
@@ -44,7 +44,6 @@ 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 @@ } $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 @@ 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)) { plugins/managesieve/lib/rcube_sieve_script.php
@@ -29,9 +29,9 @@ 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,29 +42,29 @@ '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 @@ $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,33 +222,95 @@ $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"'; } else { if ($test['type'] == 'regex') { array_push($exts, 'regex'); $tests[$i] .= ' :' . $m[1] . ' "' . $m[2] . '" :comparator "i;ascii-numeric"'; } else { $this->add_comparator($test, $tests[$i], $exts); $tests[$i] .= 'header :' . $test['type']; if ($test['type'] == 'regex') { array_push($exts, 'regex'); } $tests[$i] .= ' :' . $test['type']; } } $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] .= ' :' . $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 @@ 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 @@ // 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 @@ $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]; @@ -570,6 +632,52 @@ else { $header['arg1'] = $header['arg2']; $header['arg2'] = $tokens[$i]; } } $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]; } } @@ -597,9 +705,7 @@ } // ...and actions block if ($tests) { $actions = $this->_parse_actions($content); } $actions = $this->_parse_actions($content); if ($tests && $actions) { $result = array( @@ -747,6 +853,29 @@ } /** * */ 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 * plugins/managesieve/localization/en_US.inc
@@ -82,6 +82,25 @@ $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.'; plugins/managesieve/localization/pl_PL.inc
@@ -81,6 +81,25 @@ $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.'; plugins/managesieve/managesieve.js
@@ -542,19 +542,28 @@ 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 @@ 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), plugins/managesieve/managesieve.php
@@ -45,9 +45,23 @@ 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 @@ $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 @@ } 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]); // normal header if (in_array($header, $this->headers)) { if (preg_match('/^not/', $op)) 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); 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 @@ $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 ($header != 'size' && $comparator) { if (preg_match('/^(value|count)/', $this->form['tests'][$i]['type'])) $comparator = 'i;ascii-numeric'; 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]); $this->form['tests'][$i]['comparator'] = $comparator; } 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 ($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 @@ $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']) && 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']; } 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']; $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" /> </div>' . "\n"; @@ -1215,33 +1276,43 @@ // 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 @@ . ($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"> </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 @@ 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" : ''; plugins/managesieve/package.xml
New file @@ -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> plugins/managesieve/skins/default/images/down_small.gif
plugins/managesieve/skins/default/images/toolbar.pngBinary files differ
plugins/managesieve/skins/default/images/up_small.gif
plugins/managesieve/skins/default/managesieve.css
@@ -149,6 +149,35 @@ 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 @@ padding-left: 3px; } td.rowtargets div.adv { padding-top: 3px; } input.disabled, input.disabled:hover { color: #999999; @@ -183,6 +217,7 @@ input.radio { border: 0; margin-top: 0; } select.operator_selector @@ -190,6 +225,7 @@ width: 200px; } td.rowtargets span, span.label { color: #666666; @@ -243,7 +279,7 @@ background: url(images/add.png) no-repeat; width: 30px; height: 20px; margin-right: 4px; margin-right: 4px; display: inline-block; } plugins/managesieve/tests/parser.phpt
@@ -6,7 +6,7 @@ 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,26 +14,15 @@ 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] @@ -44,24 +33,40 @@ } # 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:[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"]; # rule:[false] if false # size :over 5000K { stop; /* rule disabled */ } # rule:[true] if true { 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; } '; $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,20 +80,9 @@ 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] @@ -101,14 +95,26 @@ { 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; } plugins/managesieve/tests/parser_body.phpt
New file @@ -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"; } plugins/managesieve/tests/parser_imapflags.phpt
New file @@ -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"]; } plugins/managesieve/tests/parser_include.phpt
New file @@ -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"; } plugins/managesieve/tests/parser_kep14.phpt
@@ -10,7 +10,7 @@ # EDITOR_VERSION 123 '; $s = new rcube_sieve_script($txt, array()); $s = new rcube_sieve_script($txt, array('body')); echo $s->as_text(); ?> plugins/managesieve/tests/parser_prefix.phpt
New file @@ -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"; plugins/managesieve/tests/parser_relational.phpt
New file @@ -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"; } plugins/managesieve/tests/parser_vacation.phpt
New file @@ -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; } plugins/managesieve/tests/parser_variables.phpt
New file @@ -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*"; plugins/managesieve/tests/parset_subaddress.phpt
New file @@ -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; } plugins/newmail_notifier/newmail_notifier.php
@@ -132,7 +132,7 @@ */ function notify($args) { if ($this->notified) { if ($this->notified || !empty($_GET['_refresh'])) { return $args; } program/include/rcube_imap.php
@@ -3178,7 +3178,7 @@ if (is_array($ns)) { foreach ($ns as $ns_data) { if (strlen($ns_data[0])) { $search = $ns_data[0]; $search[] = $ns_data[0]; } } } program/js/app.js
@@ -5738,7 +5738,7 @@ 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); }; 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