New file |
| | |
| | | <?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. |
| | | * |
| | | * $Id$ |
| | | * |
| | | */ |
| | | |
| | | // 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(); |
| | | |
| | | // 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) |
| | | { |
| | | write_log('sieve', preg_replace('/\r\n$/', '', $message)); |
| | | } |
| | | } |