You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
roundcubemail/plugins/managesieve/lib/rcube_sieve.php

385 lines
10 KiB
PHP

<?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();
// 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));
}
}