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 } |