commit | author | age
|
48e9c1
|
1 |
<?php |
T |
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 |
* |
07c6c6
|
9 |
* This program is free software: you can redistribute it and/or modify |
TB |
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. |
48e9c1
|
13 |
* |
T |
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 |
* |
07c6c6
|
19 |
* You should have received a copy of the GNU General Public License |
TB |
20 |
* along with this program. If not, see http://www.gnu.org/licenses/. |
48e9c1
|
21 |
*/ |
T |
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 $list = array(); // scripts list |
|
30 |
|
|
31 |
public $script; // rcube_sieve_script object |
|
32 |
public $current; // name of currently loaded script |
|
33 |
private $exts; // array of supported extensions |
613f96
|
34 |
|
AM |
35 |
const ERROR_CONNECTION = 1; |
|
36 |
const ERROR_LOGIN = 2; |
|
37 |
const ERROR_NOT_EXISTS = 3; // script not exists |
|
38 |
const ERROR_INSTALL = 4; // script installation |
|
39 |
const ERROR_ACTIVATE = 5; // script activation |
|
40 |
const ERROR_DELETE = 6; // script deletion |
|
41 |
const ERROR_INTERNAL = 7; // internal error |
|
42 |
const ERROR_DEACTIVATE = 8; // script activation |
|
43 |
const ERROR_OTHER = 255; // other/unknown error |
48e9c1
|
44 |
|
T |
45 |
|
|
46 |
/** |
|
47 |
* Object constructor |
|
48 |
* |
|
49 |
* @param string Username (for managesieve login) |
|
50 |
* @param string Password (for managesieve login) |
|
51 |
* @param string Managesieve server hostname/address |
|
52 |
* @param string Managesieve server port number |
|
53 |
* @param string Managesieve authentication method |
|
54 |
* @param boolean Enable/disable TLS use |
|
55 |
* @param array Disabled extensions |
|
56 |
* @param boolean Enable/disable debugging |
|
57 |
* @param string Proxy authentication identifier |
|
58 |
* @param string Proxy authentication password |
b76f8e
|
59 |
* @param array List of options to pass to stream_context_create(). |
48e9c1
|
60 |
*/ |
T |
61 |
public function __construct($username, $password='', $host='localhost', $port=2000, |
|
62 |
$auth_type=null, $usetls=true, $disabled=array(), $debug=false, |
b76f8e
|
63 |
$auth_cid=null, $auth_pw=null, $options=array()) |
48e9c1
|
64 |
{ |
T |
65 |
$this->sieve = new Net_Sieve(); |
|
66 |
|
|
67 |
if ($debug) { |
|
68 |
$this->sieve->setDebug(true, array($this, 'debug_handler')); |
|
69 |
} |
|
70 |
|
b59b72
|
71 |
$result = $this->sieve->connect($host, $port, $options, $usetls); |
AM |
72 |
|
|
73 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
74 |
return $this->_set_error(self::ERROR_CONNECTION); |
48e9c1
|
75 |
} |
T |
76 |
|
|
77 |
if (!empty($auth_cid)) { |
|
78 |
$authz = $username; |
|
79 |
$username = $auth_cid; |
6f41f0
|
80 |
} |
JB |
81 |
if (!empty($auth_pw)) { |
48e9c1
|
82 |
$password = $auth_pw; |
T |
83 |
} |
|
84 |
|
b59b72
|
85 |
$result = $this->sieve->login($username, $password, $auth_type ? strtoupper($auth_type) : null, $authz); |
AM |
86 |
|
|
87 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
88 |
return $this->_set_error(self::ERROR_LOGIN); |
48e9c1
|
89 |
} |
T |
90 |
|
b6fa7d
|
91 |
$this->exts = $this->get_extensions(); |
48e9c1
|
92 |
|
T |
93 |
// disable features by config |
|
94 |
if (!empty($disabled)) { |
|
95 |
// we're working on lower-cased names |
|
96 |
$disabled = array_map('strtolower', (array) $disabled); |
|
97 |
foreach ($disabled as $ext) { |
|
98 |
if (($idx = array_search($ext, $this->exts)) !== false) { |
|
99 |
unset($this->exts[$idx]); |
|
100 |
} |
|
101 |
} |
|
102 |
} |
|
103 |
} |
|
104 |
|
|
105 |
public function __destruct() { |
|
106 |
$this->sieve->disconnect(); |
|
107 |
} |
|
108 |
|
|
109 |
/** |
|
110 |
* Getter for error code |
|
111 |
*/ |
|
112 |
public function error() |
|
113 |
{ |
9f1f75
|
114 |
return $this->error ?: false; |
48e9c1
|
115 |
} |
T |
116 |
|
|
117 |
/** |
|
118 |
* Saves current script into server |
|
119 |
*/ |
|
120 |
public function save($name = null) |
|
121 |
{ |
b59b72
|
122 |
if (!$this->sieve) { |
613f96
|
123 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
124 |
} |
48e9c1
|
125 |
|
b59b72
|
126 |
if (!$this->script) { |
613f96
|
127 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
128 |
} |
48e9c1
|
129 |
|
b59b72
|
130 |
if (!$name) { |
48e9c1
|
131 |
$name = $this->current; |
b59b72
|
132 |
} |
48e9c1
|
133 |
|
T |
134 |
$script = $this->script->as_text(); |
|
135 |
|
b59b72
|
136 |
if (!$script) { |
48e9c1
|
137 |
$script = '/* empty script */'; |
b59b72
|
138 |
} |
48e9c1
|
139 |
|
b59b72
|
140 |
$result = $this->sieve->installScript($name, $script); |
AM |
141 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
142 |
return $this->_set_error(self::ERROR_INSTALL); |
b59b72
|
143 |
} |
48e9c1
|
144 |
|
T |
145 |
return true; |
|
146 |
} |
|
147 |
|
|
148 |
/** |
|
149 |
* Saves text script into server |
|
150 |
*/ |
|
151 |
public function save_script($name, $content = null) |
|
152 |
{ |
b59b72
|
153 |
if (!$this->sieve) { |
613f96
|
154 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
155 |
} |
48e9c1
|
156 |
|
b59b72
|
157 |
if (!$content) { |
48e9c1
|
158 |
$content = '/* empty script */'; |
b59b72
|
159 |
} |
48e9c1
|
160 |
|
b59b72
|
161 |
$result = $this->sieve->installScript($name, $content); |
AM |
162 |
|
|
163 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
164 |
return $this->_set_error(self::ERROR_INSTALL); |
b59b72
|
165 |
} |
48e9c1
|
166 |
|
T |
167 |
return true; |
|
168 |
} |
|
169 |
|
|
170 |
/** |
|
171 |
* Activates specified script |
|
172 |
*/ |
|
173 |
public function activate($name = null) |
|
174 |
{ |
b59b72
|
175 |
if (!$this->sieve) { |
613f96
|
176 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
177 |
} |
48e9c1
|
178 |
|
b59b72
|
179 |
if (!$name) { |
48e9c1
|
180 |
$name = $this->current; |
b59b72
|
181 |
} |
48e9c1
|
182 |
|
b59b72
|
183 |
$result = $this->sieve->setActive($name); |
AM |
184 |
|
|
185 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
186 |
return $this->_set_error(self::ERROR_ACTIVATE); |
b59b72
|
187 |
} |
48e9c1
|
188 |
|
T |
189 |
return true; |
|
190 |
} |
|
191 |
|
|
192 |
/** |
|
193 |
* De-activates specified script |
|
194 |
*/ |
|
195 |
public function deactivate() |
|
196 |
{ |
b59b72
|
197 |
if (!$this->sieve) { |
613f96
|
198 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
199 |
} |
48e9c1
|
200 |
|
b59b72
|
201 |
$result = $this->sieve->setActive(''); |
AM |
202 |
|
|
203 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
204 |
return $this->_set_error(self::ERROR_DEACTIVATE); |
b59b72
|
205 |
} |
48e9c1
|
206 |
|
T |
207 |
return true; |
|
208 |
} |
|
209 |
|
|
210 |
/** |
|
211 |
* Removes specified script |
|
212 |
*/ |
|
213 |
public function remove($name = null) |
|
214 |
{ |
b59b72
|
215 |
if (!$this->sieve) { |
613f96
|
216 |
return $this->_set_error(self::ERROR_INTERNAL); |
b59b72
|
217 |
} |
48e9c1
|
218 |
|
b59b72
|
219 |
if (!$name) { |
48e9c1
|
220 |
$name = $this->current; |
b59b72
|
221 |
} |
48e9c1
|
222 |
|
T |
223 |
// script must be deactivated first |
b59b72
|
224 |
if ($name == $this->sieve->getActive()) { |
AM |
225 |
$result = $this->sieve->setActive(''); |
|
226 |
|
|
227 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
228 |
return $this->_set_error(self::ERROR_DELETE); |
b59b72
|
229 |
} |
AM |
230 |
} |
48e9c1
|
231 |
|
b59b72
|
232 |
$result = $this->sieve->removeScript($name); |
AM |
233 |
|
|
234 |
if (is_a($result, 'PEAR_Error')) { |
613f96
|
235 |
return $this->_set_error(self::ERROR_DELETE); |
b59b72
|
236 |
} |
48e9c1
|
237 |
|
b59b72
|
238 |
if ($name == $this->current) { |
48e9c1
|
239 |
$this->current = null; |
b59b72
|
240 |
} |
48e9c1
|
241 |
|
T |
242 |
return true; |
|
243 |
} |
|
244 |
|
|
245 |
/** |
|
246 |
* Gets list of supported by server Sieve extensions |
|
247 |
*/ |
|
248 |
public function get_extensions() |
|
249 |
{ |
|
250 |
if ($this->exts) |
|
251 |
return $this->exts; |
|
252 |
|
|
253 |
if (!$this->sieve) |
613f96
|
254 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
255 |
|
T |
256 |
$ext = $this->sieve->getExtensions(); |
613f96
|
257 |
|
b59b72
|
258 |
if (is_a($ext, 'PEAR_Error')) { |
613f96
|
259 |
return array(); |
AM |
260 |
} |
|
261 |
|
48e9c1
|
262 |
// we're working on lower-cased names |
T |
263 |
$ext = array_map('strtolower', (array) $ext); |
|
264 |
|
|
265 |
if ($this->script) { |
|
266 |
$supported = $this->script->get_extensions(); |
|
267 |
foreach ($ext as $idx => $ext_name) |
|
268 |
if (!in_array($ext_name, $supported)) |
|
269 |
unset($ext[$idx]); |
|
270 |
} |
|
271 |
|
|
272 |
return array_values($ext); |
|
273 |
} |
|
274 |
|
|
275 |
/** |
|
276 |
* Gets list of scripts from server |
|
277 |
*/ |
|
278 |
public function get_scripts() |
|
279 |
{ |
|
280 |
if (!$this->list) { |
|
281 |
|
|
282 |
if (!$this->sieve) |
613f96
|
283 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
284 |
|
T |
285 |
$list = $this->sieve->listScripts(); |
|
286 |
|
b59b72
|
287 |
if (is_a($list, 'PEAR_Error')) { |
613f96
|
288 |
return $this->_set_error(self::ERROR_OTHER); |
b59b72
|
289 |
} |
48e9c1
|
290 |
|
T |
291 |
$this->list = $list; |
|
292 |
} |
|
293 |
|
|
294 |
return $this->list; |
|
295 |
} |
|
296 |
|
|
297 |
/** |
|
298 |
* Returns active script name |
|
299 |
*/ |
|
300 |
public function get_active() |
|
301 |
{ |
|
302 |
if (!$this->sieve) |
613f96
|
303 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
304 |
|
T |
305 |
return $this->sieve->getActive(); |
|
306 |
} |
|
307 |
|
|
308 |
/** |
|
309 |
* Loads script by name |
|
310 |
*/ |
|
311 |
public function load($name) |
|
312 |
{ |
|
313 |
if (!$this->sieve) |
613f96
|
314 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
315 |
|
T |
316 |
if ($this->current == $name) |
|
317 |
return true; |
|
318 |
|
|
319 |
$script = $this->sieve->getScript($name); |
|
320 |
|
b59b72
|
321 |
if (is_a($script, 'PEAR_Error')) { |
613f96
|
322 |
return $this->_set_error(self::ERROR_OTHER); |
b59b72
|
323 |
} |
48e9c1
|
324 |
|
T |
325 |
// try to parse from Roundcube format |
|
326 |
$this->script = $this->_parse($script); |
|
327 |
|
|
328 |
$this->current = $name; |
|
329 |
|
|
330 |
return true; |
|
331 |
} |
|
332 |
|
|
333 |
/** |
|
334 |
* Loads script from text content |
|
335 |
*/ |
|
336 |
public function load_script($script) |
|
337 |
{ |
|
338 |
if (!$this->sieve) |
613f96
|
339 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
340 |
|
T |
341 |
// try to parse from Roundcube format |
|
342 |
$this->script = $this->_parse($script); |
|
343 |
} |
|
344 |
|
|
345 |
/** |
|
346 |
* Creates rcube_sieve_script object from text script |
|
347 |
*/ |
|
348 |
private function _parse($txt) |
|
349 |
{ |
|
350 |
// parse |
|
351 |
$script = new rcube_sieve_script($txt, $this->exts); |
|
352 |
|
|
353 |
// fix/convert to Roundcube format |
|
354 |
if (!empty($script->content)) { |
|
355 |
// replace all elsif with if+stop, we support only ifs |
|
356 |
foreach ($script->content as $idx => $rule) { |
|
357 |
if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) { |
|
358 |
continue; |
|
359 |
} |
|
360 |
|
|
361 |
$script->content[$idx]['type'] = 'if'; |
|
362 |
|
|
363 |
// 'stop' not found? |
|
364 |
foreach ($rule['actions'] as $action) { |
|
365 |
if (preg_match('/^(stop|vacation)$/', $action['type'])) { |
|
366 |
continue 2; |
|
367 |
} |
|
368 |
} |
04c856
|
369 |
if (!empty($script->content[$idx+1]) && $script->content[$idx+1]['type'] != 'if') { |
48e9c1
|
370 |
$script->content[$idx]['actions'][] = array('type' => 'stop'); |
T |
371 |
} |
|
372 |
} |
|
373 |
} |
|
374 |
|
|
375 |
return $script; |
|
376 |
} |
|
377 |
|
|
378 |
/** |
|
379 |
* Gets specified script as text |
|
380 |
*/ |
|
381 |
public function get_script($name) |
|
382 |
{ |
|
383 |
if (!$this->sieve) |
613f96
|
384 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
385 |
|
T |
386 |
$content = $this->sieve->getScript($name); |
|
387 |
|
b59b72
|
388 |
if (is_a($content, 'PEAR_Error')) { |
613f96
|
389 |
return $this->_set_error(self::ERROR_OTHER); |
b59b72
|
390 |
} |
48e9c1
|
391 |
|
T |
392 |
return $content; |
|
393 |
} |
|
394 |
|
|
395 |
/** |
|
396 |
* Creates empty script or copy of other script |
|
397 |
*/ |
|
398 |
public function copy($name, $copy) |
|
399 |
{ |
|
400 |
if (!$this->sieve) |
613f96
|
401 |
return $this->_set_error(self::ERROR_INTERNAL); |
48e9c1
|
402 |
|
T |
403 |
if ($copy) { |
|
404 |
$content = $this->sieve->getScript($copy); |
|
405 |
|
b59b72
|
406 |
if (is_a($content, 'PEAR_Error')) { |
613f96
|
407 |
return $this->_set_error(self::ERROR_OTHER); |
b59b72
|
408 |
} |
48e9c1
|
409 |
} |
T |
410 |
|
b59b72
|
411 |
|
48e9c1
|
412 |
return $this->save_script($name, $content); |
T |
413 |
} |
|
414 |
|
|
415 |
private function _set_error($error) |
|
416 |
{ |
|
417 |
$this->error = $error; |
|
418 |
return false; |
|
419 |
} |
|
420 |
|
|
421 |
/** |
|
422 |
* This is our own debug handler for connection |
|
423 |
*/ |
|
424 |
public function debug_handler(&$sieve, $message) |
|
425 |
{ |
61be82
|
426 |
rcube::write_log('sieve', preg_replace('/\r\n$/', '', $message)); |
48e9c1
|
427 |
} |
T |
428 |
} |