Mercurial > hg > rc1
comparison plugins/managesieve/lib/Roundcube/rcube_sieve.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:50:29 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1e000243b222 |
|---|---|
| 1 <?php | |
| 2 | |
| 3 /** | |
| 4 * Classes for managesieve operations (using PEAR::Net_Sieve) | |
| 5 * | |
| 6 * Copyright (C) 2008-2011, The Roundcube Dev Team | |
| 7 * Copyright (C) 2011, Kolab Systems AG | |
| 8 * | |
| 9 * This program is free software: you can redistribute it and/or modify | |
| 10 * it under the terms of the GNU General Public License as published by | |
| 11 * the Free Software Foundation, either version 3 of the License, or | |
| 12 * (at your option) any later version. | |
| 13 * | |
| 14 * This program is distributed in the hope that it will be useful, | |
| 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 17 * GNU General Public License for more details. | |
| 18 * | |
| 19 * You should have received a copy of the GNU General Public License | |
| 20 * along with this program. If not, see http://www.gnu.org/licenses/. | |
| 21 */ | |
| 22 | |
| 23 // Managesieve Protocol: RFC5804 | |
| 24 | |
| 25 class rcube_sieve | |
| 26 { | |
| 27 private $sieve; // Net_Sieve object | |
| 28 private $error = false; // error flag | |
| 29 private $errorLines = array(); // array of line numbers within sieve script which raised an error | |
| 30 private $list = array(); // scripts list | |
| 31 private $exts; // array of supported extensions | |
| 32 private $active; // active script name | |
| 33 | |
| 34 public $script; // rcube_sieve_script object | |
| 35 public $current; // name of currently loaded script | |
| 36 | |
| 37 const ERROR_CONNECTION = 1; | |
| 38 const ERROR_LOGIN = 2; | |
| 39 const ERROR_NOT_EXISTS = 3; // script not exists | |
| 40 const ERROR_INSTALL = 4; // script installation | |
| 41 const ERROR_ACTIVATE = 5; // script activation | |
| 42 const ERROR_DELETE = 6; // script deletion | |
| 43 const ERROR_INTERNAL = 7; // internal error | |
| 44 const ERROR_DEACTIVATE = 8; // script activation | |
| 45 const ERROR_OTHER = 255; // other/unknown error | |
| 46 | |
| 47 | |
| 48 /** | |
| 49 * Object constructor | |
| 50 * | |
| 51 * @param string Username (for managesieve login) | |
| 52 * @param string Password (for managesieve login) | |
| 53 * @param string Managesieve server hostname/address | |
| 54 * @param string Managesieve server port number | |
| 55 * @param string Managesieve authentication method | |
| 56 * @param boolean Enable/disable TLS use | |
| 57 * @param array Disabled extensions | |
| 58 * @param boolean Enable/disable debugging | |
| 59 * @param string Proxy authentication identifier | |
| 60 * @param string Proxy authentication password | |
| 61 * @param array List of options to pass to stream_context_create(). | |
| 62 */ | |
| 63 public function __construct($username, $password='', $host='localhost', $port=2000, | |
| 64 $auth_type=null, $usetls=true, $disabled=array(), $debug=false, | |
| 65 $auth_cid=null, $auth_pw=null, $options=array()) | |
| 66 { | |
| 67 $this->sieve = new Net_Sieve(); | |
| 68 | |
| 69 if ($debug) { | |
| 70 $this->sieve->setDebug(true, array($this, 'debug_handler')); | |
| 71 } | |
| 72 | |
| 73 $result = $this->sieve->connect($host, $port, $options, $usetls); | |
| 74 | |
| 75 if (is_a($result, 'PEAR_Error')) { | |
| 76 return $this->_set_error(self::ERROR_CONNECTION); | |
| 77 } | |
| 78 | |
| 79 if (!empty($auth_cid)) { | |
| 80 $authz = $username; | |
| 81 $username = $auth_cid; | |
| 82 } | |
| 83 if (!empty($auth_pw)) { | |
| 84 $password = $auth_pw; | |
| 85 } | |
| 86 | |
| 87 $result = $this->sieve->login($username, $password, $auth_type ? strtoupper($auth_type) : null, $authz); | |
| 88 | |
| 89 if (is_a($result, 'PEAR_Error')) { | |
| 90 return $this->_set_error(self::ERROR_LOGIN); | |
| 91 } | |
| 92 | |
| 93 $this->exts = $this->get_extensions(); | |
| 94 | |
| 95 // disable features by config | |
| 96 if (!empty($disabled)) { | |
| 97 // we're working on lower-cased names | |
| 98 $disabled = array_map('strtolower', (array) $disabled); | |
| 99 foreach ($disabled as $ext) { | |
| 100 if (($idx = array_search($ext, $this->exts)) !== false) { | |
| 101 unset($this->exts[$idx]); | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 } | |
| 106 | |
| 107 public function __destruct() | |
| 108 { | |
| 109 $this->sieve->disconnect(); | |
| 110 } | |
| 111 | |
| 112 /** | |
| 113 * Getter for error code | |
| 114 */ | |
| 115 public function error() | |
| 116 { | |
| 117 return $this->error ?: false; | |
| 118 } | |
| 119 | |
| 120 /** | |
| 121 * Saves current script into server | |
| 122 */ | |
| 123 public function save($name = null) | |
| 124 { | |
| 125 if (!$this->sieve) { | |
| 126 return $this->_set_error(self::ERROR_INTERNAL); | |
| 127 } | |
| 128 | |
| 129 if (!$this->script) { | |
| 130 return $this->_set_error(self::ERROR_INTERNAL); | |
| 131 } | |
| 132 | |
| 133 if (!$name) { | |
| 134 $name = $this->current; | |
| 135 } | |
| 136 | |
| 137 $script = $this->script->as_text(); | |
| 138 | |
| 139 if (!$script) { | |
| 140 $script = '/* empty script */'; | |
| 141 } | |
| 142 | |
| 143 $result = $this->sieve->installScript($name, $script); | |
| 144 if (is_a($result, 'PEAR_Error')) { | |
| 145 return $this->_set_error(self::ERROR_INSTALL); | |
| 146 } | |
| 147 | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 /** | |
| 152 * Saves text script into server | |
| 153 */ | |
| 154 public function save_script($name, $content = null) | |
| 155 { | |
| 156 if (!$this->sieve) { | |
| 157 return $this->_set_error(self::ERROR_INTERNAL); | |
| 158 } | |
| 159 | |
| 160 if (!$content) { | |
| 161 $content = '/* empty script */'; | |
| 162 } | |
| 163 | |
| 164 $result = $this->sieve->installScript($name, $content); | |
| 165 | |
| 166 if (is_a($result, 'PEAR_Error')) { | |
| 167 $rawErrorMessage = $result->getMessage(); | |
| 168 $errMessages = preg_split("/$name:/", $rawErrorMessage); | |
| 169 | |
| 170 if (count($errMessages) > 0) { | |
| 171 foreach ($errMessages as $singleError) { | |
| 172 $matches = array(); | |
| 173 $res = preg_match('/line (\d+):(.*)/i', $singleError, $matches); | |
| 174 | |
| 175 if ($res === 1 ) { | |
| 176 if (count($matches) > 2) { | |
| 177 $this->errorLines[] = array("line" => $matches[1], "msg" => $matches[2]); | |
| 178 } | |
| 179 else { | |
| 180 $this->errorLines[] = array("line" => $matches[1], "msg" => null); | |
| 181 } | |
| 182 } | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 return $this->_set_error(self::ERROR_INSTALL); | |
| 187 } | |
| 188 | |
| 189 return true; | |
| 190 } | |
| 191 | |
| 192 /** | |
| 193 * Returns the current error line within the saved sieve script | |
| 194 */ | |
| 195 public function get_error_lines() | |
| 196 { | |
| 197 return $this->errorLines; | |
| 198 } | |
| 199 | |
| 200 /** | |
| 201 * Activates specified script | |
| 202 */ | |
| 203 public function activate($name = null) | |
| 204 { | |
| 205 if (!$this->sieve) { | |
| 206 return $this->_set_error(self::ERROR_INTERNAL); | |
| 207 } | |
| 208 | |
| 209 if (!$name) { | |
| 210 $name = $this->current; | |
| 211 } | |
| 212 | |
| 213 $result = $this->sieve->setActive($name); | |
| 214 | |
| 215 if (is_a($result, 'PEAR_Error')) { | |
| 216 return $this->_set_error(self::ERROR_ACTIVATE); | |
| 217 } | |
| 218 | |
| 219 $this->active = $name; | |
| 220 | |
| 221 return true; | |
| 222 } | |
| 223 | |
| 224 /** | |
| 225 * De-activates specified script | |
| 226 */ | |
| 227 public function deactivate() | |
| 228 { | |
| 229 if (!$this->sieve) { | |
| 230 return $this->_set_error(self::ERROR_INTERNAL); | |
| 231 } | |
| 232 | |
| 233 $result = $this->sieve->setActive(''); | |
| 234 | |
| 235 if (is_a($result, 'PEAR_Error')) { | |
| 236 return $this->_set_error(self::ERROR_DEACTIVATE); | |
| 237 } | |
| 238 | |
| 239 $this->active = null; | |
| 240 | |
| 241 return true; | |
| 242 } | |
| 243 | |
| 244 /** | |
| 245 * Removes specified script | |
| 246 */ | |
| 247 public function remove($name = null) | |
| 248 { | |
| 249 if (!$this->sieve) { | |
| 250 return $this->_set_error(self::ERROR_INTERNAL); | |
| 251 } | |
| 252 | |
| 253 if (!$name) { | |
| 254 $name = $this->current; | |
| 255 } | |
| 256 | |
| 257 // script must be deactivated first | |
| 258 if ($name == $this->sieve->getActive()) { | |
| 259 $result = $this->sieve->setActive(''); | |
| 260 | |
| 261 if (is_a($result, 'PEAR_Error')) { | |
| 262 return $this->_set_error(self::ERROR_DELETE); | |
| 263 } | |
| 264 | |
| 265 $this->active = null; | |
| 266 } | |
| 267 | |
| 268 $result = $this->sieve->removeScript($name); | |
| 269 | |
| 270 if (is_a($result, 'PEAR_Error')) { | |
| 271 return $this->_set_error(self::ERROR_DELETE); | |
| 272 } | |
| 273 | |
| 274 if ($name == $this->current) { | |
| 275 $this->current = null; | |
| 276 } | |
| 277 | |
| 278 $this->list = null; | |
| 279 | |
| 280 return true; | |
| 281 } | |
| 282 | |
| 283 /** | |
| 284 * Gets list of supported by server Sieve extensions | |
| 285 */ | |
| 286 public function get_extensions() | |
| 287 { | |
| 288 if ($this->exts) | |
| 289 return $this->exts; | |
| 290 | |
| 291 if (!$this->sieve) | |
| 292 return $this->_set_error(self::ERROR_INTERNAL); | |
| 293 | |
| 294 $ext = $this->sieve->getExtensions(); | |
| 295 | |
| 296 if (is_a($ext, 'PEAR_Error')) { | |
| 297 return array(); | |
| 298 } | |
| 299 | |
| 300 // we're working on lower-cased names | |
| 301 $ext = array_map('strtolower', (array) $ext); | |
| 302 | |
| 303 if ($this->script) { | |
| 304 $supported = $this->script->get_extensions(); | |
| 305 foreach ($ext as $idx => $ext_name) | |
| 306 if (!in_array($ext_name, $supported)) | |
| 307 unset($ext[$idx]); | |
| 308 } | |
| 309 | |
| 310 return array_values($ext); | |
| 311 } | |
| 312 | |
| 313 /** | |
| 314 * Gets list of scripts from server | |
| 315 */ | |
| 316 public function get_scripts() | |
| 317 { | |
| 318 if (!$this->list) { | |
| 319 | |
| 320 if (!$this->sieve) | |
| 321 return $this->_set_error(self::ERROR_INTERNAL); | |
| 322 | |
| 323 $list = $this->sieve->listScripts($active); | |
| 324 | |
| 325 if (is_a($list, 'PEAR_Error')) { | |
| 326 return $this->_set_error(self::ERROR_OTHER); | |
| 327 } | |
| 328 | |
| 329 $this->list = $list; | |
| 330 $this->active = $active; | |
| 331 } | |
| 332 | |
| 333 return $this->list; | |
| 334 } | |
| 335 | |
| 336 /** | |
| 337 * Returns active script name | |
| 338 */ | |
| 339 public function get_active() | |
| 340 { | |
| 341 if ($this->active !== null) { | |
| 342 return $this->active; | |
| 343 } | |
| 344 | |
| 345 if (!$this->sieve) { | |
| 346 return $this->_set_error(self::ERROR_INTERNAL); | |
| 347 } | |
| 348 | |
| 349 return $this->active = $this->sieve->getActive(); | |
| 350 } | |
| 351 | |
| 352 /** | |
| 353 * Loads script by name | |
| 354 */ | |
| 355 public function load($name) | |
| 356 { | |
| 357 if (!$this->sieve) | |
| 358 return $this->_set_error(self::ERROR_INTERNAL); | |
| 359 | |
| 360 if ($this->current == $name) | |
| 361 return true; | |
| 362 | |
| 363 $script = $this->sieve->getScript($name); | |
| 364 | |
| 365 if (is_a($script, 'PEAR_Error')) { | |
| 366 return $this->_set_error(self::ERROR_OTHER); | |
| 367 } | |
| 368 | |
| 369 // try to parse from Roundcube format | |
| 370 $this->script = $this->_parse($script); | |
| 371 | |
| 372 $this->current = $name; | |
| 373 | |
| 374 return true; | |
| 375 } | |
| 376 | |
| 377 /** | |
| 378 * Loads script from text content | |
| 379 */ | |
| 380 public function load_script($script) | |
| 381 { | |
| 382 if (!$this->sieve) | |
| 383 return $this->_set_error(self::ERROR_INTERNAL); | |
| 384 | |
| 385 // try to parse from Roundcube format | |
| 386 $this->script = $this->_parse($script); | |
| 387 } | |
| 388 | |
| 389 /** | |
| 390 * Creates rcube_sieve_script object from text script | |
| 391 */ | |
| 392 private function _parse($txt) | |
| 393 { | |
| 394 // parse | |
| 395 $script = new rcube_sieve_script($txt, $this->exts); | |
| 396 | |
| 397 // fix/convert to Roundcube format | |
| 398 if (!empty($script->content)) { | |
| 399 // replace all elsif with if+stop, we support only ifs | |
| 400 foreach ($script->content as $idx => $rule) { | |
| 401 if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) { | |
| 402 continue; | |
| 403 } | |
| 404 | |
| 405 $script->content[$idx]['type'] = 'if'; | |
| 406 | |
| 407 // 'stop' not found? | |
| 408 foreach ($rule['actions'] as $action) { | |
| 409 if (preg_match('/^(stop|vacation)$/', $action['type'])) { | |
| 410 continue 2; | |
| 411 } | |
| 412 } | |
| 413 if (!empty($script->content[$idx+1]) && $script->content[$idx+1]['type'] != 'if') { | |
| 414 $script->content[$idx]['actions'][] = array('type' => 'stop'); | |
| 415 } | |
| 416 } | |
| 417 } | |
| 418 | |
| 419 return $script; | |
| 420 } | |
| 421 | |
| 422 /** | |
| 423 * Gets specified script as text | |
| 424 */ | |
| 425 public function get_script($name) | |
| 426 { | |
| 427 if (!$this->sieve) | |
| 428 return $this->_set_error(self::ERROR_INTERNAL); | |
| 429 | |
| 430 $content = $this->sieve->getScript($name); | |
| 431 | |
| 432 if (is_a($content, 'PEAR_Error')) { | |
| 433 return $this->_set_error(self::ERROR_OTHER); | |
| 434 } | |
| 435 | |
| 436 return $content; | |
| 437 } | |
| 438 | |
| 439 /** | |
| 440 * Creates empty script or copy of other script | |
| 441 */ | |
| 442 public function copy($name, $copy) | |
| 443 { | |
| 444 if (!$this->sieve) | |
| 445 return $this->_set_error(self::ERROR_INTERNAL); | |
| 446 | |
| 447 if ($copy) { | |
| 448 $content = $this->sieve->getScript($copy); | |
| 449 | |
| 450 if (is_a($content, 'PEAR_Error')) { | |
| 451 return $this->_set_error(self::ERROR_OTHER); | |
| 452 } | |
| 453 } | |
| 454 | |
| 455 | |
| 456 return $this->save_script($name, $content); | |
| 457 } | |
| 458 | |
| 459 private function _set_error($error) | |
| 460 { | |
| 461 $this->error = $error; | |
| 462 return false; | |
| 463 } | |
| 464 | |
| 465 /** | |
| 466 * This is our own debug handler for connection | |
| 467 */ | |
| 468 public function debug_handler(&$sieve, $message) | |
| 469 { | |
| 470 rcube::write_log('sieve', preg_replace('/\r\n$/', '', $message)); | |
| 471 } | |
| 472 } |
