]> git.donarmstrong.com Git - roundcube.git/blob - plugins/managesieve/lib/rcube_sieve.php
Imported Upstream version 0.7
[roundcube.git] / plugins / managesieve / lib / rcube_sieve.php
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 version 2
11  * as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with this program; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  * $Id: rcube_sieve.php 5452 2011-11-18 14:44:48Z alec $
23  *
24  */
25
26 // Managesieve Protocol: RFC5804
27
28 define('SIEVE_ERROR_CONNECTION', 1);
29 define('SIEVE_ERROR_LOGIN', 2);
30 define('SIEVE_ERROR_NOT_EXISTS', 3);    // script not exists
31 define('SIEVE_ERROR_INSTALL', 4);       // script installation
32 define('SIEVE_ERROR_ACTIVATE', 5);      // script activation
33 define('SIEVE_ERROR_DELETE', 6);        // script deletion
34 define('SIEVE_ERROR_INTERNAL', 7);      // internal error
35 define('SIEVE_ERROR_DEACTIVATE', 8);    // script activation
36 define('SIEVE_ERROR_OTHER', 255);       // other/unknown error
37
38
39 class rcube_sieve
40 {
41     private $sieve;                 // Net_Sieve object
42     private $error = false;         // error flag
43     private $list = array();        // scripts list
44
45     public $script;                 // rcube_sieve_script object
46     public $current;                // name of currently loaded script
47     private $exts;                  // array of supported extensions
48
49
50     /**
51      * Object constructor
52      *
53      * @param string  Username (for managesieve login)
54      * @param string  Password (for managesieve login)
55      * @param string  Managesieve server hostname/address
56      * @param string  Managesieve server port number
57      * @param string  Managesieve authentication method 
58      * @param boolean Enable/disable TLS use
59      * @param array   Disabled extensions
60      * @param boolean Enable/disable debugging
61      * @param string  Proxy authentication identifier
62      * @param string  Proxy authentication password
63      */
64     public function __construct($username, $password='', $host='localhost', $port=2000,
65         $auth_type=null, $usetls=true, $disabled=array(), $debug=false,
66         $auth_cid=null, $auth_pw=null)
67     {
68         $this->sieve = new Net_Sieve();
69
70         if ($debug) {
71             $this->sieve->setDebug(true, array($this, 'debug_handler'));
72         }
73
74         if (PEAR::isError($this->sieve->connect($host, $port, null, $usetls))) {
75             return $this->_set_error(SIEVE_ERROR_CONNECTION);
76         }
77
78         if (!empty($auth_cid)) {
79             $authz    = $username;
80             $username = $auth_cid;
81             $password = $auth_pw;
82         }
83
84         if (PEAR::isError($this->sieve->login($username, $password,
85             $auth_type ? strtoupper($auth_type) : null, $authz))
86         ) {
87             return $this->_set_error(SIEVE_ERROR_LOGIN);
88         }
89
90         $this->exts     = $this->get_extensions();
91
92         // disable features by config
93         if (!empty($disabled)) {
94             // we're working on lower-cased names
95             $disabled = array_map('strtolower', (array) $disabled);
96             foreach ($disabled as $ext) {
97                 if (($idx = array_search($ext, $this->exts)) !== false) {
98                     unset($this->exts[$idx]);
99                 }
100             }
101         }
102     }
103
104     public function __destruct() {
105         $this->sieve->disconnect();
106     }
107
108     /**
109      * Getter for error code
110      */
111     public function error()
112     {
113         return $this->error ? $this->error : false;
114     }
115
116     /**
117      * Saves current script into server
118      */
119     public function save($name = null)
120     {
121         if (!$this->sieve)
122             return $this->_set_error(SIEVE_ERROR_INTERNAL);
123
124         if (!$this->script)
125             return $this->_set_error(SIEVE_ERROR_INTERNAL);
126
127         if (!$name)
128             $name = $this->current;
129
130         $script = $this->script->as_text();
131
132         if (!$script)
133             $script = '/* empty script */';
134
135         if (PEAR::isError($this->sieve->installScript($name, $script)))
136             return $this->_set_error(SIEVE_ERROR_INSTALL);
137
138         return true;
139     }
140
141     /**
142      * Saves text script into server
143      */
144     public function save_script($name, $content = null)
145     {
146         if (!$this->sieve)
147             return $this->_set_error(SIEVE_ERROR_INTERNAL);
148
149         if (!$content)
150             $content = '/* empty script */';
151
152         if (PEAR::isError($this->sieve->installScript($name, $content)))
153             return $this->_set_error(SIEVE_ERROR_INSTALL);
154
155         return true;
156     }
157
158     /**
159      * Activates specified script
160      */
161     public function activate($name = null)
162     {
163         if (!$this->sieve)
164             return $this->_set_error(SIEVE_ERROR_INTERNAL);
165
166         if (!$name)
167             $name = $this->current;
168
169         if (PEAR::isError($this->sieve->setActive($name)))
170             return $this->_set_error(SIEVE_ERROR_ACTIVATE);
171
172         return true;
173     }
174
175     /**
176      * De-activates specified script
177      */
178     public function deactivate()
179     {
180         if (!$this->sieve)
181             return $this->_set_error(SIEVE_ERROR_INTERNAL);
182
183         if (PEAR::isError($this->sieve->setActive('')))
184             return $this->_set_error(SIEVE_ERROR_DEACTIVATE);
185
186         return true;
187     }
188
189     /**
190      * Removes specified script
191      */
192     public function remove($name = null)
193     {
194         if (!$this->sieve)
195             return $this->_set_error(SIEVE_ERROR_INTERNAL);
196
197         if (!$name)
198             $name = $this->current;
199
200         // script must be deactivated first
201         if ($name == $this->sieve->getActive())
202             if (PEAR::isError($this->sieve->setActive('')))
203                 return $this->_set_error(SIEVE_ERROR_DELETE);
204
205         if (PEAR::isError($this->sieve->removeScript($name)))
206             return $this->_set_error(SIEVE_ERROR_DELETE);
207
208         if ($name == $this->current)
209             $this->current = null;
210
211         return true;
212     }
213
214     /**
215      * Gets list of supported by server Sieve extensions
216      */
217     public function get_extensions()
218     {
219         if ($this->exts)
220             return $this->exts;
221
222         if (!$this->sieve)
223             return $this->_set_error(SIEVE_ERROR_INTERNAL);
224
225         $ext = $this->sieve->getExtensions();
226         // we're working on lower-cased names
227         $ext = array_map('strtolower', (array) $ext);
228
229         if ($this->script) {
230             $supported = $this->script->get_extensions();
231             foreach ($ext as $idx => $ext_name)
232                 if (!in_array($ext_name, $supported))
233                     unset($ext[$idx]);
234         }
235
236         return array_values($ext);
237     }
238
239     /**
240      * Gets list of scripts from server
241      */
242     public function get_scripts()
243     {
244         if (!$this->list) {
245
246             if (!$this->sieve)
247                 return $this->_set_error(SIEVE_ERROR_INTERNAL);
248
249             $list = $this->sieve->listScripts();
250
251             if (PEAR::isError($list))
252                 return $this->_set_error(SIEVE_ERROR_OTHER);
253
254             $this->list = $list;
255         }
256
257         return $this->list;
258     }
259
260     /**
261      * Returns active script name
262      */
263     public function get_active()
264     {
265         if (!$this->sieve)
266             return $this->_set_error(SIEVE_ERROR_INTERNAL);
267
268         return $this->sieve->getActive();
269     }
270
271     /**
272      * Loads script by name
273      */
274     public function load($name)
275     {
276         if (!$this->sieve)
277             return $this->_set_error(SIEVE_ERROR_INTERNAL);
278
279         if ($this->current == $name)
280             return true;
281
282         $script = $this->sieve->getScript($name);
283
284         if (PEAR::isError($script))
285             return $this->_set_error(SIEVE_ERROR_OTHER);
286
287         // try to parse from Roundcube format
288         $this->script = $this->_parse($script);
289
290         $this->current = $name;
291
292         return true;
293     }
294
295     /**
296      * Loads script from text content
297      */
298     public function load_script($script)
299     {
300         if (!$this->sieve)
301             return $this->_set_error(SIEVE_ERROR_INTERNAL);
302
303         // try to parse from Roundcube format
304         $this->script = $this->_parse($script);
305     }
306
307     /**
308      * Creates rcube_sieve_script object from text script
309      */
310     private function _parse($txt)
311     {
312         // parse
313         $script = new rcube_sieve_script($txt, $this->exts);
314
315         // fix/convert to Roundcube format
316         if (!empty($script->content)) {
317             // replace all elsif with if+stop, we support only ifs
318             foreach ($script->content as $idx => $rule) {
319                 if (empty($rule['type']) || !preg_match('/^(if|elsif|else)$/', $rule['type'])) {
320                     continue;
321                 }
322
323                 $script->content[$idx]['type'] = 'if';
324
325                 // 'stop' not found?
326                 foreach ($rule['actions'] as $action) {
327                     if (preg_match('/^(stop|vacation)$/', $action['type'])) {
328                         continue 2;
329                     }
330                 }
331                 if (empty($script->content[$idx+1]) || $script->content[$idx+1]['type'] != 'if') {
332                     $script->content[$idx]['actions'][] = array('type' => 'stop');
333                 }
334             }
335         }
336
337         return $script;
338     }
339
340     /**
341      * Gets specified script as text
342      */
343     public function get_script($name)
344     {
345         if (!$this->sieve)
346             return $this->_set_error(SIEVE_ERROR_INTERNAL);
347
348         $content = $this->sieve->getScript($name);
349
350         if (PEAR::isError($content))
351             return $this->_set_error(SIEVE_ERROR_OTHER);
352
353         return $content;
354     }
355
356     /**
357      * Creates empty script or copy of other script
358      */
359     public function copy($name, $copy)
360     {
361         if (!$this->sieve)
362             return $this->_set_error(SIEVE_ERROR_INTERNAL);
363
364         if ($copy) {
365             $content = $this->sieve->getScript($copy);
366
367             if (PEAR::isError($content))
368                 return $this->_set_error(SIEVE_ERROR_OTHER);
369         }
370
371         return $this->save_script($name, $content);
372     }
373
374     private function _set_error($error)
375     {
376         $this->error = $error;
377         return false;
378     }
379
380     /**
381      * This is our own debug handler for connection
382      */
383     public function debug_handler(&$sieve, $message)
384     {
385         write_log('sieve', preg_replace('/\r\n$/', '', $message));
386     }
387 }