4 Classes for managesieve operations (using PEAR::Net_Sieve)
6 Author: Aleksander Machniak <alec@alec.pl>
8 $Id: rcube_sieve.php 5203 2011-09-12 06:44:56Z alec $
12 // Managesieve Protocol: RFC5804
14 define('SIEVE_ERROR_CONNECTION', 1);
15 define('SIEVE_ERROR_LOGIN', 2);
16 define('SIEVE_ERROR_NOT_EXISTS', 3); // script not exists
17 define('SIEVE_ERROR_INSTALL', 4); // script installation
18 define('SIEVE_ERROR_ACTIVATE', 5); // script activation
19 define('SIEVE_ERROR_DELETE', 6); // script deletion
20 define('SIEVE_ERROR_INTERNAL', 7); // internal error
21 define('SIEVE_ERROR_DEACTIVATE', 8); // script activation
22 define('SIEVE_ERROR_OTHER', 255); // other/unknown error
27 private $sieve; // Net_Sieve object
28 private $error = false; // error flag
29 private $list = array(); // scripts list
31 public $script; // rcube_sieve_script object
32 public $current; // name of currently loaded script
33 private $disabled; // array of disabled extensions
34 private $exts; // array of supported extensions
40 * @param string Username (for managesieve login)
41 * @param string Password (for managesieve login)
42 * @param string Managesieve server hostname/address
43 * @param string Managesieve server port number
44 * @param string Managesieve authentication method
45 * @param boolean Enable/disable TLS use
46 * @param array Disabled extensions
47 * @param boolean Enable/disable debugging
48 * @param string Proxy authentication identifier
49 * @param string Proxy authentication password
51 public function __construct($username, $password='', $host='localhost', $port=2000,
52 $auth_type=null, $usetls=true, $disabled=array(), $debug=false,
53 $auth_cid=null, $auth_pw=null)
55 $this->sieve = new Net_Sieve();
58 $this->sieve->setDebug(true, array($this, 'debug_handler'));
61 if (PEAR::isError($this->sieve->connect($host, $port, null, $usetls))) {
62 return $this->_set_error(SIEVE_ERROR_CONNECTION);
65 if (!empty($auth_cid)) {
67 $username = $auth_cid;
71 if (PEAR::isError($this->sieve->login($username, $password,
72 $auth_type ? strtoupper($auth_type) : null, $authz))
74 return $this->_set_error(SIEVE_ERROR_LOGIN);
77 $this->exts = $this->get_extensions();
78 $this->disabled = $disabled;
81 public function __destruct() {
82 $this->sieve->disconnect();
86 * Getter for error code
88 public function error()
90 return $this->error ? $this->error : false;
94 * Saves current script into server
96 public function save($name = null)
99 return $this->_set_error(SIEVE_ERROR_INTERNAL);
102 return $this->_set_error(SIEVE_ERROR_INTERNAL);
105 $name = $this->current;
107 $script = $this->script->as_text();
110 $script = '/* empty script */';
112 if (PEAR::isError($this->sieve->installScript($name, $script)))
113 return $this->_set_error(SIEVE_ERROR_INSTALL);
119 * Saves text script into server
121 public function save_script($name, $content = null)
124 return $this->_set_error(SIEVE_ERROR_INTERNAL);
127 $content = '/* empty script */';
129 if (PEAR::isError($this->sieve->installScript($name, $content)))
130 return $this->_set_error(SIEVE_ERROR_INSTALL);
136 * Activates specified script
138 public function activate($name = null)
141 return $this->_set_error(SIEVE_ERROR_INTERNAL);
144 $name = $this->current;
146 if (PEAR::isError($this->sieve->setActive($name)))
147 return $this->_set_error(SIEVE_ERROR_ACTIVATE);
153 * De-activates specified script
155 public function deactivate()
158 return $this->_set_error(SIEVE_ERROR_INTERNAL);
160 if (PEAR::isError($this->sieve->setActive('')))
161 return $this->_set_error(SIEVE_ERROR_DEACTIVATE);
167 * Removes specified script
169 public function remove($name = null)
172 return $this->_set_error(SIEVE_ERROR_INTERNAL);
175 $name = $this->current;
177 // script must be deactivated first
178 if ($name == $this->sieve->getActive())
179 if (PEAR::isError($this->sieve->setActive('')))
180 return $this->_set_error(SIEVE_ERROR_DELETE);
182 if (PEAR::isError($this->sieve->removeScript($name)))
183 return $this->_set_error(SIEVE_ERROR_DELETE);
185 if ($name == $this->current)
186 $this->current = null;
192 * Gets list of supported by server Sieve extensions
194 public function get_extensions()
200 return $this->_set_error(SIEVE_ERROR_INTERNAL);
202 $ext = $this->sieve->getExtensions();
203 // we're working on lower-cased names
204 $ext = array_map('strtolower', (array) $ext);
207 $supported = $this->script->get_extensions();
208 foreach ($ext as $idx => $ext_name)
209 if (!in_array($ext_name, $supported))
213 return array_values($ext);
217 * Gets list of scripts from server
219 public function get_scripts()
224 return $this->_set_error(SIEVE_ERROR_INTERNAL);
226 $list = $this->sieve->listScripts();
228 if (PEAR::isError($list))
229 return $this->_set_error(SIEVE_ERROR_OTHER);
238 * Returns active script name
240 public function get_active()
243 return $this->_set_error(SIEVE_ERROR_INTERNAL);
245 return $this->sieve->getActive();
249 * Loads script by name
251 public function load($name)
254 return $this->_set_error(SIEVE_ERROR_INTERNAL);
256 if ($this->current == $name)
259 $script = $this->sieve->getScript($name);
261 if (PEAR::isError($script))
262 return $this->_set_error(SIEVE_ERROR_OTHER);
264 // try to parse from Roundcube format
265 $this->script = $this->_parse($script);
267 $this->current = $name;
273 * Loads script from text content
275 public function load_script($script)
278 return $this->_set_error(SIEVE_ERROR_INTERNAL);
280 // try to parse from Roundcube format
281 $this->script = $this->_parse($script);
285 * Creates rcube_sieve_script object from text script
287 private function _parse($txt)
289 // try to parse from Roundcube format
290 $script = new rcube_sieve_script($txt, $this->disabled, $this->exts);
292 // ... else try to import from different formats
293 if (empty($script->content)) {
294 $script = $this->_import_rules($txt);
295 $script = new rcube_sieve_script($script, $this->disabled, $this->exts);
297 // replace all elsif with if+stop, we support only ifs
298 foreach ($script->content as $idx => $rule) {
300 foreach ($rule['actions'] as $action) {
301 if (preg_match('/^(stop|vacation)$/', $action['type'])) {
305 $script->content[$idx]['actions'][] = array('type' => 'stop');
313 * Gets specified script as text
315 public function get_script($name)
318 return $this->_set_error(SIEVE_ERROR_INTERNAL);
320 $content = $this->sieve->getScript($name);
322 if (PEAR::isError($content))
323 return $this->_set_error(SIEVE_ERROR_OTHER);
329 * Creates empty script or copy of other script
331 public function copy($name, $copy)
334 return $this->_set_error(SIEVE_ERROR_INTERNAL);
337 $content = $this->sieve->getScript($copy);
339 if (PEAR::isError($content))
340 return $this->_set_error(SIEVE_ERROR_OTHER);
343 return $this->save_script($name, $content);
346 private function _import_rules($script)
351 // Squirrelmail (Avelsieve)
352 if (preg_match('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script)) {
353 $tokens = preg_split('/(#START_SIEVE_RULE.*END_SIEVE_RULE)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE);
354 foreach ($tokens as $token) {
355 if (preg_match('/^#START_SIEVE_RULE.*/', $token, $matches)) {
356 $name[$i] = "unnamed rule ".($i+1);
357 $content .= "# rule:[".$name[$i]."]\n";
359 elseif (isset($name[$i])) {
360 // This preg_replace is added because I've found some Avelsieve scripts
361 // with rules containing "if" here. I'm not sure it was working
362 // before without this or not.
363 $token = preg_replace('/^if\s+/', '', trim($token));
364 $content .= "if $token\n";
370 else if (preg_match('/(# .+)\r?\n/', $script)) {
371 $tokens = preg_split('/(# .+)\r?\n/', $script, -1, PREG_SPLIT_DELIM_CAPTURE);
372 foreach($tokens as $token) {
373 if (preg_match('/^# (.+)/', $token, $matches)) {
374 $name[$i] = $matches[1];
375 $content .= "# rule:[" . $name[$i] . "]\n";
377 elseif (isset($name[$i])) {
378 $token = str_replace(":comparator \"i;ascii-casemap\" ", "", $token);
379 $content .= $token . "\n";
388 private function _set_error($error)
390 $this->error = $error;
395 * This is our own debug handler for connection
397 public function debug_handler(&$sieve, $message)
399 write_log('sieve', preg_replace('/\r\n$/', '', $message));