Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F223802
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
177 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/managesieve/Changelog b/plugins/managesieve/Changelog
index 5f31d311c..32d87a0d8 100644
--- a/plugins/managesieve/Changelog
+++ b/plugins/managesieve/Changelog
@@ -1,269 +1,271 @@
+- Add vacation-seconds extension support (RFC 6131)
+
* version 6.2 [2013-02-17]
-----------------------------------------------------------
- Support tls:// prefix in managesieve_host option
- Removed depracated functions usage
- Don't trim whitespace in folder names (#1488955)
* version 6.1 [2012-12-21]
-----------------------------------------------------------
- Fixed filter activation/deactivation confirmation message (#1488765)
- Moved rcube_* classes to <plugin>/lib/Roundcube for compat. with Roundcube Framework autoloader
- Fixed filter selection after filter deletion (#1488832)
- Fixed compatibility with jQueryUI-1.9
- Don't force 'stop' action on last rule in a script
* version 6.0 [2012-10-03]
-----------------------------------------------------------
- Fixed issue with DBMail bug [http://pear.php.net/bugs/bug.php?id=19077] (#1488594)
- Added support for enotify/notify (RFC5435, RFC5436, draft-ietf-sieve-notify-00)
- Change default port to 4190 (IANA-allocated), add port auto-detection (#1488713)
- Added request size limits detection and script corruption prevention (#1488648)
- Fix so scripts listed in managesieve_filename_exceptions aren't displayed on the list (#1488724)
* version 5.2 [2012-07-24]
-----------------------------------------------------------
- Added GUI for variables setting - RFC5229 (patch from Paweł Słowik)
- Fixed scrollbars in Larry's iframes
- Fix performance issue in message_headers_output hook handling
* version 5.1 [2012-06-21]
-----------------------------------------------------------
- Fixed filter popup width (for non-english localizations)
- Fixed tokenizer infinite loop on invalid script content
- Larry skin support
- Fixed custom header name validity check, made RFC2822-compliant
* version 5.0 [2012-01-05]
-----------------------------------------------------------
- Fixed setting test type to :is when none is specified
- Fixed javascript error in IE8
- Fixed possible ID duplication when adding filter rules very fast (#1488288)
- Fixed bug where drag layer wasn't removed when dragging was ended over sets list
* 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]
-----------------------------------------------------------
- Added possibility to create a filter based on selected message "in-place"
- Fixed import from Horde-INGO (#1488064)
- Add managesieve_script_name option for default name of the script (#1487956)
- Fixed handling of enabled magic_quotes_gpc setting
- Fixed PHP warning on connection error when submitting filter form
- Fixed bug where new action row with flags wasn't handled properly
- Added managesieve_connect hook for plugins
- Fixed doubled Filter tab on page refresh
- Added filters set selector in filter form when invoked in mail task
- Improved script parser, added support for include and variables extensions
- Added Kolab's KEP:14 support (http://wiki.kolab.org/User:Greve/Drafts/KEP:14)
- Use smaller action/rule buttons
- UI redesign: added possibility to move filter to any place using drag&drop
(instead of up/down buttons), added filter sets list object, added more
'loading' messages
- Added option to hide some scripts (managesieve_filename_exceptions)
* version 4.3 [2011-07-28]
-----------------------------------------------------------
- Fixed handling of error in Net_Sieve::listScripts()
- Fixed handling of REFERRAL responses (http://pear.php.net/bugs/bug.php?id=17107)
- Fixed bug where wrong folders hierarchy was displayed on folders listing
* version 4.2 [2011-05-24]
-----------------------------------------------------------
- Moved elsif replacement code to handle only imports from other formats
- Fixed mod_mailbox() usage for newer Roundcube versions
- Fixed regex extension (error: regex require missing)
* version 4.1 [2011-03-07]
-----------------------------------------------------------
- Fix fileinto target is always INBOX (#1487776)
- Fix escaping of backslash character in quoted strings (#1487780)
- Fix handling of non-safe characters (double-quote, backslash)
or UTF-8 characters (dovecot's implementation bug workaround)
in script names
- Fix saving of a script using flags extension on servers with imap4flags support (#1487825)
* version 4.0 [2011-02-10]
-----------------------------------------------------------
- Fix STARTTLS for timsieved < 2.3.10
- Added :regex and :matches support (#1487746)
- Added setflag/addflag/removeflag support (#1487449)
- Added support for vacation :subject field (#1487120)
- rcube_sieve_script class moved to separate file
- Moved javascript code from skin templates into managesieve.js file
* version 3.0 [2011-02-01]
-----------------------------------------------------------
- Added support for SASL proxy authentication (#1486691)
- Fixed parsing of scripts with \r\n line separator
- Apply forgotten changes for form errors handling
- Fix multi-line strings parsing (#1487685)
- Added tests for script parser
- Rewritten script parser
- Fix double request when clicking on Filters tab using Firefox
* version 2.10 [2010-10-10]
-----------------------------------------------------------
- Fixed import from Avelsieve
- Use localized size units (#1486976)
- Added support for relational operators and i;ascii-numeric comparator
- Added popups with form errors
* version 2.9 [2010-08-02]
-----------------------------------------------------------
- Fixed vacation parameters parsing (#1486883)
* version 2.8 [2010-07-08]
-----------------------------------------------------------
- Added managesieve_auth_type option (#1486731)
* version 2.7 [2010-07-06]
-----------------------------------------------------------
- Update Net_Sieve to version 1.3.0 (fixes LOGIN athentication)
- Added support for copying and copy sending of messages (COPY extension)
* version 2.6 [2010-06-03]
-----------------------------------------------------------
- Support %n and %d variables in managesieve_host option
* version 2.5 [2010-05-04]
-----------------------------------------------------------
- Fix filters set label after activation
- Fix filters set activation, add possibility to deactivate sets (#1486699)
- Fix download button state when sets list is empty
- Fix errors when sets list is empty
* version 2.4 [2010-04-01]
-----------------------------------------------------------
- Fixed bug in DIGEST-MD5 authentication (http://pear.php.net/bugs/bug.php?id=17285)
- Fixed disabling rules with many tests
- Small css unification with core
- Scripts import/export
* version 2.3 [2010-03-18]
-----------------------------------------------------------
- Added import from Horde-INGO
- Support for more than one match using if+stop instead of if+elsif structures (#1486078)
- Support for selectively disabling rules within a single sieve script (#1485882)
- Added vertical splitter
* version 2.2 [2010-02-06]
-----------------------------------------------------------
- Fix handling of "<>" characters in filter names (#1486477)
* version 2.1 [2010-01-12]
-----------------------------------------------------------
- Fix "require" structure generation when many modules are used
- Fix problem with '<' and '>' characters in header tests
* version 2.0 [2009-11-02]
-----------------------------------------------------------
- Added 'managesieve_debug' option
- Added multi-script support
- Small css improvements + sprite image buttons
- PEAR::NetSieve 1.2.0b1
* version 1.7 [2009-09-20]
-----------------------------------------------------------
- Support multiple managesieve hosts using %h variable
in managesieve_host option
- Fix first rule deleting (#1486140)
* version 1.6 [2009-09-08]
-----------------------------------------------------------
- Fix warning when importing squirrelmail rules
- Fix handling of "true" as "anyof (true)" test
* version 1.5 [2009-09-04]
-----------------------------------------------------------
- Added es_ES, ua_UA localizations
- Added 'managesieve_mbox_encoding' option
* version 1.4 [2009-07-29]
-----------------------------------------------------------
- Updated PEAR::Net_Sieve to 1.1.7
* version 1.3 [2009-07-24]
-----------------------------------------------------------
- support more languages
- support config.inc.php file
* version 1.2 [2009-06-28]
-----------------------------------------------------------
- Support IMAP namespaces in fileinto (#1485943)
- Added it_IT localization
* version 1.1 [2009-05-27]
-----------------------------------------------------------
- Added new icons
- Added support for headers lists (coma-separated) in rules
- Added de_CH localization
* version 1.0 [2009-05-21]
-----------------------------------------------------------
- Rewritten using plugin API
- Added hu_HU localization (Tamas Tevesz)
* version beta7 (svn-r2300) [2009-03-01]
-----------------------------------------------------------
- Added SquirrelMail script auto-import (Jonathan Ernst)
- Added 'vacation' support (Jonathan Ernst & alec)
- Added 'stop' support (Jonathan Ernst)
- Added option for extensions disabling (Jonathan Ernst & alec)
- Added fi_FI, nl_NL, bg_BG localization
- Small style fixes
* version 0.2-stable1 (svn-r2205) [2009-01-03]
-----------------------------------------------------------
- Fix moving down filter row
- Fixes for compressed js files in stable release package
- Created patch for svn version r2205
* version 0.2-stable [2008-12-31]
-----------------------------------------------------------
- Added ru_RU, fr_FR, zh_CN translation
- Fixes for Roundcube 0.2-stable
* version rc0.2beta [2008-09-21]
-----------------------------------------------------------
- Small css fixes for IE
- Fixes for Roundcube 0.2-beta
* version beta6 [2008-08-08]
-----------------------------------------------------------
- Added de_DE translation
- Fix for Roundcube r1634
* version beta5 [2008-06-10]
-----------------------------------------------------------
- Fixed 'exists' operators
- Fixed 'not*' operators for custom headers
- Fixed filters deleting
* version beta4 [2008-06-09]
-----------------------------------------------------------
- Fix for Roundcube r1490
* version beta3 [2008-05-22]
-----------------------------------------------------------
- Fixed textarea error class setting
- Added pagetitle setting
- Added option 'managesieve_replace_delimiter'
- Fixed errors on IE (still need some css fixes)
* version beta2 [2008-05-20]
-----------------------------------------------------------
- Use 'if' only for first filter and 'elsif' for the rest
* version beta1 [2008-05-15]
-----------------------------------------------------------
- Initial version for Roundcube r1388.
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve.php b/plugins/managesieve/lib/Roundcube/rcube_sieve.php
index 736f73146..a04e3c2a4 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve.php
@@ -1,384 +1,384 @@
<?php
/**
* Classes for managesieve operations (using PEAR::Net_Sieve)
*
* Copyright (C) 2008-2011, The Roundcube Dev Team
* Copyright (C) 2011, Kolab Systems AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
// Managesieve Protocol: RFC5804
define('SIEVE_ERROR_CONNECTION', 1);
define('SIEVE_ERROR_LOGIN', 2);
define('SIEVE_ERROR_NOT_EXISTS', 3); // script not exists
define('SIEVE_ERROR_INSTALL', 4); // script installation
define('SIEVE_ERROR_ACTIVATE', 5); // script activation
define('SIEVE_ERROR_DELETE', 6); // script deletion
define('SIEVE_ERROR_INTERNAL', 7); // internal error
define('SIEVE_ERROR_DEACTIVATE', 8); // script activation
define('SIEVE_ERROR_OTHER', 255); // other/unknown error
class rcube_sieve
{
private $sieve; // Net_Sieve object
private $error = false; // error flag
private $list = array(); // scripts list
public $script; // rcube_sieve_script object
public $current; // name of currently loaded script
private $exts; // array of supported extensions
/**
* Object constructor
*
* @param string Username (for managesieve login)
* @param string Password (for managesieve login)
* @param string Managesieve server hostname/address
* @param string Managesieve server port number
* @param string Managesieve authentication method
* @param boolean Enable/disable TLS use
* @param array Disabled extensions
* @param boolean Enable/disable debugging
* @param string Proxy authentication identifier
* @param string Proxy authentication password
*/
public function __construct($username, $password='', $host='localhost', $port=2000,
$auth_type=null, $usetls=true, $disabled=array(), $debug=false,
$auth_cid=null, $auth_pw=null)
{
$this->sieve = new Net_Sieve();
if ($debug) {
$this->sieve->setDebug(true, array($this, 'debug_handler'));
}
if (PEAR::isError($this->sieve->connect($host, $port, null, $usetls))) {
return $this->_set_error(SIEVE_ERROR_CONNECTION);
}
if (!empty($auth_cid)) {
$authz = $username;
$username = $auth_cid;
$password = $auth_pw;
}
if (PEAR::isError($this->sieve->login($username, $password,
$auth_type ? strtoupper($auth_type) : null, $authz))
) {
return $this->_set_error(SIEVE_ERROR_LOGIN);
}
- $this->exts = $this->get_extensions();
+ $this->exts = $this->get_extensions();
// 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() {
$this->sieve->disconnect();
}
/**
* Getter for error code
*/
public function error()
{
return $this->error ? $this->error : false;
}
/**
* Saves current script into server
*/
public function save($name = null)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (!$this->script)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (!$name)
$name = $this->current;
$script = $this->script->as_text();
if (!$script)
$script = '/* empty script */';
if (PEAR::isError($this->sieve->installScript($name, $script)))
return $this->_set_error(SIEVE_ERROR_INSTALL);
return true;
}
/**
* Saves text script into server
*/
public function save_script($name, $content = null)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (!$content)
$content = '/* empty script */';
if (PEAR::isError($this->sieve->installScript($name, $content)))
return $this->_set_error(SIEVE_ERROR_INSTALL);
return true;
}
/**
* Activates specified script
*/
public function activate($name = null)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (!$name)
$name = $this->current;
if (PEAR::isError($this->sieve->setActive($name)))
return $this->_set_error(SIEVE_ERROR_ACTIVATE);
return true;
}
/**
* De-activates specified script
*/
public function deactivate()
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (PEAR::isError($this->sieve->setActive('')))
return $this->_set_error(SIEVE_ERROR_DEACTIVATE);
return true;
}
/**
* Removes specified script
*/
public function remove($name = null)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if (!$name)
$name = $this->current;
// script must be deactivated first
if ($name == $this->sieve->getActive())
if (PEAR::isError($this->sieve->setActive('')))
return $this->_set_error(SIEVE_ERROR_DELETE);
if (PEAR::isError($this->sieve->removeScript($name)))
return $this->_set_error(SIEVE_ERROR_DELETE);
if ($name == $this->current)
$this->current = null;
return true;
}
/**
* Gets list of supported by server Sieve extensions
*/
public function get_extensions()
{
if ($this->exts)
return $this->exts;
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
$ext = $this->sieve->getExtensions();
// we're working on lower-cased names
$ext = array_map('strtolower', (array) $ext);
if ($this->script) {
$supported = $this->script->get_extensions();
foreach ($ext as $idx => $ext_name)
if (!in_array($ext_name, $supported))
unset($ext[$idx]);
}
return array_values($ext);
}
/**
* Gets list of scripts from server
*/
public function get_scripts()
{
if (!$this->list) {
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
$list = $this->sieve->listScripts();
if (PEAR::isError($list))
return $this->_set_error(SIEVE_ERROR_OTHER);
$this->list = $list;
}
return $this->list;
}
/**
* Returns active script name
*/
public function get_active()
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
return $this->sieve->getActive();
}
/**
* Loads script by name
*/
public function load($name)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if ($this->current == $name)
return true;
$script = $this->sieve->getScript($name);
if (PEAR::isError($script))
return $this->_set_error(SIEVE_ERROR_OTHER);
// try to parse from Roundcube format
$this->script = $this->_parse($script);
$this->current = $name;
return true;
}
/**
* Loads script from text content
*/
public function load_script($script)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
// try to parse from Roundcube format
$this->script = $this->_parse($script);
}
/**
* Creates rcube_sieve_script object from text script
*/
private function _parse($txt)
{
// parse
$script = new rcube_sieve_script($txt, $this->exts);
// fix/convert to Roundcube format
if (!empty($script->content)) {
// replace all elsif with if+stop, we support only ifs
foreach ($script->content as $idx => $rule) {
if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) {
continue;
}
$script->content[$idx]['type'] = 'if';
// 'stop' not found?
foreach ($rule['actions'] as $action) {
if (preg_match('/^(stop|vacation)$/', $action['type'])) {
continue 2;
}
}
if (!empty($script->content[$idx+1]) && $script->content[$idx+1]['type'] != 'if') {
$script->content[$idx]['actions'][] = array('type' => 'stop');
}
}
}
return $script;
}
/**
* Gets specified script as text
*/
public function get_script($name)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
$content = $this->sieve->getScript($name);
if (PEAR::isError($content))
return $this->_set_error(SIEVE_ERROR_OTHER);
return $content;
}
/**
* Creates empty script or copy of other script
*/
public function copy($name, $copy)
{
if (!$this->sieve)
return $this->_set_error(SIEVE_ERROR_INTERNAL);
if ($copy) {
$content = $this->sieve->getScript($copy);
if (PEAR::isError($content))
return $this->_set_error(SIEVE_ERROR_OTHER);
}
return $this->save_script($name, $content);
}
private function _set_error($error)
{
$this->error = $error;
return false;
}
/**
* This is our own debug handler for connection
*/
public function debug_handler(&$sieve, $message)
{
rcube::write_log('sieve', preg_replace('/\r\n$/', '', $message));
}
}
diff --git a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
index 36eb1bcf8..80f590f4b 100644
--- a/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
+++ b/plugins/managesieve/lib/Roundcube/rcube_sieve_script.php
@@ -1,1154 +1,1155 @@
<?php
/**
* Class for operations on Sieve scripts
*
* Copyright (C) 2008-2011, The Roundcube Dev Team
* Copyright (C) 2011, Kolab Systems AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
class rcube_sieve_script
{
public $content = array(); // script rules array
private $vars = array(); // "global" variables
private $prefix = ''; // script header (comments)
private $supported = array( // Sieve extensions supported by class
'fileinto', // RFC5228
'envelope', // RFC5228
'reject', // RFC5429
'ereject', // RFC5429
'copy', // RFC3894
'vacation', // RFC5230
+ 'vacation-seconds', // RFC6131
'relational', // RFC3431
'regex', // draft-ietf-sieve-regex-01
'imapflags', // draft-melnikov-sieve-imapflags-06
'imap4flags', // RFC5232
'include', // draft-ietf-sieve-include-12
'variables', // RFC5229
'body', // RFC5173
'subaddress', // RFC5233
'enotify', // RFC5435
'notify', // draft-ietf-sieve-notify-00
// @TODO: spamtest+virustest, mailbox, date
);
/**
* Object constructor
*
* @param string Script's text content
* @param array List of capabilities supported by server
*/
public function __construct($script, $capabilities=array())
{
$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]);
}
}
}
// Parse text content of the script
$this->_parse_text($script);
}
/**
* Adds rule to the script (at the end)
*
* @param string Rule name
* @param array Rule content (as array)
*
* @return int The index of the new rule
*/
public function add_rule($content)
{
// TODO: check this->supported
array_push($this->content, $content);
return sizeof($this->content)-1;
}
public function delete_rule($index)
{
if(isset($this->content[$index])) {
unset($this->content[$index]);
return true;
}
return false;
}
public function size()
{
return sizeof($this->content);
}
public function update_rule($index, $content)
{
// TODO: check this->supported
if ($this->content[$index]) {
$this->content[$index] = $content;
return $index;
}
return false;
}
/**
* Sets "global" variable
*
* @param string $name Variable name
* @param string $value Variable value
* @param array $mods Variable modifiers
*/
public function set_var($name, $value, $mods = array())
{
// Check if variable exists
for ($i=0, $len=count($this->vars); $i<$len; $i++) {
if ($this->vars[$i]['name'] == $name) {
break;
}
}
$var = array_merge($mods, array('name' => $name, 'value' => $value));
$this->vars[$i] = $var;
}
/**
* Unsets "global" variable
*
* @param string $name Variable name
*/
public function unset_var($name)
{
// Check if variable exists
foreach ($this->vars as $idx => $var) {
if ($var['name'] == $name) {
unset($this->vars[$idx]);
break;
}
}
}
/**
* Gets the value of "global" variable
*
* @param string $name Variable name
*
* @return string Variable value
*/
public function get_var($name)
{
// Check if variable exists
for ($i=0, $len=count($this->vars); $i<$len; $i++) {
if ($this->vars[$i]['name'] == $name) {
return $this->vars[$i]['name'];
}
}
}
/**
* Sets script header content
*
* @param string $text Header content
*/
public function set_prefix($text)
{
$this->prefix = $text;
}
/**
* Returns script as text
*/
public function as_text()
{
$output = '';
$exts = array();
$idx = 0;
if (!empty($this->vars)) {
if (in_array('variables', (array)$this->supported)) {
$has_vars = true;
array_push($exts, 'variables');
}
foreach ($this->vars as $var) {
if (empty($has_vars)) {
// 'variables' extension not supported, put vars in comments
$output .= sprintf("# %s %s\n", $var['name'], $var['value']);
}
else {
$output .= 'set ';
foreach (array_diff(array_keys($var), array('name', 'value')) as $opt) {
$output .= ":$opt ";
}
$output .= self::escape_string($var['name']) . ' ' . self::escape_string($var['value']) . ";\n";
}
}
}
$imapflags = in_array('imap4flags', $this->supported) ? 'imap4flags' : 'imapflags';
$notify = in_array('enotify', $this->supported) ? 'enotify' : 'notify';
// rules
foreach ($this->content as $rule) {
$extension = '';
$script = '';
$tests = array();
$i = 0;
// header
if (!empty($rule['name']) && strlen($rule['name'])) {
$script .= '# rule:[' . $rule['name'] . "]\n";
}
// constraints expressions
if (!empty($rule['tests'])) {
foreach ($rule['tests'] as $test) {
$tests[$i] = '';
switch ($test['test']) {
case 'size':
$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';
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] .= ' :' . $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'];
}
}
$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++;
}
}
// disabled rule: if false #....
if (!empty($tests)) {
$script .= 'if ' . ($rule['disabled'] ? 'false # ' : '');
if (count($tests) > 1) {
$tests_str = implode(', ', $tests);
}
else {
$tests_str = $tests[0];
}
if ($rule['join'] || count($tests) > 1) {
$script .= sprintf('%s (%s)', $rule['join'] ? 'allof' : 'anyof', $tests_str);
}
else {
$script .= $tests_str;
}
$script .= "\n{\n";
}
// action(s)
if (!empty($rule['actions'])) {
foreach ($rule['actions'] as $action) {
$action_script = '';
switch ($action['type']) {
case 'fileinto':
array_push($exts, 'fileinto');
$action_script .= 'fileinto ';
if ($action['copy']) {
$action_script .= ':copy ';
array_push($exts, 'copy');
}
$action_script .= self::escape_string($action['target']);
break;
case 'redirect':
$action_script .= 'redirect ';
if ($action['copy']) {
$action_script .= ':copy ';
array_push($exts, 'copy');
}
$action_script .= self::escape_string($action['target']);
break;
case 'reject':
case 'ereject':
array_push($exts, $action['type']);
$action_script .= $action['type'].' '
. self::escape_string($action['target']);
break;
case 'addflag':
case 'setflag':
case 'removeflag':
array_push($exts, $imapflags);
$action_script .= $action['type'].' '
. self::escape_string($action['target']);
break;
case 'keep':
case 'discard':
case 'stop':
$action_script .= $action['type'];
break;
case 'include':
array_push($exts, 'include');
$action_script .= 'include ';
foreach (array_diff(array_keys($action), array('target', 'type')) as $opt) {
$action_script .= ":$opt ";
}
$action_script .= self::escape_string($action['target']);
break;
case 'set':
array_push($exts, 'variables');
$action_script .= 'set ';
foreach (array_diff(array_keys($action), array('name', 'value', 'type')) as $opt) {
$action_script .= ":$opt ";
}
$action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']);
break;
case 'notify':
array_push($exts, $notify);
$action_script .= 'notify';
// Here we support only 00 version of notify draft, there
// were a couple regressions in 00 to 04 changelog, we use
// the version used by Cyrus
if ($notify == 'notify') {
switch ($action['importance']) {
case 1: $action_script .= " :high"; break;
case 2: $action_script .= " :normal"; break;
case 3: $action_script .= " :low"; break;
}
unset($action['importance']);
}
foreach (array('from', 'importance', 'options', 'message') as $n_tag) {
if (!empty($action[$n_tag])) {
$action_script .= " :$n_tag " . self::escape_string($action[$n_tag]);
}
}
if (!empty($action['address'])) {
$method = 'mailto:' . $action['address'];
if (!empty($action['body'])) {
$method .= '?body=' . rawurlencode($action['body']);
}
}
else {
$method = $action['method'];
}
// method is optional in notify extension
if (!empty($method)) {
$action_script .= ($notify == 'notify' ? " :method " : " ") . self::escape_string($method);
}
break;
case 'vacation':
array_push($exts, 'vacation');
$action_script .= 'vacation';
- if (!empty($action['days']))
- $action_script .= " :days " . $action['days'];
+ if (isset($action['seconds'])) {
+ array_push($exts, 'vacation-seconds');
+ $action_script .= " :seconds " . intval($action['seconds']);
+ }
+ else if (!empty($action['days'])) {
+ $action_script .= " :days " . intval($action['days']);
+ }
if (!empty($action['addresses']))
$action_script .= " :addresses " . self::escape_string($action['addresses']);
if (!empty($action['subject']))
$action_script .= " :subject " . self::escape_string($action['subject']);
if (!empty($action['handle']))
$action_script .= " :handle " . self::escape_string($action['handle']);
if (!empty($action['from']))
$action_script .= " :from " . self::escape_string($action['from']);
if (!empty($action['mime']))
$action_script .= " :mime";
$action_script .= " " . self::escape_string($action['reason']);
break;
}
if ($action_script) {
$script .= !empty($tests) ? "\t" : '';
$script .= $action_script . ";\n";
}
}
}
if ($script) {
$output .= $script . (!empty($tests) ? "}\n" : '');
$idx++;
}
}
// requires
- if (!empty($exts))
- $output = 'require ["' . implode('","', array_unique($exts)) . "\"];\n" . $output;
+ if (!empty($exts)) {
+ $exts = array_unique($exts);
+
+ if (in_array('vacation-seconds', $exts) && ($key = array_search('vacation', $exts)) !== false) {
+ unset($exts[$key]);
+ }
+
+ $output = 'require ["' . implode('","', $exts) . "\"];\n" . $output;
+ }
if (!empty($this->prefix)) {
$output = $this->prefix . "\n\n" . $output;
}
return $output;
}
/**
* Returns script object
*
*/
public function as_array()
{
return $this->content;
}
/**
* Returns array of supported extensions
*
*/
public function get_extensions()
{
return array_values($this->supported);
}
/**
* Converts text script to rules array
*
* @param string Text script
*/
private function _parse_text($script)
{
$prefix = '';
$options = array();
while ($script) {
$script = trim($script);
$rule = array();
// Comments
while (!empty($script) && $script[0] == '#') {
$endl = strpos($script, "\n");
$line = $endl ? substr($script, 0, $endl) : $script;
// Roundcube format
if (preg_match('/^# rule:\[(.*)\]/', $line, $matches)) {
$rulename = $matches[1];
}
// KEP:14 variables
else if (preg_match('/^# (EDITOR|EDITOR_VERSION) (.+)$/', $line, $matches)) {
$this->set_var($matches[1], $matches[2]);
}
// Horde-Ingo format
else if (!empty($options['format']) && $options['format'] == 'INGO'
&& preg_match('/^# (.*)/', $line, $matches)
) {
$rulename = $matches[1];
}
else if (empty($options['prefix'])) {
$prefix .= $line . "\n";
}
$script = ltrim(substr($script, strlen($line) + 1));
}
// handle script header
if (empty($options['prefix'])) {
$options['prefix'] = true;
if ($prefix && strpos($prefix, 'horde.org/ingo')) {
$options['format'] = 'INGO';
}
}
// Control structures/blocks
if (preg_match('/^(if|else|elsif)/i', $script)) {
$rule = $this->_tokenize_rule($script);
if (strlen($rulename) && !empty($rule)) {
$rule['name'] = $rulename;
}
}
// Simple commands
else {
$rule = $this->_parse_actions($script, ';');
if (!empty($rule[0]) && is_array($rule)) {
// set "global" variables
if ($rule[0]['type'] == 'set') {
unset($rule[0]['type']);
$this->vars[] = $rule[0];
}
else {
$rule = array('actions' => $rule);
}
}
}
$rulename = '';
if (!empty($rule)) {
$this->content[] = $rule;
}
}
if (!empty($prefix)) {
$this->prefix = trim($prefix);
}
}
/**
* Convert text script fragment to rule object
*
* @param string Text rule
*
* @return array Rule data
*/
private function _tokenize_rule(&$content)
{
$cond = strtolower(self::tokenize($content, 1));
if ($cond != 'if' && $cond != 'elsif' && $cond != 'else') {
return null;
}
$disabled = false;
$join = false;
// disabled rule (false + comment): if false # .....
if (preg_match('/^\s*false\s+#/i', $content)) {
$content = preg_replace('/^\s*false\s+#\s*/i', '', $content);
$disabled = true;
}
while (strlen($content)) {
$tokens = self::tokenize($content, true);
$separator = array_pop($tokens);
if (!empty($tokens)) {
$token = array_shift($tokens);
}
else {
$token = $separator;
}
$token = strtolower($token);
if ($token == 'not') {
$not = true;
$token = strtolower(array_shift($tokens));
}
else {
$not = false;
}
switch ($token) {
case 'allof':
$join = true;
break;
case 'anyof':
break;
case 'size':
$size = array('test' => 'size', 'not' => $not);
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i])
&& preg_match('/^:(under|over)$/i', $tokens[$i])
) {
$size['type'] = strtolower(substr($tokens[$i], 1));
}
else {
$size['arg'] = $tokens[$i];
}
}
$tests[] = $size;
break;
case 'header':
$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])) {
$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];
}
else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) {
$header['type'] = strtolower(substr($tokens[$i], 1));
}
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];
}
}
$tests[] = $header;
break;
case 'exists':
$tests[] = array('test' => 'exists', 'not' => $not,
'arg' => array_pop($tokens));
break;
case 'true':
$tests[] = array('test' => 'true', 'not' => $not);
break;
case 'false':
$tests[] = array('test' => 'true', 'not' => !$not);
break;
}
// goto actions...
if ($separator == '{') {
break;
}
}
// ...and actions block
$actions = $this->_parse_actions($content);
if ($tests && $actions) {
$result = array(
'type' => $cond,
'tests' => $tests,
'actions' => $actions,
'join' => $join,
'disabled' => $disabled,
);
}
return $result;
}
/**
* Parse body of actions section
*
* @param string $content Text body
* @param string $end End of text separator
*
* @return array Array of parsed action type/target pairs
*/
private function _parse_actions(&$content, $end = '}')
{
$result = null;
while (strlen($content)) {
$tokens = self::tokenize($content, true);
$separator = array_pop($tokens);
if (!empty($tokens)) {
$token = array_shift($tokens);
}
else {
$token = $separator;
}
switch ($token) {
case 'discard':
case 'keep':
case 'stop':
$result[] = array('type' => $token);
break;
case 'fileinto':
case 'redirect':
$copy = false;
$target = '';
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (strtolower($tokens[$i]) == ':copy') {
$copy = true;
}
else {
$target = $tokens[$i];
}
}
$result[] = array('type' => $token, 'copy' => $copy,
'target' => $target);
break;
case 'reject':
case 'ereject':
$result[] = array('type' => $token, 'target' => array_pop($tokens));
break;
case 'vacation':
$vacation = array('type' => 'vacation', 'reason' => array_pop($tokens));
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = strtolower($tokens[$i]);
- if ($tok == ':days') {
- $vacation['days'] = $tokens[++$i];
- }
- else if ($tok == ':subject') {
- $vacation['subject'] = $tokens[++$i];
- }
- else if ($tok == ':addresses') {
- $vacation['addresses'] = $tokens[++$i];
- }
- else if ($tok == ':handle') {
- $vacation['handle'] = $tokens[++$i];
- }
- else if ($tok == ':from') {
- $vacation['from'] = $tokens[++$i];
- }
- else if ($tok == ':mime') {
+ if ($tok == ':mime') {
$vacation['mime'] = true;
}
+ else if ($tok[0] == ':') {
+ $vacation[substr($tok, 1)] = $tokens[++$i];
+ }
}
$result[] = $vacation;
break;
case 'setflag':
case 'addflag':
case 'removeflag':
$result[] = array('type' => $token,
// Flags list: last token (skip optional variable)
'target' => $tokens[count($tokens)-1]
);
break;
case 'include':
$include = array('type' => 'include', 'target' => array_pop($tokens));
// Parameters: :once, :optional, :global, :personal
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = strtolower($tokens[$i]);
if ($tok[0] == ':') {
$include[substr($tok, 1)] = true;
}
}
$result[] = $include;
break;
case 'set':
$set = array('type' => 'set', 'value' => array_pop($tokens), 'name' => array_pop($tokens));
// Parameters: :lower :upper :lowerfirst :upperfirst :quotewildcard :length
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = strtolower($tokens[$i]);
if ($tok[0] == ':') {
$set[substr($tok, 1)] = true;
}
}
$result[] = $set;
break;
case 'require':
// skip, will be build according to used commands
// $result[] = array('type' => 'require', 'target' => $tokens);
break;
case 'notify':
$notify = array('type' => 'notify');
$priorities = array(':high' => 1, ':normal' => 2, ':low' => 3);
// Parameters: :from, :importance, :options, :message
// additional (optional) :method parameter for notify extension
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = strtolower($tokens[$i]);
if ($tok[0] == ':') {
// Here we support only 00 version of notify draft, there
// were a couple regressions in 00 to 04 changelog, we use
// the version used by Cyrus
if (isset($priorities[$tok])) {
$notify['importance'] = $priorities[$tok];
}
else {
$notify[substr($tok, 1)] = $tokens[++$i];
}
}
else {
// unnamed parameter is a :method in enotify extension
$notify['method'] = $tokens[$i];
}
}
$method_components = parse_url($notify['method']);
if ($method_components['scheme'] == 'mailto') {
$notify['address'] = $method_components['path'];
$method_params = array();
if (array_key_exists('query', $method_components)) {
parse_str($method_components['query'], $method_params);
}
$method_params = array_change_key_case($method_params, CASE_LOWER);
// magic_quotes_gpc and magic_quotes_sybase affect the output of parse_str
if (ini_get('magic_quotes_gpc') || ini_get('magic_quotes_sybase')) {
array_map('stripslashes', $method_params);
}
$notify['body'] = (array_key_exists('body', $method_params)) ? $method_params['body'] : '';
}
$result[] = $notify;
break;
}
if ($separator == $end)
break;
}
return $result;
}
/**
*
*/
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
*
* @param string $str Text or array (list) of strings
*
* @return string Result text
*/
static function escape_string($str)
{
if (is_array($str) && count($str) > 1) {
foreach($str as $idx => $val)
$str[$idx] = self::escape_string($val);
return '[' . implode(',', $str) . ']';
}
else if (is_array($str)) {
$str = array_pop($str);
}
// multi-line string
if (preg_match('/[\r\n\0]/', $str) || strlen($str) > 1024) {
return sprintf("text:\n%s\n.\n", self::escape_multiline_string($str));
}
// quoted-string
else {
return '"' . addcslashes($str, '\\"') . '"';
}
}
/**
* Escape special chars in multi-line string value
*
* @param string $str Text
*
* @return string Text
*/
static function escape_multiline_string($str)
{
$str = preg_split('/(\r?\n)/', $str, -1, PREG_SPLIT_DELIM_CAPTURE);
foreach ($str as $idx => $line) {
// dot-stuffing
if (isset($line[0]) && $line[0] == '.') {
$str[$idx] = '.' . $line;
}
}
return implode($str);
}
/**
* Splits script into string tokens
*
* @param string &$str The script
* @param mixed $num Number of tokens to return, 0 for all
* or True for all tokens until separator is found.
* Separator will be returned as last token.
* @param int $in_list Enable to call recursively inside a list
*
* @return mixed Tokens array or string if $num=1
*/
static function tokenize(&$str, $num=0, $in_list=false)
{
$result = array();
// remove spaces from the beginning of the string
while (($str = ltrim($str)) !== ''
&& (!$num || $num === true || count($result) < $num)
) {
switch ($str[0]) {
// Quoted string
case '"':
$len = strlen($str);
for ($pos=1; $pos<$len; $pos++) {
if ($str[$pos] == '"') {
break;
}
if ($str[$pos] == "\\") {
if ($str[$pos + 1] == '"' || $str[$pos + 1] == "\\") {
$pos++;
}
}
}
if ($str[$pos] != '"') {
// error
}
// we need to strip slashes for a quoted string
$result[] = stripslashes(substr($str, 1, $pos - 1));
$str = substr($str, $pos + 1);
break;
// Parenthesized list
case '[':
$str = substr($str, 1);
$result[] = self::tokenize($str, 0, true);
break;
case ']':
$str = substr($str, 1);
return $result;
break;
// list/test separator
case ',':
// command separator
case ';':
// block/tests-list
case '(':
case ')':
case '{':
case '}':
$sep = $str[0];
$str = substr($str, 1);
if ($num === true) {
$result[] = $sep;
break 2;
}
break;
// bracket-comment
case '/':
if ($str[1] == '*') {
if ($end_pos = strpos($str, '*/')) {
$str = substr($str, $end_pos + 2);
}
else {
// error
$str = '';
}
}
break;
// hash-comment
case '#':
if ($lf_pos = strpos($str, "\n")) {
$str = substr($str, $lf_pos);
break;
}
else {
$str = '';
}
// String atom
default:
// empty or one character
if ($str === '' || $str === null) {
break 2;
}
if (strlen($str) < 2) {
$result[] = $str;
$str = '';
break;
}
// tag/identifier/number
if (preg_match('/^([a-z0-9:_]+)/i', $str, $m)) {
$str = substr($str, strlen($m[1]));
if ($m[1] != 'text:') {
$result[] = $m[1];
}
// multiline string
else {
// possible hash-comment after "text:"
if (preg_match('/^( |\t)*(#[^\n]+)?\n/', $str, $m)) {
$str = substr($str, strlen($m[0]));
}
// get text until alone dot in a line
if (preg_match('/^(.*)\r?\n\.\r?\n/sU', $str, $m)) {
$text = $m[1];
// remove dot-stuffing
$text = str_replace("\n..", "\n.", $text);
$str = substr($str, strlen($m[0]));
}
else {
$text = '';
}
$result[] = $text;
}
}
// fallback, skip one character as infinite loop prevention
else {
$str = substr($str, 1);
}
break;
}
}
return $num === 1 ? (isset($result[0]) ? $result[0] : null) : $result;
}
}
diff --git a/plugins/managesieve/localization/en_US.inc b/plugins/managesieve/localization/en_US.inc
index 97c20aa76..2b391e0c5 100644
--- a/plugins/managesieve/localization/en_US.inc
+++ b/plugins/managesieve/localization/en_US.inc
@@ -1,174 +1,177 @@
<?php
/*
+-----------------------------------------------------------------------+
| plugins/managesieve/localization/<lang>.inc |
| |
| Localization file of the Roundcube Webmail Managesieve plugin |
| Copyright (C) 2012-2013, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
+-----------------------------------------------------------------------+
For translation see https://www.transifex.com/projects/p/roundcube-webmail/resource/plugin-managesieve/
*/
$labels['filters'] = 'Filters';
$labels['managefilters'] = 'Manage incoming mail filters';
$labels['filtername'] = 'Filter name';
$labels['newfilter'] = 'New filter';
$labels['filteradd'] = 'Add filter';
$labels['filterdel'] = 'Delete filter';
$labels['moveup'] = 'Move up';
$labels['movedown'] = 'Move down';
$labels['filterallof'] = 'matching all of the following rules';
$labels['filteranyof'] = 'matching any of the following rules';
$labels['filterany'] = 'all messages';
$labels['filtercontains'] = 'contains';
$labels['filternotcontains'] = 'not contains';
$labels['filteris'] = 'is equal to';
$labels['filterisnot'] = 'is not equal to';
$labels['filterexists'] = 'exists';
$labels['filternotexists'] = 'not exists';
$labels['filtermatches'] = 'matches expression';
$labels['filternotmatches'] = 'not matches expression';
$labels['filterregex'] = 'matches regular expression';
$labels['filternotregex'] = 'not matches regular expression';
$labels['filterunder'] = 'under';
$labels['filterover'] = 'over';
$labels['addrule'] = 'Add rule';
$labels['delrule'] = 'Delete rule';
$labels['messagemoveto'] = 'Move message to';
$labels['messageredirect'] = 'Redirect message to';
$labels['messagecopyto'] = 'Copy message to';
$labels['messagesendcopy'] = 'Send message copy to';
$labels['messagereply'] = 'Reply with message';
$labels['messagedelete'] = 'Delete message';
$labels['messagediscard'] = 'Discard with message';
$labels['messagesrules'] = 'For incoming mail:';
$labels['messagesactions'] = '...execute the following actions:';
$labels['add'] = 'Add';
$labels['del'] = 'Delete';
$labels['sender'] = 'Sender';
$labels['recipient'] = 'Recipient';
$labels['vacationaddresses'] = 'My additional e-mail addresse(s) (comma-separated):';
$labels['vacationdays'] = 'How often send messages (in days):';
+$labels['vacationinterval'] = 'How often send messages:';
+$labels['days'] = 'days';
+$labels['seconds'] = 'seconds';
$labels['vacationreason'] = 'Message body (vacation reason):';
$labels['vacationsubject'] = 'Message subject:';
$labels['rulestop'] = 'Stop evaluating rules';
$labels['enable'] = 'Enable/Disable';
$labels['filterset'] = 'Filters set';
$labels['filtersets'] = 'Filter sets';
$labels['filtersetadd'] = 'Add filters set';
$labels['filtersetdel'] = 'Delete current filters set';
$labels['filtersetact'] = 'Activate current filters set';
$labels['filtersetdeact'] = 'Deactivate current filters set';
$labels['filterdef'] = 'Filter definition';
$labels['filtersetname'] = 'Filters set name';
$labels['newfilterset'] = 'New filters set';
$labels['active'] = 'active';
$labels['none'] = 'none';
$labels['fromset'] = 'from set';
$labels['fromfile'] = 'from file';
$labels['filterdisabled'] = 'Filter disabled';
$labels['countisgreaterthan'] = 'count is greater than';
$labels['countisgreaterthanequal'] = 'count is greater than or equal to';
$labels['countislessthan'] = 'count is less than';
$labels['countislessthanequal'] = 'count is less than or equal to';
$labels['countequals'] = 'count is equal to';
$labels['countnotequals'] = 'count does not equal';
$labels['valueisgreaterthan'] = 'value is greater than';
$labels['valueisgreaterthanequal'] = 'value is greater than or equal to';
$labels['valueislessthan'] = 'value is less than';
$labels['valueislessthanequal'] = 'value is less than or equal to';
$labels['valueequals'] = 'value is equal to';
$labels['valuenotequals'] = 'value does not equal';
$labels['setflags'] = 'Set flags to the message';
$labels['addflags'] = 'Add flags to the message';
$labels['removeflags'] = 'Remove flags from the message';
$labels['flagread'] = 'Read';
$labels['flagdeleted'] = 'Deleted';
$labels['flaganswered'] = 'Answered';
$labels['flagflagged'] = 'Flagged';
$labels['flagdraft'] = 'Draft';
$labels['setvariable'] = 'Set variable';
$labels['setvarname'] = 'Variable name:';
$labels['setvarvalue'] = 'Variable value:';
$labels['setvarmodifiers'] = 'Modifiers:';
$labels['varlower'] = 'lower-case';
$labels['varupper'] = 'upper-case';
$labels['varlowerfirst'] = 'first character lower-case';
$labels['varupperfirst'] = 'first character upper-case';
$labels['varquotewildcard'] = 'quote special characters';
$labels['varlength'] = 'length';
$labels['notify'] = 'Send notification';
$labels['notifyaddress'] = 'To e-mail address:';
$labels['notifybody'] = 'Notification body:';
$labels['notifysubject'] = 'Notification subject:';
$labels['notifyfrom'] = 'Notification sender:';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
$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.';
$messages['filterconnerror'] = 'Unable to connect to server.';
$messages['filterdeleteerror'] = 'Unable to delete filter. Server error occured.';
$messages['filterdeleted'] = 'Filter deleted successfully.';
$messages['filtersaved'] = 'Filter saved successfully.';
$messages['filtersaveerror'] = 'Unable to save filter. Server error occured.';
$messages['filterdeleteconfirm'] = 'Do you really want to delete selected filter?';
$messages['ruledeleteconfirm'] = 'Are you sure, you want to delete selected rule?';
$messages['actiondeleteconfirm'] = 'Are you sure, you want to delete selected action?';
$messages['forbiddenchars'] = 'Forbidden characters in field.';
$messages['cannotbeempty'] = 'Field cannot be empty.';
$messages['ruleexist'] = 'Filter with specified name already exists.';
$messages['setactivateerror'] = 'Unable to activate selected filters set. Server error occured.';
$messages['setdeactivateerror'] = 'Unable to deactivate selected filters set. Server error occured.';
$messages['setdeleteerror'] = 'Unable to delete selected filters set. Server error occured.';
$messages['setactivated'] = 'Filters set activated successfully.';
$messages['setdeactivated'] = 'Filters set deactivated successfully.';
$messages['setdeleted'] = 'Filters set deleted successfully.';
$messages['setdeleteconfirm'] = 'Are you sure, you want to delete selected filters set?';
$messages['setcreateerror'] = 'Unable to create filters set. Server error occured.';
$messages['setcreated'] = 'Filters set created successfully.';
$messages['activateerror'] = 'Unable to enable selected filter(s). Server error occured.';
$messages['deactivateerror'] = 'Unable to disable selected filter(s). Server error occured.';
$messages['deactivated'] = 'Filter(s) disabled successfully.';
$messages['activated'] = 'Filter(s) enabled successfully.';
$messages['moved'] = 'Filter moved successfully.';
$messages['moveerror'] = 'Unable to move selected filter. Server error occured.';
$messages['nametoolong'] = 'Name too long.';
$messages['namereserved'] = 'Reserved name.';
$messages['setexist'] = 'Set already exists.';
$messages['nodata'] = 'At least one position must be selected!';
?>
diff --git a/plugins/managesieve/managesieve.php b/plugins/managesieve/managesieve.php
index a667c06cd..817fa8650 100644
--- a/plugins/managesieve/managesieve.php
+++ b/plugins/managesieve/managesieve.php
@@ -1,2050 +1,2059 @@
<?php
/**
* Managesieve (Sieve Filters)
*
* Plugin that adds a possibility to manage Sieve filters in Thunderbird's style.
* It's clickable interface which operates on text scripts and communicates
* with server using managesieve protocol. Adds Filters tab in Settings.
*
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*
* Configuration (see config.inc.php.dist)
*
* Copyright (C) 2008-2012, The Roundcube Dev Team
* Copyright (C) 2011-2012, Kolab Systems AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
class managesieve extends rcube_plugin
{
public $task = 'mail|settings';
private $rc;
private $sieve;
private $errors;
private $form;
private $tips = array();
private $script = array();
private $exts = array();
private $list;
private $active = array();
private $headers = array(
'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 = '6.2';
const PROGNAME = 'Roundcube (Managesieve)';
const PORT = 4190;
function init()
{
$this->rc = rcmail::get_instance();
// register actions
$this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
$this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
if ($this->rc->task == 'settings') {
$this->init_ui();
}
else if ($this->rc->task == 'mail') {
// register message hook
$this->add_hook('message_headers_output', array($this, 'mail_headers'));
// inject Create Filter popup stuff
if (empty($this->rc->action) || $this->rc->action == 'show') {
$this->mail_task_handler();
}
}
}
/**
* Initializes plugin's UI (localization, js script)
*/
private function init_ui()
{
if ($this->ui_initialized)
return;
// load localization
$this->add_texts('localization/', array('filters','managefilters'));
$this->include_script('managesieve.js');
$this->ui_initialized = true;
}
/**
* Add UI elements to the 'mailbox view' and 'show message' UI.
*/
function mail_task_handler()
{
// use jQuery for popup window
$this->require_plugin('jqueryui');
// include js script and localization
$this->init_ui();
// include styles
$skin_path = $this->local_skin_path();
if (is_file($this->home . "/$skin_path/managesieve_mail.css")) {
$this->include_stylesheet("$skin_path/managesieve_mail.css");
}
// add 'Create filter' item to message menu
$this->api->add_content(html::tag('li', null,
$this->api->output->button(array(
'command' => 'managesieve-create',
'label' => 'managesieve.filtercreate',
'type' => 'link',
'classact' => 'icon filterlink active',
'class' => 'icon filterlink',
'innerclass' => 'icon filterlink',
))), 'messagemenu');
// register some labels/messages
$this->rc->output->add_label('managesieve.newfilter', 'managesieve.usedata',
'managesieve.nodata', 'managesieve.nextstep', 'save');
$this->rc->session->remove('managesieve_current');
}
/**
* Get message headers for popup window
*/
function mail_headers($args)
{
// this hook can be executed many times
if ($this->mail_headers_done) {
return $args;
}
$this->mail_headers_done = true;
$headers = $args['headers'];
$ret = array();
if ($headers->subject)
$ret[] = array('Subject', rcube_mime::decode_header($headers->subject));
// @TODO: List-Id, others?
foreach (array('From', 'To') as $h) {
$hl = strtolower($h);
if ($headers->$hl) {
$list = rcube_mime::decode_address_list($headers->$hl);
foreach ($list as $item) {
if ($item['mailto']) {
$ret[] = array($h, $item['mailto']);
}
}
}
}
if ($this->rc->action == 'preview')
$this->rc->output->command('parent.set_env', array('sieve_headers' => $ret));
else
$this->rc->output->set_env('sieve_headers', $ret);
return $args;
}
/**
* Loads configuration, initializes plugin (including sieve connection)
*/
function managesieve_start()
{
$this->load_config();
// register UI objects
$this->rc->output->add_handlers(array(
'filterslist' => array($this, 'filters_list'),
'filtersetslist' => array($this, 'filtersets_list'),
'filterframe' => array($this, 'filter_frame'),
'filterform' => array($this, 'filter_form'),
'filtersetform' => array($this, 'filterset_form'),
));
// Add include path for internal classes
$include_path = $this->home . '/lib' . PATH_SEPARATOR;
$include_path .= ini_get('include_path');
set_include_path($include_path);
// Get connection parameters
$host = $this->rc->config->get('managesieve_host', 'localhost');
$port = $this->rc->config->get('managesieve_port');
$tls = $this->rc->config->get('managesieve_usetls', false);
$host = rcube_utils::parse_host($host);
$host = rcube_utils::idn_to_ascii($host);
// remove tls:// prefix, set TLS flag
if (($host = preg_replace('|^tls://|i', '', $host, 1, $cnt)) && $cnt) {
$tls = true;
}
if (empty($port)) {
$port = getservbyname('sieve', 'tcp');
if (empty($port)) {
$port = self::PORT;
}
}
$plugin = $this->rc->plugins->exec_hook('managesieve_connect', array(
'user' => $_SESSION['username'],
'password' => $this->rc->decrypt($_SESSION['password']),
'host' => $host,
'port' => $port,
'usetls' => $tls,
'auth_type' => $this->rc->config->get('managesieve_auth_type'),
'disabled' => $this->rc->config->get('managesieve_disabled_extensions'),
'debug' => $this->rc->config->get('managesieve_debug', false),
'auth_cid' => $this->rc->config->get('managesieve_auth_cid'),
'auth_pw' => $this->rc->config->get('managesieve_auth_pw'),
));
// try to connect to managesieve server and to fetch the script
$this->sieve = new rcube_sieve(
$plugin['user'],
$plugin['password'],
$plugin['host'],
$plugin['port'],
$plugin['auth_type'],
$plugin['usetls'],
$plugin['disabled'],
$plugin['debug'],
$plugin['auth_cid'],
$plugin['auth_pw']
);
if (!($error = $this->sieve->error())) {
// Get list of scripts
$list = $this->list_scripts();
if (!empty($_GET['_set']) || !empty($_POST['_set'])) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
}
else if (!empty($_SESSION['managesieve_current'])) {
$script_name = $_SESSION['managesieve_current'];
}
else {
// get (first) active script
if (!empty($this->active[0])) {
$script_name = $this->active[0];
}
else if ($list) {
$script_name = $list[0];
}
// create a new (initial) script
else {
// if script not exists build default script contents
$script_file = $this->rc->config->get('managesieve_default');
$script_name = $this->rc->config->get('managesieve_script_name');
if (empty($script_name))
$script_name = 'roundcube';
if ($script_file && is_readable($script_file))
$content = file_get_contents($script_file);
// add script and set it active
if ($this->sieve->save_script($script_name, $content)) {
$this->activate_script($script_name);
$this->list[] = $script_name;
}
}
}
if ($script_name) {
$this->sieve->load($script_name);
}
$error = $this->sieve->error();
}
// finally set script objects
if ($error) {
switch ($error) {
case SIEVE_ERROR_CONNECTION:
case SIEVE_ERROR_LOGIN:
$this->rc->output->show_message('managesieve.filterconnerror', 'error');
break;
default:
$this->rc->output->show_message('managesieve.filterunknownerror', 'error');
break;
}
rcube::raise_error(array('code' => 403, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Unable to connect to managesieve on $host:$port"), true, false);
// to disable 'Add filter' button set env variable
$this->rc->output->set_env('filterconnerror', true);
$this->script = array();
}
else {
$this->exts = $this->sieve->get_extensions();
$this->script = $this->sieve->script->as_array();
$this->rc->output->set_env('currentset', $this->sieve->current);
$_SESSION['managesieve_current'] = $this->sieve->current;
}
return $error;
}
function managesieve_actions()
{
$this->init_ui();
$error = $this->managesieve_start();
// Handle user requests
if ($action = rcube_utils::get_input_value('_act', rcube_utils::INPUT_GPC)) {
$fid = (int) rcube_utils::get_input_value('_fid', rcube_utils::INPUT_POST);
if ($action == 'delete' && !$error) {
if (isset($this->script[$fid])) {
if ($this->sieve->script->delete_rule($fid))
$result = $this->save_script();
if ($result === true) {
$this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'del', array('id' => $fid));
} else {
$this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
}
}
}
else if ($action == 'move' && !$error) {
if (isset($this->script[$fid])) {
$to = (int) rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST);
$rule = $this->script[$fid];
// remove rule
unset($this->script[$fid]);
$this->script = array_values($this->script);
// add at target position
if ($to >= count($this->script)) {
$this->script[] = $rule;
}
else {
$script = array();
foreach ($this->script as $idx => $r) {
if ($idx == $to)
$script[] = $rule;
$script[] = $r;
}
$this->script = $script;
}
$this->sieve->script->content = $this->script;
$result = $this->save_script();
if ($result === true) {
$result = $this->list_rules();
$this->rc->output->show_message('managesieve.moved', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'list',
array('list' => $result, 'clear' => true, 'set' => $to));
} else {
$this->rc->output->show_message('managesieve.moveerror', 'error');
}
}
}
else if ($action == 'act' && !$error) {
if (isset($this->script[$fid])) {
$rule = $this->script[$fid];
$disabled = $rule['disabled'] ? true : false;
$rule['disabled'] = !$disabled;
$result = $this->sieve->script->update_rule($fid, $rule);
if ($result !== false)
$result = $this->save_script();
if ($result === true) {
if ($rule['disabled'])
$this->rc->output->show_message('managesieve.deactivated', 'confirmation');
else
$this->rc->output->show_message('managesieve.activated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'update',
array('id' => $fid, 'disabled' => $rule['disabled']));
} else {
if ($rule['disabled'])
$this->rc->output->show_message('managesieve.deactivateerror', 'error');
else
$this->rc->output->show_message('managesieve.activateerror', 'error');
}
}
}
else if ($action == 'setact' && !$error) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
$result = $this->activate_script($script_name);
$kep14 = $this->rc->config->get('managesieve_kolab_master');
if ($result === true) {
$this->rc->output->set_env('active_sets', $this->active);
$this->rc->output->show_message('managesieve.setactivated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setact',
array('name' => $script_name, 'active' => true, 'all' => !$kep14));
} else {
$this->rc->output->show_message('managesieve.setactivateerror', 'error');
}
}
else if ($action == 'deact' && !$error) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
$result = $this->deactivate_script($script_name);
if ($result === true) {
$this->rc->output->set_env('active_sets', $this->active);
$this->rc->output->show_message('managesieve.setdeactivated', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setact',
array('name' => $script_name, 'active' => false));
} else {
$this->rc->output->show_message('managesieve.setdeactivateerror', 'error');
}
}
else if ($action == 'setdel' && !$error) {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
$result = $this->remove_script($script_name);
if ($result === true) {
$this->rc->output->show_message('managesieve.setdeleted', 'confirmation');
$this->rc->output->command('managesieve_updatelist', 'setdel',
array('name' => $script_name));
$this->rc->session->remove('managesieve_current');
} else {
$this->rc->output->show_message('managesieve.setdeleteerror', 'error');
}
}
else if ($action == 'setget') {
$script_name = rcube_utils::get_input_value('_set', rcube_utils::INPUT_GPC, true);
$script = $this->sieve->get_script($script_name);
if (PEAR::isError($script))
exit;
$browser = new rcube_browser;
// send download headers
header("Content-Type: application/octet-stream");
header("Content-Length: ".strlen($script));
if ($browser->ie)
header("Content-Type: application/force-download");
if ($browser->ie && $browser->ver < 7)
$filename = rawurlencode(abbreviate_string($script_name, 55));
else if ($browser->ie)
$filename = rawurlencode($script_name);
else
$filename = addcslashes($script_name, '\\"');
header("Content-Disposition: attachment; filename=\"$filename.txt\"");
echo $script;
exit;
}
else if ($action == 'list') {
$result = $this->list_rules();
$this->rc->output->command('managesieve_updatelist', 'list', array('list' => $result));
}
else if ($action == 'ruleadd') {
$rid = rcube_utils::get_input_value('_rid', rcube_utils::INPUT_GPC);
$id = $this->genid();
$content = $this->rule_div($fid, $id, false);
$this->rc->output->command('managesieve_rulefill', $content, $id, $rid);
}
else if ($action == 'actionadd') {
$aid = rcube_utils::get_input_value('_aid', rcube_utils::INPUT_GPC);
$id = $this->genid();
$content = $this->action_div($fid, $id, false);
$this->rc->output->command('managesieve_actionfill', $content, $id, $aid);
}
$this->rc->output->send();
}
else if ($this->rc->task == 'mail') {
// Initialize the form
$rules = rcube_utils::get_input_value('r', rcube_utils::INPUT_GET);
if (!empty($rules)) {
$i = 0;
foreach ($rules as $rule) {
list($header, $value) = explode(':', $rule, 2);
$tests[$i] = array(
'type' => 'contains',
'test' => 'header',
'arg1' => $header,
'arg2' => $value,
);
$i++;
}
$this->form = array(
'join' => count($tests) > 1 ? 'allof' : 'anyof',
'name' => '',
'tests' => $tests,
'actions' => array(
0 => array('type' => 'fileinto'),
1 => array('type' => 'stop'),
),
);
}
}
$this->managesieve_send();
}
function managesieve_save()
{
// load localization
$this->add_texts('localization/', array('filters','managefilters'));
// include main js script
if ($this->api->output->type == 'html') {
$this->include_script('managesieve.js');
}
// Init plugin and handle managesieve connection
$error = $this->managesieve_start();
// get request size limits (#1488648)
$max_post = max(array(
ini_get('max_input_vars'),
ini_get('suhosin.request.max_vars'),
ini_get('suhosin.post.max_vars'),
));
$max_depth = max(array(
ini_get('suhosin.request.max_array_depth'),
ini_get('suhosin.post.max_array_depth'),
));
// check request size limit
if ($max_post && count($_POST, COUNT_RECURSIVE) >= $max_post) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of max_input_vars/suhosin.request.max_vars/suhosin.post.max_vars)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// check request depth limits
else if ($max_depth && count($_POST['_header']) > $max_depth) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of suhosin.request.max_array_depth/suhosin.post.max_array_depth)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// filters set add action
else if (!empty($_POST['_newset'])) {
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true);
$copy = rcube_utils::get_input_value('_copy', rcube_utils::INPUT_POST, true);
$from = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST);
$exceptions = $this->rc->config->get('managesieve_filename_exceptions');
$kolab = $this->rc->config->get('managesieve_kolab_master');
$name_uc = mb_strtolower($name);
$list = $this->list_scripts();
if (!$name) {
$this->errors['name'] = $this->gettext('cannotbeempty');
}
else if (mb_strlen($name) > 128) {
$this->errors['name'] = $this->gettext('nametoolong');
}
else if (!empty($exceptions) && in_array($name, (array)$exceptions)) {
$this->errors['name'] = $this->gettext('namereserved');
}
else if (!empty($kolab) && in_array($name_uc, array('MASTER', 'USER', 'MANAGEMENT'))) {
$this->errors['name'] = $this->gettext('namereserved');
}
else if (in_array($name, $list)) {
$this->errors['name'] = $this->gettext('setexist');
}
else if ($from == 'file') {
// from file
if (is_uploaded_file($_FILES['_file']['tmp_name'])) {
$file = file_get_contents($_FILES['_file']['tmp_name']);
$file = preg_replace('/\r/', '', $file);
// for security don't save script directly
// check syntax before, like this...
$this->sieve->load_script($file);
if (!$this->save_script($name)) {
$this->errors['file'] = $this->gettext('setcreateerror');
}
}
else { // upload failed
$err = $_FILES['_file']['error'];
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
$msg = $this->rc->gettext(array('name' => 'filesizeerror',
'vars' => array('size' =>
$this->rc->show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
}
else {
$this->errors['file'] = $this->gettext('fileuploaderror');
}
}
}
else if (!$this->sieve->copy($name, $from == 'set' ? $copy : '')) {
$error = 'managesieve.setcreateerror';
}
if (!$error && empty($this->errors)) {
// Find position of the new script on the list
$list[] = $name;
asort($list, SORT_LOCALE_STRING);
$list = array_values($list);
$index = array_search($name, $list);
$this->rc->output->show_message('managesieve.setcreated', 'confirmation');
$this->rc->output->command('parent.managesieve_updatelist', 'setadd',
array('name' => $name, 'index' => $index));
} else if ($msg) {
$this->rc->output->command('display_message', $msg, 'error');
} else if ($error) {
$this->rc->output->show_message($error, 'error');
}
}
// filter add/edit action
else if (isset($_POST['_name'])) {
$name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true));
$fid = trim(rcube_utils::get_input_value('_fid', rcube_utils::INPUT_POST));
$join = trim(rcube_utils::get_input_value('_join', rcube_utils::INPUT_POST));
// and arrays
$headers = rcube_utils::get_input_value('_header', rcube_utils::INPUT_POST);
$cust_headers = rcube_utils::get_input_value('_custom_header', rcube_utils::INPUT_POST);
$ops = rcube_utils::get_input_value('_rule_op', rcube_utils::INPUT_POST);
$sizeops = rcube_utils::get_input_value('_rule_size_op', rcube_utils::INPUT_POST);
$sizeitems = rcube_utils::get_input_value('_rule_size_item', rcube_utils::INPUT_POST);
$sizetargets = rcube_utils::get_input_value('_rule_size_target', rcube_utils::INPUT_POST);
$targets = rcube_utils::get_input_value('_rule_target', rcube_utils::INPUT_POST, true);
$mods = rcube_utils::get_input_value('_rule_mod', rcube_utils::INPUT_POST);
$mod_types = rcube_utils::get_input_value('_rule_mod_type', rcube_utils::INPUT_POST);
$body_trans = rcube_utils::get_input_value('_rule_trans', rcube_utils::INPUT_POST);
$body_types = rcube_utils::get_input_value('_rule_trans_type', rcube_utils::INPUT_POST, true);
$comparators = rcube_utils::get_input_value('_rule_comp', rcube_utils::INPUT_POST);
$act_types = rcube_utils::get_input_value('_action_type', rcube_utils::INPUT_POST, true);
$mailboxes = rcube_utils::get_input_value('_action_mailbox', rcube_utils::INPUT_POST, true);
$act_targets = rcube_utils::get_input_value('_action_target', rcube_utils::INPUT_POST, true);
$area_targets = rcube_utils::get_input_value('_action_target_area', rcube_utils::INPUT_POST, true);
$reasons = rcube_utils::get_input_value('_action_reason', rcube_utils::INPUT_POST, true);
$addresses = rcube_utils::get_input_value('_action_addresses', rcube_utils::INPUT_POST, true);
- $days = rcube_utils::get_input_value('_action_days', rcube_utils::INPUT_POST);
+ $intervals = rcube_utils::get_input_value('_action_interval', rcube_utils::INPUT_POST);
+ $interval_types = rcube_utils::get_input_value('_action_interval_type', rcube_utils::INPUT_POST);
$subject = rcube_utils::get_input_value('_action_subject', rcube_utils::INPUT_POST, true);
$flags = rcube_utils::get_input_value('_action_flags', rcube_utils::INPUT_POST);
$varnames = rcube_utils::get_input_value('_action_varname', rcube_utils::INPUT_POST);
$varvalues = rcube_utils::get_input_value('_action_varvalue', rcube_utils::INPUT_POST);
$varmods = rcube_utils::get_input_value('_action_varmods', rcube_utils::INPUT_POST);
$notifyaddrs = rcube_utils::get_input_value('_action_notifyaddress', rcube_utils::INPUT_POST);
$notifybodies = rcube_utils::get_input_value('_action_notifybody', rcube_utils::INPUT_POST);
$notifymessages = rcube_utils::get_input_value('_action_notifymessage', rcube_utils::INPUT_POST);
$notifyfrom = rcube_utils::get_input_value('_action_notifyfrom', rcube_utils::INPUT_POST);
$notifyimp = rcube_utils::get_input_value('_action_notifyimportance', rcube_utils::INPUT_POST);
// we need a "hack" for radiobuttons
foreach ($sizeitems as $item)
$items[] = $item;
$this->form['disabled'] = $_POST['_disabled'] ? true : false;
$this->form['join'] = $join=='allof' ? true : false;
$this->form['name'] = $name;
$this->form['tests'] = array();
$this->form['actions'] = array();
if ($name == '')
$this->errors['name'] = $this->gettext('cannotbeempty');
else {
foreach($this->script as $idx => $rule)
if($rule['name'] == $name && $idx != $fid) {
$this->errors['name'] = $this->gettext('ruleexist');
break;
}
}
$i = 0;
// rules
if ($join == 'any') {
$this->form['tests'][0]['test'] = 'true';
}
else {
foreach ($headers as $idx => $header) {
$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);
if (preg_match('/^not/', $operator))
$this->form['tests'][$i]['not'] = true;
$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) {
// RFC2822: printable ASCII except colon
if (!preg_match('/^[\x21-\x39\x41-\x7E]+$/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 == '...' ? $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'] = $test;
$this->form['tests'][$i]['arg1'] = $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');
if ($mod) {
$this->form['tests'][$i]['part'] = $mod_type;
}
}
}
if ($header != 'size' && $comparator) {
if (preg_match('/^(value|count)/', $this->form['tests'][$i]['type']))
$comparator = 'i;ascii-numeric';
$this->form['tests'][$i]['comparator'] = $comparator;
}
$i++;
}
}
$i = 0;
// actions
foreach($act_types as $idx => $type) {
$type = $this->strip_value($type);
$target = $this->strip_value($act_targets[$idx]);
switch ($type) {
case 'fileinto':
case 'fileinto_copy':
$mailbox = $this->strip_value($mailboxes[$idx], false, false);
$this->form['actions'][$i]['target'] = $this->mod_mailbox($mailbox, 'in');
if ($type == 'fileinto_copy') {
$type = 'fileinto';
$this->form['actions'][$i]['copy'] = true;
}
break;
case 'reject':
case 'ereject':
$target = $this->strip_value($area_targets[$idx]);
$this->form['actions'][$i]['target'] = str_replace("\r\n", "\n", $target);
// if ($target == '')
// $this->errors['actions'][$i]['targetarea'] = $this->gettext('cannotbeempty');
break;
case 'redirect':
case 'redirect_copy':
$this->form['actions'][$i]['target'] = $target;
if ($this->form['actions'][$i]['target'] == '')
$this->errors['actions'][$i]['target'] = $this->gettext('cannotbeempty');
else if (!rcube_utils::check_email($this->form['actions'][$i]['target']))
$this->errors['actions'][$i]['target'] = $this->gettext('noemailwarning');
if ($type == 'redirect_copy') {
$type = 'redirect';
$this->form['actions'][$i]['copy'] = true;
}
break;
case 'addflag':
case 'setflag':
case 'removeflag':
$_target = array();
if (empty($flags[$idx])) {
$this->errors['actions'][$i]['target'] = $this->gettext('noflagset');
}
else {
foreach ($flags[$idx] as $flag) {
$_target[] = $this->strip_value($flag);
}
}
$this->form['actions'][$i]['target'] = $_target;
break;
case 'vacation':
- $reason = $this->strip_value($reasons[$idx]);
+ $reason = $this->strip_value($reasons[$idx]);
+ $interval_type = $interval_types[$idx] == 'seconds' ? 'seconds' : 'days';
$this->form['actions'][$i]['reason'] = str_replace("\r\n", "\n", $reason);
- $this->form['actions'][$i]['days'] = $days[$idx];
$this->form['actions'][$i]['subject'] = $subject[$idx];
$this->form['actions'][$i]['addresses'] = explode(',', $addresses[$idx]);
+ $this->form['actions'][$i][$interval_type] = $intervals[$idx];
// @TODO: vacation :mime, :from, :handle
if ($this->form['actions'][$i]['addresses']) {
foreach($this->form['actions'][$i]['addresses'] as $aidx => $address) {
$address = trim($address);
if (!$address)
unset($this->form['actions'][$i]['addresses'][$aidx]);
else if(!rcube_utils::check_email($address)) {
$this->errors['actions'][$i]['addresses'] = $this->gettext('noemailwarning');
break;
} else
$this->form['actions'][$i]['addresses'][$aidx] = $address;
}
}
if ($this->form['actions'][$i]['reason'] == '')
$this->errors['actions'][$i]['reason'] = $this->gettext('cannotbeempty');
- if ($this->form['actions'][$i]['days'] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i]['days']))
- $this->errors['actions'][$i]['days'] = $this->gettext('forbiddenchars');
+ if ($this->form['actions'][$i][$interval_type] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i][$interval_type]))
+ $this->errors['actions'][$i]['interval'] = $this->gettext('forbiddenchars');
break;
case 'set':
$this->form['actions'][$i]['name'] = $varnames[$idx];
$this->form['actions'][$i]['value'] = $varvalues[$idx];
foreach ((array)$varmods[$idx] as $v_m) {
$this->form['actions'][$i][$v_m] = true;
}
if (empty($varnames[$idx])) {
$this->errors['actions'][$i]['name'] = $this->gettext('cannotbeempty');
}
else if (!preg_match('/^[0-9a-z_]+$/i', $varnames[$idx])) {
$this->errors['actions'][$i]['name'] = $this->gettext('forbiddenchars');
}
if (!isset($varvalues[$idx]) || $varvalues[$idx] === '') {
$this->errors['actions'][$i]['value'] = $this->gettext('cannotbeempty');
}
break;
case 'notify':
if (empty($notifyaddrs[$idx])) {
$this->errors['actions'][$i]['address'] = $this->gettext('cannotbeempty');
}
else if (!rcube_utils::check_email($notifyaddrs[$idx])) {
$this->errors['actions'][$i]['address'] = $this->gettext('noemailwarning');
}
if (!empty($notifyfrom[$idx]) && !rcube_utils::check_email($notifyfrom[$idx])) {
$this->errors['actions'][$i]['from'] = $this->gettext('noemailwarning');
}
$this->form['actions'][$i]['address'] = $notifyaddrs[$idx];
$this->form['actions'][$i]['body'] = $notifybodies[$idx];
$this->form['actions'][$i]['message'] = $notifymessages[$idx];
$this->form['actions'][$i]['from'] = $notifyfrom[$idx];
$this->form['actions'][$i]['importance'] = $notifyimp[$idx];
break;
}
$this->form['actions'][$i]['type'] = $type;
$i++;
}
if (!$this->errors && !$error) {
// zapis skryptu
if (!isset($this->script[$fid])) {
$fid = $this->sieve->script->add_rule($this->form);
$new = true;
} else
$fid = $this->sieve->script->update_rule($fid, $this->form);
if ($fid !== false)
$save = $this->save_script();
if ($save && $fid !== false) {
$this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
if ($this->rc->task != 'mail') {
$this->rc->output->command('parent.managesieve_updatelist',
isset($new) ? 'add' : 'update',
array(
'name' => rcube::Q($this->form['name']),
'id' => $fid,
'disabled' => $this->form['disabled']
));
}
else {
$this->rc->output->command('managesieve_dialog_close');
$this->rc->output->send('iframe');
}
}
else {
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
// $this->rc->output->send();
}
}
}
$this->managesieve_send();
}
private function managesieve_send()
{
// Handle form action
if (isset($_GET['_framed']) || isset($_POST['_framed'])) {
if (isset($_GET['_newset']) || isset($_POST['_newset'])) {
$this->rc->output->send('managesieve.setedit');
}
else {
$this->rc->output->send('managesieve.filteredit');
}
} else {
$this->rc->output->set_pagetitle($this->gettext('filters'));
$this->rc->output->send('managesieve.managesieve');
}
}
// return the filters list as HTML table
function filters_list($attrib)
{
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmfilterslist';
// define list of cols to be displayed
$a_show_cols = array('name');
$result = $this->list_rules();
// create XHTML table
$out = $this->rc->table_output($attrib, $result, $a_show_cols, 'id');
// set client env
$this->rc->output->add_gui_object('filterslist', $attrib['id']);
$this->rc->output->include_script('list.js');
// add some labels to client
$this->rc->output->add_label('managesieve.filterdeleteconfirm');
return $out;
}
// return the filters list as <SELECT>
function filtersets_list($attrib, $no_env = false)
{
// add id to message list table if not specified
if (!strlen($attrib['id']))
$attrib['id'] = 'rcmfiltersetslist';
$list = $this->list_scripts();
if ($list) {
asort($list, SORT_LOCALE_STRING);
}
if (!empty($attrib['type']) && $attrib['type'] == 'list') {
// define list of cols to be displayed
$a_show_cols = array('name');
if ($list) {
foreach ($list as $idx => $set) {
$scripts['S'.$idx] = $set;
$result[] = array(
'name' => rcube::Q($set),
'id' => 'S'.$idx,
'class' => !in_array($set, $this->active) ? 'disabled' : '',
);
}
}
// create XHTML table
$out = $this->rc->table_output($attrib, $result, $a_show_cols, 'id');
$this->rc->output->set_env('filtersets', $scripts);
$this->rc->output->include_script('list.js');
}
else {
$select = new html_select(array('name' => '_set', 'id' => $attrib['id'],
'onchange' => $this->rc->task != 'mail' ? 'rcmail.managesieve_set()' : ''));
if ($list) {
foreach ($list as $set)
$select->add($set, $set);
}
$out = $select->show($this->sieve->current);
}
// set client env
if (!$no_env) {
$this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
$this->rc->output->add_label('managesieve.setdeleteconfirm');
}
return $out;
}
function filter_frame($attrib)
{
if (!$attrib['id'])
$attrib['id'] = 'rcmfilterframe';
$attrib['name'] = $attrib['id'];
$this->rc->output->set_env('contentframe', $attrib['name']);
$this->rc->output->set_env('blankpage', $attrib['src'] ?
$this->rc->output->abs_url($attrib['src']) : 'program/resources/blank.gif');
return $this->rc->output->frame($attrib);
}
function filterset_form($attrib)
{
if (!$attrib['id'])
$attrib['id'] = 'rcmfiltersetform';
$out = '<form name="filtersetform" action="./" method="post" enctype="multipart/form-data">'."\n";
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
$hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
$hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
$hiddenfields->add(array('name' => '_newset', 'value' => 1));
$out .= $hiddenfields->show();
$name = rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST);
$copy = rcube_utils::get_input_value('_copy', rcube_utils::INPUT_POST);
$selected = rcube_utils::get_input_value('_from', rcube_utils::INPUT_POST);
// filter set name input
$input_name = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30,
'class' => ($this->errors['name'] ? 'error' : '')));
$out .= sprintf('<label for="%s"><b>%s:</b></label> %s<br /><br />',
'_name', rcube::Q($this->gettext('filtersetname')), $input_name->show($name));
$out .="\n<fieldset class=\"itemlist\"><legend>" . $this->gettext('filters') . ":</legend>\n";
$out .= '<input type="radio" id="from_none" name="_from" value="none"'
.(!$selected || $selected=='none' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s</label> ', 'from_none', rcube::Q($this->gettext('none')));
// filters set list
$list = $this->list_scripts();
$select = new html_select(array('name' => '_copy', 'id' => '_copy'));
if (is_array($list)) {
asort($list, SORT_LOCALE_STRING);
if (!$copy)
$copy = $_SESSION['managesieve_current'];
foreach ($list as $set) {
$select->add($set, $set);
}
$out .= '<br /><input type="radio" id="from_set" name="_from" value="set"'
.($selected=='set' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s:</label> ', 'from_set', rcube::Q($this->gettext('fromset')));
$out .= $select->show($copy);
}
// script upload box
$upload = new html_inputfield(array('name' => '_file', 'id' => '_file', 'size' => 30,
'type' => 'file', 'class' => ($this->errors['file'] ? 'error' : '')));
$out .= '<br /><input type="radio" id="from_file" name="_from" value="file"'
.($selected=='file' ? ' checked="checked"' : '').'></input>';
$out .= sprintf('<label for="%s">%s:</label> ', 'from_file', rcube::Q($this->gettext('fromfile')));
$out .= $upload->show();
$out .= '</fieldset>';
$this->rc->output->add_gui_object('sieveform', 'filtersetform');
if ($this->errors['name'])
$this->add_tip('_name', $this->errors['name'], true);
if ($this->errors['file'])
$this->add_tip('_file', $this->errors['file'], true);
$this->print_tips();
return $out;
}
function filter_form($attrib)
{
if (!$attrib['id'])
$attrib['id'] = 'rcmfilterform';
$fid = rcube_utils::get_input_value('_fid', rcube_utils::INPUT_GPC);
$scr = isset($this->form) ? $this->form : $this->script[$fid];
$hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
$hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
$hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
$hiddenfields->add(array('name' => '_fid', 'value' => $fid));
$out = '<form name="filterform" action="./" method="post">'."\n";
$out .= $hiddenfields->show();
// 'any' flag
if (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not'])
$any = true;
// filter name input
$field_id = '_name';
$input_name = new html_inputfield(array('name' => '_name', 'id' => $field_id, 'size' => 30,
'class' => ($this->errors['name'] ? 'error' : '')));
if ($this->errors['name'])
$this->add_tip($field_id, $this->errors['name'], true);
if (isset($scr))
$input_name = $input_name->show($scr['name']);
else
$input_name = $input_name->show();
$out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s\n",
$field_id, rcube::Q($this->gettext('filtername')), $input_name);
// filter set selector
if ($this->rc->task == 'mail') {
$out .= sprintf("\n <label for=\"%s\"><b>%s:</b></label> %s\n",
$field_id, rcube::Q($this->gettext('filterset')),
$this->filtersets_list(array('id' => 'sievescriptname'), true));
}
$out .= '<br /><br /><fieldset><legend>' . rcube::Q($this->gettext('messagesrules')) . "</legend>\n";
// any, allof, anyof radio buttons
$field_id = '_allof';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'allof',
'onclick' => 'rule_join_radio(\'allof\')', 'class' => 'radio'));
if (isset($scr) && !$any)
$input_join = $input_join->show($scr['join'] ? 'allof' : '');
else
$input_join = $input_join->show();
$out .= sprintf("%s<label for=\"%s\">%s</label> \n",
$input_join, $field_id, rcube::Q($this->gettext('filterallof')));
$field_id = '_anyof';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'anyof',
'onclick' => 'rule_join_radio(\'anyof\')', 'class' => 'radio'));
if (isset($scr) && !$any)
$input_join = $input_join->show($scr['join'] ? '' : 'anyof');
else
$input_join = $input_join->show('anyof'); // default
$out .= sprintf("%s<label for=\"%s\">%s</label>\n",
$input_join, $field_id, rcube::Q($this->gettext('filteranyof')));
$field_id = '_any';
$input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'any',
'onclick' => 'rule_join_radio(\'any\')', 'class' => 'radio'));
$input_join = $input_join->show($any ? 'any' : '');
$out .= sprintf("%s<label for=\"%s\">%s</label>\n",
$input_join, $field_id, rcube::Q($this->gettext('filterany')));
$rows_num = isset($scr) ? sizeof($scr['tests']) : 1;
$out .= '<div id="rules"'.($any ? ' style="display: none"' : '').'>';
for ($x=0; $x<$rows_num; $x++)
$out .= $this->rule_div($fid, $x);
$out .= "</div>\n";
$out .= "</fieldset>\n";
// actions
$out .= '<fieldset><legend>' . rcube::Q($this->gettext('messagesactions')) . "</legend>\n";
$rows_num = isset($scr) ? sizeof($scr['actions']) : 1;
$out .= '<div id="actions">';
for ($x=0; $x<$rows_num; $x++)
$out .= $this->action_div($fid, $x);
$out .= "</div>\n";
$out .= "</fieldset>\n";
$this->print_tips();
if ($scr['disabled']) {
$this->rc->output->set_env('rule_disabled', true);
}
$this->rc->output->add_label(
'managesieve.ruledeleteconfirm',
'managesieve.actiondeleteconfirm'
);
$this->rc->output->add_gui_object('sieveform', 'filterform');
return $out;
}
function rule_div($fid, $id, $div=true)
{
$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']);
// 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(rcube::Q($this->gettext($name)), Q($val));
if (in_array('body', $this->exts))
$select_header->add(rcube::Q($this->gettext('body')), 'body');
$select_header->add(rcube::Q($this->gettext('size')), 'size');
$select_header->add(rcube::Q($this->gettext('...')), '...');
// TODO: list arguments
$aout = '';
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)
) {
$aout .= $select_header->show($rule['arg']);
}
else if (isset($rule['test']) && $rule['test'] == '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')
$aout .= $select_header->show('...');
else
$aout .= $select_header->show();
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'];
}
$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="' .rcube::Q($custom). '" size="15" /> </div>' . "\n";
// matching type select (operator)
$select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id,
'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'),
'class' => 'operator_selector',
'onchange' => 'rule_op_select('.$id.')'));
$select_op->add(rcube::Q($this->gettext('filtercontains')), 'contains');
$select_op->add(rcube::Q($this->gettext('filternotcontains')), 'notcontains');
$select_op->add(rcube::Q($this->gettext('filteris')), 'is');
$select_op->add(rcube::Q($this->gettext('filterisnot')), 'notis');
$select_op->add(rcube::Q($this->gettext('filterexists')), 'exists');
$select_op->add(rcube::Q($this->gettext('filternotexists')), 'notexists');
$select_op->add(rcube::Q($this->gettext('filtermatches')), 'matches');
$select_op->add(rcube::Q($this->gettext('filternotmatches')), 'notmatches');
if (in_array('regex', $this->exts)) {
$select_op->add(rcube::Q($this->gettext('filterregex')), 'regex');
$select_op->add(rcube::Q($this->gettext('filternotregex')), 'notregex');
}
if (in_array('relational', $this->exts)) {
$select_op->add(rcube::Q($this->gettext('countisgreaterthan')), 'count-gt');
$select_op->add(rcube::Q($this->gettext('countisgreaterthanequal')), 'count-ge');
$select_op->add(rcube::Q($this->gettext('countislessthan')), 'count-lt');
$select_op->add(rcube::Q($this->gettext('countislessthanequal')), 'count-le');
$select_op->add(rcube::Q($this->gettext('countequals')), 'count-eq');
$select_op->add(rcube::Q($this->gettext('countnotequals')), 'count-ne');
$select_op->add(rcube::Q($this->gettext('valueisgreaterthan')), 'value-gt');
$select_op->add(rcube::Q($this->gettext('valueisgreaterthanequal')), 'value-ge');
$select_op->add(rcube::Q($this->gettext('valueislessthan')), 'value-lt');
$select_op->add(rcube::Q($this->gettext('valueislessthanequal')), 'value-le');
$select_op->add(rcube::Q($this->gettext('valueequals')), 'value-eq');
$select_op->add(rcube::Q($this->gettext('valuenotequals')), 'value-ne');
}
// target input (TODO: lists)
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') {
$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 {
$test = ($rule['not'] ? 'not' : '').$rule['test'];
$target = '';
}
$tout .= $select_op->show($test);
$tout .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
value="' .rcube::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(rcube::Q($this->gettext('filterover')), 'over');
$select_size_op->add(rcube::Q($this->gettext('filterunder')), 'under');
$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" />'.$this->rc->gettext('B').'
- <input type="radio" name="_rule_size_item['.$id.']" value="K"'
- . ($sizeitem=='K' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('KB').'
- <input type="radio" name="_rule_size_item['.$id.']" value="M"'
- . ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('MB').'
- <input type="radio" name="_rule_size_item['.$id.']" value="G"'
- . ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('GB');
+ <label><input type="radio" name="_rule_size_item['.$id.']" value=""'
+ . (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('B').'</label>
+ <label><input type="radio" name="_rule_size_item['.$id.']" value="K"'
+ . ($sizeitem=='K' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('KB').'</label>
+ <label><input type="radio" name="_rule_size_item['.$id.']" value="M"'
+ . ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('MB').'</label>
+ <label><input type="radio" name="_rule_size_item['.$id.']" value="G"'
+ . ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.$this->rc->gettext('GB').'</label>';
$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(rcube::Q($this->gettext('none')), '');
$select_mod->add(rcube::Q($this->gettext('address')), 'address');
if (in_array('envelope', $this->exts))
$select_mod->add(rcube::Q($this->gettext('envelope')), 'envelope');
$select_type = new html_select(array('name' => "_rule_mod_type[]", 'id' => 'rule_mod_type'.$id));
$select_type->add(rcube::Q($this->gettext('allparts')), 'all');
$select_type->add(rcube::Q($this->gettext('domain')), 'domain');
$select_type->add(rcube::Q($this->gettext('localpart')), 'localpart');
if (in_array('subaddress', $this->exts)) {
$select_type->add(rcube::Q($this->gettext('user')), 'user');
$select_type->add(rcube::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 .= rcube::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 .= rcube::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(rcube::Q($this->gettext('text')), 'text');
$select_mod->add(rcube::Q($this->gettext('undecoded')), 'raw');
$select_mod->add(rcube::Q($this->gettext('contenttype')), 'content');
$mout .= '<div id="rule_trans' .$id. '" class="adv" style="display:' . ($rule['test'] == 'body' ? 'block' : 'none') .'">';
$mout .= ' <span>';
$mout .= rcube::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(rcube::Q($this->gettext('default')), '');
$select_comp->add(rcube::Q($this->gettext('octet')), 'i;octet');
$select_comp->add(rcube::Q($this->gettext('asciicasemap')), 'i;ascii-casemap');
if (in_array('comparator-i;ascii-numeric', $this->exts)) {
$select_comp->add(rcube::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 .= rcube::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="'. rcube::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
$out .= '<td class="rowbuttons">';
$out .= '<a href="#" id="ruleadd' . $id .'" title="'. rcube::Q($this->gettext('add')). '"
onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button add"></a>';
$out .= '<a href="#" id="ruledel' . $id .'" title="'. rcube::Q($this->gettext('del')). '"
onclick="rcmail.managesieve_ruledel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
$out .= '</td>';
$out .= '</tr></table>';
$out .= $div ? "</div>\n" : '';
return $out;
}
function action_div($fid, $id, $div=true)
{
$action = isset($this->form) ? $this->form['actions'][$id] : $this->script[$fid]['actions'][$id];
$rows_num = isset($this->form) ? sizeof($this->form['actions']) : sizeof($this->script[$fid]['actions']);
$out = $div ? '<div class="actionrow" id="actionrow' .$id .'">'."\n" : '';
$out .= '<table><tr><td class="rowactions">';
// action select
$select_action = new html_select(array('name' => "_action_type[$id]", 'id' => 'action_type'.$id,
'onchange' => 'action_type_select(' .$id .')'));
if (in_array('fileinto', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagemoveto')), 'fileinto');
if (in_array('fileinto', $this->exts) && in_array('copy', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagecopyto')), 'fileinto_copy');
$select_action->add(rcube::Q($this->gettext('messageredirect')), 'redirect');
if (in_array('copy', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagesendcopy')), 'redirect_copy');
if (in_array('reject', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagediscard')), 'reject');
else if (in_array('ereject', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagediscard')), 'ereject');
if (in_array('vacation', $this->exts))
$select_action->add(rcube::Q($this->gettext('messagereply')), 'vacation');
$select_action->add(rcube::Q($this->gettext('messagedelete')), 'discard');
if (in_array('imapflags', $this->exts) || in_array('imap4flags', $this->exts)) {
$select_action->add(rcube::Q($this->gettext('setflags')), 'setflag');
$select_action->add(rcube::Q($this->gettext('addflags')), 'addflag');
$select_action->add(rcube::Q($this->gettext('removeflags')), 'removeflag');
}
if (in_array('variables', $this->exts)) {
$select_action->add(rcube::Q($this->gettext('setvariable')), 'set');
}
if (in_array('enotify', $this->exts) || in_array('notify', $this->exts)) {
$select_action->add(rcube::Q($this->gettext('notify')), 'notify');
}
$select_action->add(rcube::Q($this->gettext('rulestop')), 'stop');
$select_type = $action['type'];
if (in_array($action['type'], array('fileinto', 'redirect')) && $action['copy']) {
$select_type .= '_copy';
}
$out .= $select_action->show($select_type);
$out .= '</td>';
// actions target inputs
$out .= '<td class="rowtargets">';
// shared targets
$out .= '<input type="text" name="_action_target['.$id.']" id="action_target' .$id. '" '
.'value="' .($action['type']=='redirect' ? rcube::Q($action['target'], 'strict', false) : ''). '" size="35" '
.'style="display:' .($action['type']=='redirect' ? 'inline' : 'none') .'" '
. $this->error_class($id, 'action', 'target', 'action_target') .' />';
$out .= '<textarea name="_action_target_area['.$id.']" id="action_target_area' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'targetarea', 'action_target_area')
.'style="display:' .(in_array($action['type'], array('reject', 'ereject')) ? 'inline' : 'none') .'">'
. (in_array($action['type'], array('reject', 'ereject')) ? rcube::Q($action['target'], 'strict', false) : '')
. "</textarea>\n";
// vacation
+ $vsec = in_array('vacation-seconds', $this->exts);
$out .= '<div id="action_vacation' .$id.'" style="display:' .($action['type']=='vacation' ? 'inline' : 'none') .'">';
$out .= '<span class="label">'. rcube::Q($this->gettext('vacationreason')) .'</span><br />'
.'<textarea name="_action_reason['.$id.']" id="action_reason' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'reason', 'action_reason') . '>'
. Q($action['reason'], 'strict', false) . "</textarea>\n";
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('vacationsubject')) . '</span><br />'
.'<input type="text" name="_action_subject['.$id.']" id="action_subject'.$id.'" '
.'value="' . (is_array($action['subject']) ? rcube::Q(implode(', ', $action['subject']), 'strict', false) : $action['subject']) . '" size="35" '
. $this->error_class($id, 'action', 'subject', 'action_subject') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('vacationaddresses')) . '</span><br />'
.'<input type="text" name="_action_addresses['.$id.']" id="action_addr'.$id.'" '
.'value="' . (is_array($action['addresses']) ? rcube::Q(implode(', ', $action['addresses']), 'strict', false) : $action['addresses']) . '" size="35" '
. $this->error_class($id, 'action', 'addresses', 'action_addr') .' />';
- $out .= '<br /><span class="label">' . rcube::Q($this->gettext('vacationdays')) . '</span><br />'
- .'<input type="text" name="_action_days['.$id.']" id="action_days'.$id.'" '
- .'value="' .rcube::Q($action['days'], 'strict', false) . '" size="2" '
- . $this->error_class($id, 'action', 'days', 'action_days') .' />';
+ $out .= '<br /><span class="label">' . rcube::Q($this->gettext($vsec ? 'vacationinterval' : 'vacationdays')) . '</span><br />'
+ .'<input type="text" name="_action_interval['.$id.']" id="action_interval'.$id.'" '
+ .'value="' .rcube::Q(isset($action['seconds']) ? $action['seconds'] : $action['days'], 'strict', false) . '" size="2" '
+ . $this->error_class($id, 'action', 'interval', 'action_interval') .' />';
+ if ($vsec) {
+ $out .= ' <label><input type="radio" name="_action_interval_type['.$id.']" value="days"'
+ . (!isset($action['seconds']) ? ' checked="checked"' : '') .' class="radio" />'.$this->gettext('days').'</label>'
+ . ' <label><input type="radio" name="_action_interval_type['.$id.']" value="seconds"'
+ . (isset($action['seconds']) ? ' checked="checked"' : '') .' class="radio" />'.$this->gettext('seconds').'</label>';
+ }
$out .= '</div>';
// flags
$flags = array(
'read' => '\\Seen',
'answered' => '\\Answered',
'flagged' => '\\Flagged',
'deleted' => '\\Deleted',
'draft' => '\\Draft',
);
$flags_target = (array)$action['target'];
$out .= '<div id="action_flags' .$id.'" style="display:'
. (preg_match('/^(set|add|remove)flag$/', $action['type']) ? 'inline' : 'none') . '"'
. $this->error_class($id, 'action', 'flags', 'action_flags') . '>';
foreach ($flags as $fidx => $flag) {
$out .= '<input type="checkbox" name="_action_flags[' .$id .'][]" value="' . $flag . '"'
. (in_array_nocase($flag, $flags_target) ? 'checked="checked"' : '') . ' />'
. rcube::Q($this->gettext('flag'.$fidx)) .'<br>';
}
$out .= '</div>';
// set variable
$set_modifiers = array(
'lower',
'upper',
'lowerfirst',
'upperfirst',
'quotewildcard',
'length'
);
$out .= '<div id="action_set' .$id.'" style="display:' .($action['type']=='set' ? 'inline' : 'none') .'">';
$out .= '<span class="label">' .rcube::Q($this->gettext('setvarname')) . '</span><br />'
.'<input type="text" name="_action_varname['.$id.']" id="action_varname'.$id.'" '
.'value="' . rcube::Q($action['name']) . '" size="35" '
. $this->error_class($id, 'action', 'name', 'action_varname') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('setvarvalue')) . '</span><br />'
.'<input type="text" name="_action_varvalue['.$id.']" id="action_varvalue'.$id.'" '
.'value="' . rcube::Q($action['value']) . '" size="35" '
. $this->error_class($id, 'action', 'value', 'action_varvalue') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('setvarmodifiers')) . '</span><br />';
foreach ($set_modifiers as $j => $s_m) {
$s_m_id = 'action_varmods' . $id . $s_m;
$out .= sprintf('<input type="checkbox" name="_action_varmods[%s][]" value="%s" id="%s"%s />%s<br>',
$id, $s_m, $s_m_id,
(array_key_exists($s_m, (array)$action) && $action[$s_m] ? ' checked="checked"' : ''),
rcube::Q($this->gettext('var' . $s_m)));
}
$out .= '</div>';
// notify
// skip :options tag - not used by the mailto method
$out .= '<div id="action_notify' .$id.'" style="display:' .($action['type']=='notify' ? 'inline' : 'none') .'">';
$out .= '<span class="label">' .rcube::Q($this->gettext('notifyaddress')) . '</span><br />'
.'<input type="text" name="_action_notifyaddress['.$id.']" id="action_notifyaddress'.$id.'" '
.'value="' . rcube::Q($action['address']) . '" size="35" '
. $this->error_class($id, 'action', 'address', 'action_notifyaddress') .' />';
$out .= '<br /><span class="label">'. rcube::Q($this->gettext('notifybody')) .'</span><br />'
.'<textarea name="_action_notifybody['.$id.']" id="action_notifybody' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'method', 'action_notifybody') . '>'
. rcube::Q($action['body'], 'strict', false) . "</textarea>\n";
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('notifysubject')) . '</span><br />'
.'<input type="text" name="_action_notifymessage['.$id.']" id="action_notifymessage'.$id.'" '
.'value="' . rcube::Q($action['message']) . '" size="35" '
. $this->error_class($id, 'action', 'message', 'action_notifymessage') .' />';
$out .= '<br /><span class="label">' .rcube::Q($this->gettext('notifyfrom')) . '</span><br />'
.'<input type="text" name="_action_notifyfrom['.$id.']" id="action_notifyfrom'.$id.'" '
.'value="' . rcube::Q($action['from']) . '" size="35" '
. $this->error_class($id, 'action', 'from', 'action_notifyfrom') .' />';
$importance_options = array(
3 => 'notifyimportancelow',
2 => 'notifyimportancenormal',
1 => 'notifyimportancehigh'
);
$select_importance = new html_select(array(
'name' => '_action_notifyimportance[' . $id . ']',
'id' => '_action_notifyimportance' . $id,
'class' => $this->error_class($id, 'action', 'importance', 'action_notifyimportance')));
foreach ($importance_options as $io_v => $io_n) {
$select_importance->add(rcube::Q($this->gettext($io_n)), $io_v);
}
$out .= '<br /><span class="label">' . rcube::Q($this->gettext('notifyimportance')) . '</span><br />';
$out .= $select_importance->show($action['importance'] ? $action['importance'] : 2);
$out .= '</div>';
// mailbox select
if ($action['type'] == 'fileinto')
$mailbox = $this->mod_mailbox($action['target'], 'out');
else
$mailbox = '';
$select = $this->rc->folder_selector(array(
'realnames' => false,
'maxlength' => 100,
'id' => 'action_mailbox' . $id,
'name' => "_action_mailbox[$id]",
'style' => 'display:'.(!isset($action) || $action['type']=='fileinto' ? 'inline' : 'none')
));
$out .= $select->show($mailbox);
$out .= '</td>';
// add/del buttons
$out .= '<td class="rowbuttons">';
$out .= '<a href="#" id="actionadd' . $id .'" title="'. rcube::Q($this->gettext('add')). '"
onclick="rcmail.managesieve_actionadd(' . $id .')" class="button add"></a>';
$out .= '<a href="#" id="actiondel' . $id .'" title="'. rcube::Q($this->gettext('del')). '"
onclick="rcmail.managesieve_actiondel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
$out .= '</td>';
$out .= '</tr></table>';
$out .= $div ? "</div>\n" : '';
return $out;
}
private function genid()
{
return preg_replace('/[^0-9]/', '', microtime(true));
}
private function strip_value($str, $allow_html = false, $trim = true)
{
if (!$allow_html) {
$str = strip_tags($str);
}
return $trim ? trim($str) : $str;
}
private function error_class($id, $type, $target, $elem_prefix='')
{
// TODO: tooltips
if (($type == 'test' && ($str = $this->errors['tests'][$id][$target])) ||
($type == 'action' && ($str = $this->errors['actions'][$id][$target]))
) {
$this->add_tip($elem_prefix.$id, $str, true);
return ' class="error"';
}
return '';
}
private function add_tip($id, $str, $error=false)
{
if ($error)
$str = html::span('sieve error', $str);
$this->tips[] = array($id, $str);
}
private function print_tips()
{
if (empty($this->tips))
return;
$script = rcmail_output::JS_OBJECT_NAME.'.managesieve_tip_register('.json_encode($this->tips).');';
$this->rc->output->add_script($script, 'foot');
}
/**
* Converts mailbox name from/to UTF7-IMAP from/to internal Sieve encoding
* with delimiter replacement.
*
* @param string $mailbox Mailbox name
* @param string $mode Conversion direction ('in'|'out')
*
* @return string Mailbox name
*/
private function mod_mailbox($mailbox, $mode = 'out')
{
$delimiter = $_SESSION['imap_delimiter'];
$replace_delimiter = $this->rc->config->get('managesieve_replace_delimiter');
$mbox_encoding = $this->rc->config->get('managesieve_mbox_encoding', 'UTF7-IMAP');
if ($mode == 'out') {
$mailbox = rcube_charset::convert($mailbox, $mbox_encoding, 'UTF7-IMAP');
if ($replace_delimiter && $replace_delimiter != $delimiter)
$mailbox = str_replace($replace_delimiter, $delimiter, $mailbox);
}
else {
$mailbox = rcube_charset::convert($mailbox, 'UTF7-IMAP', $mbox_encoding);
if ($replace_delimiter && $replace_delimiter != $delimiter)
$mailbox = str_replace($delimiter, $replace_delimiter, $mailbox);
}
return $mailbox;
}
/**
* List sieve scripts
*
* @return array Scripts list
*/
public function list_scripts()
{
if ($this->list !== null) {
return $this->list;
}
$this->list = $this->sieve->get_scripts();
// Handle active script(s) and list of scripts according to Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
// Skip protected names
foreach ((array)$this->list as $idx => $name) {
$_name = strtoupper($name);
if ($_name == 'MASTER')
$master_script = $name;
else if ($_name == 'MANAGEMENT')
$management_script = $name;
else if($_name == 'USER')
$user_script = $name;
else
continue;
unset($this->list[$idx]);
}
// get active script(s), read USER script
if ($user_script) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$filename_regex = '/'.preg_quote($extension, '/').'$/';
$_SESSION['managesieve_user_script'] = $user_script;
$this->sieve->load($user_script);
foreach ($this->sieve->script->as_array() as $rules) {
foreach ($rules['actions'] as $action) {
if ($action['type'] == 'include' && empty($action['global'])) {
$name = preg_replace($filename_regex, '', $action['target']);
$this->active[] = $name;
}
}
}
}
// create USER script if it doesn't exist
else {
$content = "# USER Management Script\n"
."#\n"
."# This script includes the various active sieve scripts\n"
."# it is AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY!\n"
."#\n"
."# For more information, see http://wiki.kolab.org/KEP:14#USER\n"
."#\n";
if ($this->sieve->save_script('USER', $content)) {
$_SESSION['managesieve_user_script'] = 'USER';
if (empty($this->master_file))
$this->sieve->activate('USER');
}
}
}
else if (!empty($this->list)) {
// Get active script name
if ($active = $this->sieve->get_active()) {
$this->active = array($active);
}
// Hide scripts from config
$exceptions = $this->rc->config->get('managesieve_filename_exceptions');
if (!empty($exceptions)) {
$this->list = array_diff($this->list, (array)$exceptions);
}
}
return $this->list;
}
/**
* Removes sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function remove_script($name)
{
$result = $this->sieve->remove($name);
// Kolab's KEP:14
if ($result && $this->rc->config->get('managesieve_kolab_master')) {
$this->deactivate_script($name);
}
return $result;
}
/**
* Activates sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function activate_script($name)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$user_script = $_SESSION['managesieve_user_script'];
// if the script is not active...
if ($user_script && ($key = array_search($name, $this->active)) === false) {
// ...rewrite USER file adding appropriate include command
if ($this->sieve->load($user_script)) {
$script = $this->sieve->script->as_array();
$list = array();
$regexp = '/' . preg_quote($extension, '/') . '$/';
// Create new include entry
$rule = array(
'actions' => array(
0 => array(
'target' => $name.$extension,
'type' => 'include',
'personal' => true,
)));
// get all active scripts for sorting
foreach ($script as $rid => $rules) {
foreach ($rules['actions'] as $aid => $action) {
if ($action['type'] == 'include' && empty($action['global'])) {
$target = $extension ? preg_replace($regexp, '', $action['target']) : $action['target'];
$list[] = $target;
}
}
}
$list[] = $name;
// Sort and find current script position
asort($list, SORT_LOCALE_STRING);
$list = array_values($list);
$index = array_search($name, $list);
// add rule at the end of the script
if ($index === false || $index == count($list)-1) {
$this->sieve->script->add_rule($rule);
}
// add rule at index position
else {
$script2 = array();
foreach ($script as $rid => $rules) {
if ($rid == $index) {
$script2[] = $rule;
}
$script2[] = $rules;
}
$this->sieve->script->content = $script2;
}
$result = $this->sieve->save();
if ($result) {
$this->active[] = $name;
}
}
}
}
else {
$result = $this->sieve->activate($name);
if ($result)
$this->active = array($name);
}
return $result;
}
/**
* Deactivates sieve script
*
* @param string $name Script name
*
* @return bool True on success, False on failure
*/
public function deactivate_script($name)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
$user_script = $_SESSION['managesieve_user_script'];
// if the script is active...
if ($user_script && ($key = array_search($name, $this->active)) !== false) {
// ...rewrite USER file removing appropriate include command
if ($this->sieve->load($user_script)) {
$script = $this->sieve->script->as_array();
$name = $name.$extension;
foreach ($script as $rid => $rules) {
foreach ($rules['actions'] as $aid => $action) {
if ($action['type'] == 'include' && empty($action['global'])
&& $action['target'] == $name
) {
break 2;
}
}
}
// Entry found
if ($rid < count($script)) {
$this->sieve->script->delete_rule($rid);
$result = $this->sieve->save();
if ($result) {
unset($this->active[$key]);
}
}
}
}
}
else {
$result = $this->sieve->deactivate();
if ($result)
$this->active = array();
}
return $result;
}
/**
* Saves current script (adding some variables)
*/
public function save_script($name = null)
{
// Kolab's KEP:14
if ($this->rc->config->get('managesieve_kolab_master')) {
$this->sieve->script->set_var('EDITOR', self::PROGNAME);
$this->sieve->script->set_var('EDITOR_VERSION', self::VERSION);
}
return $this->sieve->save($name);
}
/**
* Returns list of rules from the current script
*
* @return array List of rules
*/
public function list_rules()
{
$result = array();
$i = 1;
foreach ($this->script as $idx => $filter) {
if ($filter['type'] != 'if') {
continue;
}
$fname = $filter['name'] ? $filter['name'] : "#$i";
$result[] = array(
'id' => $idx,
'name' => rcube::Q($fname),
'class' => $filter['disabled'] ? 'disabled' : '',
);
$i++;
}
return $result;
}
}
diff --git a/plugins/managesieve/skins/classic/managesieve.css b/plugins/managesieve/skins/classic/managesieve.css
index 9527b44e9..86a0d197d 100644
--- a/plugins/managesieve/skins/classic/managesieve.css
+++ b/plugins/managesieve/skins/classic/managesieve.css
@@ -1,317 +1,322 @@
#filtersetslistbox
{
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 195px;
border: 1px solid #999999;
background-color: #F9F9F9;
overflow: hidden;
/* css hack for IE */
height: expression(parseInt(this.parentNode.offsetHeight)+'px');
}
#filtersscreen
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 205px;
/* css hack for IE */
height: expression(parseInt(this.parentNode.offsetHeight)+'px');
}
#filterslistbox
{
position: absolute;
left: 0;
top: 0;
bottom: 0;
border: 1px solid #999999;
overflow: auto;
/* css hack for IE */
height: expression(parseInt(this.parentNode.offsetHeight)+'px');
}
#filterslist,
#filtersetslist
{
width: 100%;
table-layout: fixed;
}
#filterslist tbody td,
#filtersetslist tbody td
{
cursor: default;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
#filterslist tbody tr.disabled td,
#filtersetslist tbody tr.disabled td
{
color: #999999;
}
#filtersetslist tbody td
{
font-weight: bold;
}
/*
#filtersetslist tr.selected
{
background-color: #929292;
border-bottom: 1px solid #898989;
color: #FFF;
font-weight: bold;
}
*/
#filterslist tbody tr.filtermoveup td
{
border-top: 2px dotted #555;
padding-top: 0px;
}
#filterslist tbody tr.filtermovedown td
{
border-bottom: 2px dotted #555;
padding-bottom: 1px;
}
#filter-box
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
border: 1px solid #999999;
overflow: hidden;
/* css hack for IE */
width: expression((parseInt(this.parentNode.offsetWidth)-20-parseInt(document.getElementById('filterslistbox').offsetWidth))+'px');
height: expression(parseInt(this.parentNode.offsetHeight)+'px');
}
#filter-frame
{
border: none;
}
body.iframe
{
min-width: 620px;
width: expression(Math.max(620, document.documentElement.clientWidth)+'px');
background-color: #F2F2F2;
}
#filter-form
{
min-width: 550px;
width: expression(Math.max(550, document.documentElement.clientWidth)+'px');
white-space: nowrap;
padding: 20px 10px 10px 10px;
}
legend, label
{
color: #666666;
}
#rules, #actions
{
margin-top: 5px;
padding: 0;
border-collapse: collapse;
}
div.rulerow, div.actionrow
{
width: auto;
padding: 2px;
white-space: nowrap;
border: 1px solid #F2F2F2;
}
div.rulerow:hover, div.actionrow:hover
{
padding: 2px;
white-space: nowrap;
background: #F9F9F9;
border: 1px solid silver;
}
div.rulerow table, div.actionrow table
{
padding: 0px;
min-width: 600px;
width: expression(Math.max(600, document.documentElement.clientWidth)+'px');
}
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;
white-space: nowrap;
width: 1%;
}
td.rowactions
{
white-space: nowrap;
width: 1%;
padding-top: 2px;
}
td.rowtargets
{
white-space: nowrap;
width: 98%;
padding-left: 3px;
padding-top: 2px;
}
td.rowtargets div.adv
{
padding-top: 3px;
}
input.disabled, input.disabled:hover
{
color: #999999;
}
input.error, textarea.error
{
background-color: #FFFF88;
}
input.box,
input.radio
{
border: 0;
margin-top: 0;
}
select.operator_selector
{
width: 200px;
}
td.rowtargets span,
span.label
{
color: #666666;
font-size: 10px;
white-space: nowrap;
}
+td.rowtargets label
+{
+ color: black;
+}
+
#footer
{
padding-top: 5px;
width: 100%;
}
#footer .footerleft
{
padding-left: 2px;
white-space: nowrap;
float: left;
}
#footer .footerright
{
padding-right: 2px;
white-space: nowrap;
text-align: right;
float: right;
}
.itemlist
{
line-height: 25px;
}
.itemlist input
{
vertical-align: middle;
}
span.sieve.error
{
color: red;
}
a.button.add
{
background: url(images/add.png) no-repeat;
width: 30px;
height: 20px;
margin-right: 4px;
display: inline-block;
}
a.button.del
{
background: url(images/del.png) no-repeat;
width: 30px;
height: 20px;
display: inline-block;
}
a.button.disabled
{
opacity: 0.35;
filter: alpha(opacity=35);
cursor: default;
}
#filter-form select,
#filter-form input,
#filter-form textarea
{
font-size: 11px;
}
/* fixes for popup window */
body.iframe.mail
{
margin: 0;
padding: 0;
}
body.iframe.mail #filter-form
{
padding: 10px 5px 5px 5px;
}
diff --git a/plugins/managesieve/skins/larry/managesieve.css b/plugins/managesieve/skins/larry/managesieve.css
index 49ebe007f..099f05f0d 100644
--- a/plugins/managesieve/skins/larry/managesieve.css
+++ b/plugins/managesieve/skins/larry/managesieve.css
@@ -1,310 +1,320 @@
#filtersetslistbox
{
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 150px;
}
#filtersscreen
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 162px;
}
#filterslistbox
{
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 180px;
}
#filter-box
{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 192px;
}
#filter-frame
{
border-radius: 4px;
}
#filterslist,
#filtersetslist
{
width: 100%;
table-layout: fixed;
}
#filterslist tbody td,
#filtersetslist tbody td
{
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
}
#filterslist tbody tr.disabled td,
#filtersetslist tbody tr.disabled td
{
color: #87A3AA;
}
#filtersetslist tbody td
{
font-weight: bold;
}
#filterslist tbody tr.filtermoveup td
{
border-top: 2px dotted #555;
padding-top: 5px;
}
#filterslist tbody tr.filtermovedown td
{
border-bottom: 2px dotted #555;
padding-bottom: 4px;
}
body.iframe
{
min-width: 620px;
}
#filter-form
{
min-width: 550px;
white-space: nowrap;
padding: 20px 10px 10px 10px;
}
legend, label
{
color: #666666;
}
#rules, #actions
{
margin-top: 5px;
padding: 0;
border-collapse: collapse;
}
div.rulerow, div.actionrow
{
width: auto;
padding: 2px;
white-space: nowrap;
border: 1px solid white;
}
div.rulerow:hover, div.actionrow:hover
{
padding: 2px;
white-space: nowrap;
background-color: #D9ECF4;
border: 1px solid #BBD3DA;
border-radius: 4px;
}
div.rulerow table, div.actionrow table
{
padding: 0px;
min-width: 600px;
}
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;
white-space: nowrap;
width: 1%;
}
td.rowactions
{
white-space: nowrap;
width: 1%;
padding-top: 2px;
}
td.rowtargets
{
white-space: nowrap;
width: 98%;
padding-left: 3px;
padding-top: 2px;
}
td.rowtargets div.adv
{
padding-top: 3px;
}
input.disabled, input.disabled:hover
{
color: #999999;
}
input.error, textarea.error
{
background-color: #FFFFC4;
}
input.box,
input.radio
{
border: 0;
margin-top: 0;
}
+input.radio
+{
+ vertical-align: middle;
+}
+
select.operator_selector
{
width: 200px;
}
td.rowtargets span,
span.label
{
color: #666666;
font-size: 10px;
white-space: nowrap;
}
+td.rowtargets label
+{
+ color: black;
+}
+
#footer
{
padding-top: 5px;
width: 100%;
}
#footer .footerleft label
{
margin-left: 40px;
white-space: nowrap;
}
.itemlist
{
line-height: 25px;
}
.itemlist input
{
vertical-align: middle;
}
span.sieve.error
{
color: red;
white-space: nowrap;
}
#managesieve-tip
{
padding: 3px;
background-color: #eee;
}
a.button
{
margin: 0;
padding: 0;
}
a.button.add
{
background: url(images/add.png) no-repeat;
width: 30px;
height: 20px;
margin-right: 4px;
display: inline-block;
}
a.button.del
{
background: url(images/del.png) no-repeat;
width: 30px;
height: 20px;
display: inline-block;
}
a.button.disabled
{
opacity: 0.35;
filter: alpha(opacity=35);
cursor: default;
}
#filter-form select,
#filter-form input,
#filter-form textarea
{
font-size: 11px;
padding: 1px;
}
/* revert larry style button */
#filter-form input.button
{
padding-bottom: 2px;
padding-left: 5px;
padding-right: 5px;
padding-top: 2px;
}
fieldset
{
border-radius: 4px;
}
/* fixes for popup window */
body.iframe.mail
{
margin: 0;
padding: 0;
}
body.iframe.mail #filter-form
{
padding: 10px 5px 5px 5px;
}
diff --git a/plugins/managesieve/tests/src/parser_vacation_seconds b/plugins/managesieve/tests/src/parser_vacation_seconds
new file mode 100644
index 000000000..75cbcae46
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_vacation_seconds
@@ -0,0 +1,12 @@
+require ["vacation-seconds"];
+# rule:[test-vacation]
+if header :contains "Subject" "vacation"
+{
+ vacation :seconds 0 text:
+# test
+test test /* test */
+test
+.
+;
+ stop;
+}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Mar 1, 5:54 AM (1 d, 20 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
166024
Default Alt Text
(177 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment