plugins/managesieve/Changelog | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/Roundcube/rcube_sieve.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php | ●●●●● patch | view | raw | blame | history | |
plugins/managesieve/managesieve.php | ●●●●● patch | view | raw | blame | history |
plugins/managesieve/Changelog
@@ -1,3 +1,4 @@ - Added simple API to manage vacation rule - Fix missing css/js scripts in filter form in mail task - Fix default vacation status (#1490019) - Make possible to set vacation start/end date and time plugins/managesieve/lib/Roundcube/rcube_sieve.php
@@ -22,17 +22,6 @@ // 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 @@ -42,6 +31,16 @@ public $script; // rcube_sieve_script object public $current; // name of currently loaded script private $exts; // array of supported extensions const ERROR_CONNECTION = 1; const ERROR_LOGIN = 2; const ERROR_NOT_EXISTS = 3; // script not exists const ERROR_INSTALL = 4; // script installation const ERROR_ACTIVATE = 5; // script activation const ERROR_DELETE = 6; // script deletion const ERROR_INTERNAL = 7; // internal error const ERROR_DEACTIVATE = 8; // script activation const ERROR_OTHER = 255; // other/unknown error /** @@ -70,7 +69,7 @@ } if (PEAR::isError($this->sieve->connect($host, $port, $options, $usetls))) { return $this->_set_error(SIEVE_ERROR_CONNECTION); return $this->_set_error(self::ERROR_CONNECTION); } if (!empty($auth_cid)) { @@ -82,7 +81,7 @@ if (PEAR::isError($this->sieve->login($username, $password, $auth_type ? strtoupper($auth_type) : null, $authz)) ) { return $this->_set_error(SIEVE_ERROR_LOGIN); return $this->_set_error(self::ERROR_LOGIN); } $this->exts = $this->get_extensions(); @@ -117,10 +116,10 @@ public function save($name = null) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (!$this->script) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (!$name) $name = $this->current; @@ -131,7 +130,7 @@ $script = '/* empty script */'; if (PEAR::isError($this->sieve->installScript($name, $script))) return $this->_set_error(SIEVE_ERROR_INSTALL); return $this->_set_error(self::ERROR_INSTALL); return true; } @@ -142,13 +141,13 @@ public function save_script($name, $content = null) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (!$content) $content = '/* empty script */'; if (PEAR::isError($this->sieve->installScript($name, $content))) return $this->_set_error(SIEVE_ERROR_INSTALL); return $this->_set_error(self::ERROR_INSTALL); return true; } @@ -159,13 +158,13 @@ public function activate($name = null) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (!$name) $name = $this->current; if (PEAR::isError($this->sieve->setActive($name))) return $this->_set_error(SIEVE_ERROR_ACTIVATE); return $this->_set_error(self::ERROR_ACTIVATE); return true; } @@ -176,10 +175,10 @@ public function deactivate() { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (PEAR::isError($this->sieve->setActive(''))) return $this->_set_error(SIEVE_ERROR_DEACTIVATE); return $this->_set_error(self::ERROR_DEACTIVATE); return true; } @@ -190,7 +189,7 @@ public function remove($name = null) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if (!$name) $name = $this->current; @@ -198,10 +197,10 @@ // script must be deactivated first if ($name == $this->sieve->getActive()) if (PEAR::isError($this->sieve->setActive(''))) return $this->_set_error(SIEVE_ERROR_DELETE); return $this->_set_error(self::ERROR_DELETE); if (PEAR::isError($this->sieve->removeScript($name))) return $this->_set_error(SIEVE_ERROR_DELETE); return $this->_set_error(self::ERROR_DELETE); if ($name == $this->current) $this->current = null; @@ -218,9 +217,14 @@ return $this->exts; if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); $ext = $this->sieve->getExtensions(); if (PEAR::isError($ext)) { return array(); } // we're working on lower-cased names $ext = array_map('strtolower', (array) $ext); @@ -242,12 +246,12 @@ if (!$this->list) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); $list = $this->sieve->listScripts(); if (PEAR::isError($list)) return $this->_set_error(SIEVE_ERROR_OTHER); return $this->_set_error(self::ERROR_OTHER); $this->list = $list; } @@ -261,7 +265,7 @@ public function get_active() { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); return $this->sieve->getActive(); } @@ -272,7 +276,7 @@ public function load($name) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if ($this->current == $name) return true; @@ -280,7 +284,7 @@ $script = $this->sieve->getScript($name); if (PEAR::isError($script)) return $this->_set_error(SIEVE_ERROR_OTHER); return $this->_set_error(self::ERROR_OTHER); // try to parse from Roundcube format $this->script = $this->_parse($script); @@ -296,7 +300,7 @@ public function load_script($script) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); // try to parse from Roundcube format $this->script = $this->_parse($script); @@ -341,12 +345,12 @@ public function get_script($name) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); $content = $this->sieve->getScript($name); if (PEAR::isError($content)) return $this->_set_error(SIEVE_ERROR_OTHER); return $this->_set_error(self::ERROR_OTHER); return $content; } @@ -357,13 +361,13 @@ public function copy($name, $copy) { if (!$this->sieve) return $this->_set_error(SIEVE_ERROR_INTERNAL); return $this->_set_error(self::ERROR_INTERNAL); if ($copy) { $content = $this->sieve->getScript($copy); if (PEAR::isError($content)) return $this->_set_error(SIEVE_ERROR_OTHER); return $this->_set_error(self::ERROR_OTHER); } return $this->save_script($name, $content); plugins/managesieve/lib/Roundcube/rcube_sieve_engine.php
@@ -73,7 +73,7 @@ */ function __construct($plugin) { $this->rc = rcmail::get_instance(); $this->rc = rcube::get_instance(); $this->plugin = $plugin; } @@ -91,56 +91,11 @@ 'filtersetform' => array($this, 'filterset_form'), )); // 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); // connect to managesieve server $error = $this->connect($_SESSION['username'], $this->rc->decrypt($_SESSION['password'])); $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'), 'socket_options' => $this->rc->config->get('managesieve_conn_options'), )); // 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'], $plugin['socket_options'] ); if (!($error = $this->sieve->error())) { // load current/active script if (!$error) { // Get list of scripts $list = $this->list_scripts(); @@ -158,46 +113,14 @@ } } if ($script_name === null || $script_name === '') { // 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(); $error = $this->load_script($script_name); } // finally set script objects if ($error) { switch ($error) { case SIEVE_ERROR_CONNECTION: case SIEVE_ERROR_LOGIN: case rcube_sieve::ERROR_CONNECTION: case rcube_sieve::ERROR_LOGIN: $this->rc->output->show_message('managesieve.filterconnerror', 'error'); rcube::raise_error(array('code' => 403, 'type' => 'php', 'file' => __FILE__, 'line' => __LINE__, @@ -228,6 +151,120 @@ return $error; } /** * Connect to configured managesieve server * * @param string $username User login * @param string $password User password * * @return int Connection status: 0 on success, >0 on failure */ public function connect($username, $password) { // 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' => $username, 'password' => $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'), 'socket_options' => $this->rc->config->get('managesieve_conn_options'), )); // 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'], $plugin['socket_options'] ); return $this->sieve->error(); } /** * Load specified (or active) script * * @param string $script_name Optional script name * * @return int Connection status: 0 on success, >0 on failure */ public function load_script($script_name = null) { // Get list of scripts $list = $this->list_scripts(); if ($script_name === null || $script_name === '') { // get (first) active script if (!empty($this->active[0])) { $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); } return $this->sieve->error(); } /** * User interface actions handler */ function actions() { $error = $this->start(); plugins/managesieve/lib/Roundcube/rcube_sieve_vacation.php
@@ -23,6 +23,8 @@ class rcube_sieve_vacation extends rcube_sieve_engine { protected $error; function actions() { $error = $this->start('vacation'); @@ -503,4 +505,232 @@ return $result; } /** * API: get vacation rule * * @return array Vacation rule information */ public function get_vacation() { $this->exts = $this->sieve->get_extensions(); $this->init_script(); $this->vacation_rule(); // check supported extensions $date_extension = in_array('date', $this->exts); $regex_extension = in_array('regex', $this->exts); $seconds_extension = in_array('vacation-seconds', $this->exts); // set user's timezone try { $timezone = new DateTimeZone($this->rc->config->get('timezone', 'GMT')); } catch (Exception $e) { $timezone = new DateTimeZone('GMT'); } if ($date_extension) { $date_value = array(); foreach ((array) $this->vacation['tests'] as $test) { if ($test['test'] == 'currentdate') { $idx = $test['type'] == 'value-ge' ? 'start' : 'end'; if ($test['part'] == 'date') { $date_value[$idx]['date'] = $test['arg']; } else if ($test['part'] == 'iso8601') { $date_value[$idx]['datetime'] = $test['arg']; } } } foreach ($date_value as $idx => $value) { $$idx = new DateTime($value['datetime'] ?: $value['date'], $timezone); } } else if ($regex_extension) { // Sieve 'date' extension not available, read start/end from RegEx based rules instead if ($date_tests = self::parse_regexp_tests($this->vacation['tests'])) { $from = new DateTime($date_tests['from'] . ' ' . '00:00:00', $timezone); $to = new DateTime($date_tests['to'] . ' ' . '23:59:59', $timezone); } } if (isset($this->vacation['seconds'])) { $interval = $this->vacation['seconds'] . 's'; } else if (isset($this->vacation['days'])) { $interval = $this->vacation['days'] . 'd'; } $vacation = array( 'supported' => $this->exts, 'interval' => $interval, 'start' => $start, 'end' => $end, 'enabled' => $this->vacation['reason'] && empty($this->vacation['disabled']), 'message' => $this->vacation['reason'], 'subject' => $this->vacation['subject'], 'action' => $this->vacation['action'], 'target' => $this->vacation['target'], 'addresses' => $this->vacation['addresses'], ); return $vacation; } /** * API: set vacation rule * * @param array $vacation Vacation rule information (see self::get_vacation()) * * @return bool True on success, False on failure */ public function set_vacation($data) { $this->exts = $this->sieve->get_extensions(); $this->error = false; $this->init_script(); $this->vacation_rule(); // check supported extensions $date_extension = in_array('date', $this->exts); $regex_extension = in_array('regex', $this->exts); $seconds_extension = in_array('vacation-seconds', $this->exts); $vacation['type'] = 'vacation'; $vacation['reason'] = $this->strip_value(str_replace("\r\n", "\n", $data['message'])); $vacation['addresses'] = $data['addresses']; $vacation['subject'] = $data['subject']; $vacation_tests = (array) $this->vacation['tests']; foreach ((array) $vacation['addresses'] as $aidx => $address) { $vacation['addresses'][$aidx] = $address = trim($address); if (empty($address)) { unset($vacation['addresses'][$aidx]); } else if (!rcube_utils::check_email($address)) { $this->error = "Invalid address in vacation addresses: $address"; return false; } } if ($vacation['reason'] == '') { $this->error = "No vacation message specified"; return false; } if ($data['interval']) { if (!preg_match('/^([0-9]+)\s*([sd])$/', $data['interval'], $m)) { $this->error = "Invalid vacation interval value: " . $data['interval']; return false; } else if ($m[1]) { $vacation[strtolower($m[2]) == 's' ? 'seconds' : 'days'] = $m[1]; } } // find and remove existing date/regex/true rules foreach ((array) $vacation_tests as $idx => $t) { if ($t['test'] == 'currentdate' || $t['test'] == 'true' || ($t['test'] == 'header' && $t['type'] == 'regex' && $t['arg1'] == 'received') ) { unset($vacation_tests[$idx]); } } if ($date_extension) { foreach (array('start', 'end') as $var) { if ($dt = $data[$var]) { $vacation_tests[] = array( 'test' => 'currentdate', 'part' => 'iso8601', 'type' => 'value-' . ($var == 'start' ? 'ge' : 'le'), 'zone' => $dt->format('O'), 'arg' => str_replace('+00:00', 'Z', strtoupper($dt->format('c'))), ); } } } else if ($regex_extension) { // Add date range rules if range specified if ($data['start'] && $data['end']) { if ($tests = self::build_regexp_tests($data['start'], $data['end'], $error)) { $vacation_tests = array_merge($vacation_tests, $tests); } if ($error) { $this->error = "Invalid dates specified or unsupported period length"; return false; } } } if ($data['action'] == 'redirect' || $data['action'] == 'copy') { if (empty($data['target']) || !rcube_utils::check_email($data['target'])) { $this->error = "Invalid address in action taget: " . $data['target']; return false; } } else if ($data['action'] && $data['action'] != 'keep' && $data['action'] != 'discard') { $this->error = "Unsupported vacation action: " . $data['action']; return false; } if (empty($vacation_tests)) { $vacation_tests = $this->rc->config->get('managesieve_vacation_test', array(array('test' => 'true'))); } // @TODO: handle situation when there's no active script $rule = $this->vacation; $rule['type'] = 'if'; $rule['name'] = $rule['name'] ?: 'Out-of-Office'; $rule['disabled'] = isset($data['enabled']) && !$data['enabled']; $rule['tests'] = $vacation_tests; $rule['join'] = $date_extension ? count($vacation_tests) > 1 : false; $rule['actions'] = array($vacation); if ($data['action'] && $data['action'] != 'keep') { $rule['actions'][] = array( 'type' => $data['action'] == 'discard' ? 'discard' : 'redirect', 'copy' => $data['action'] == 'copy', 'target' => $data['action'] != 'discard' ? $data['target'] : '', ); } // reset original vacation rule if (isset($this->vacation['idx'])) { $this->script[$this->vacation['idx']] = null; } array_unshift($this->script, $rule); $this->sieve->script->content = array_values(array_filter($this->script)); return $this->save_script(); } /** * API: connect to managesieve server */ public function connect($username, $password) { if (!parent::connect($username, $password)) { return $this->load_script(); } } /** * API: Returns last error * * @return string Error message */ public function get_error() { return $this->error; } } plugins/managesieve/managesieve.php
@@ -37,7 +37,7 @@ function init() { $this->rc = rcmail::get_instance(); $this->rc = rcube::get_instance(); // register actions $this->register_action('plugin.managesieve', array($this, 'managesieve_actions')); @@ -230,7 +230,7 @@ /** * Initializes engine object */ private function get_engine($type = null) public function get_engine($type = null) { if (!$this->engine) { $this->load_config();