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 }