source: subversion/trunk/plugins/managesieve/managesieve.php @ 5334

Last change on this file since 5334 was 5334, checked in by alec, 20 months ago
  • Use smaller action/rule buttons, create separate dir for images
  • Property svn:keywords set to Id
File size: 65.6 KB
Line 
1<?php
2
3/**
4 * Managesieve (Sieve Filters)
5 *
6 * Plugin that adds a possibility to manage Sieve filters in Thunderbird's style.
7 * It's clickable interface which operates on text scripts and communicates
8 * with server using managesieve protocol. Adds Filters tab in Settings.
9 *
10 * @version 5.0
11 * @author Aleksander Machniak <alec@alec.pl>
12 *
13 * Configuration (see config.inc.php.dist)
14 *
15 * Copyright (C) 2008-2011, The Roundcube Dev Team
16 * Copyright (C) 2011, Kolab Systems AG
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License version 2
20 * as published by the Free Software Foundation.
21 *
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * You should have received a copy of the GNU General Public License along
28 * with this program; if not, write to the Free Software Foundation, Inc.,
29 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 *
31 * $Id$
32 */
33
34class managesieve extends rcube_plugin
35{
36    public $task = 'mail|settings';
37
38    private $rc;
39    private $sieve;
40    private $errors;
41    private $form;
42    private $tips = array();
43    private $script = array();
44    private $exts = array();
45    private $list;
46    private $active = array();
47    private $headers = array(
48        'subject'   => 'Subject',
49        'sender'    => 'From',
50        'recipient' => 'To',
51    );
52
53    const VERSION = '5.0';
54    const PROGNAME = 'Roundcube (Managesieve)';
55
56
57    function init()
58    {
59        $this->rc = rcmail::get_instance();
60
61        // register actions
62        $this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
63        $this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
64
65        if ($this->rc->task == 'settings') {
66            $this->init_ui();
67        }
68        else if ($this->rc->task == 'mail') {
69            // register message hook
70            $this->add_hook('message_headers_output', array($this, 'mail_headers'));
71
72            // inject Create Filter popup stuff
73            if (empty($this->rc->action) || $this->rc->action == 'show') {
74                $this->mail_task_handler();
75            }
76        }
77    }
78
79    /**
80     * Initializes plugin's UI (localization, js script)
81     */
82    private function init_ui()
83    {
84        if ($this->ui_initialized)
85            return;
86
87        // load localization
88        $this->add_texts('localization/', array('filters','managefilters'));
89        $this->include_script('managesieve.js');
90
91        $this->ui_initialized = true;
92    }
93
94    /**
95     * Add UI elements to the 'mailbox view' and 'show message' UI.
96     */
97    function mail_task_handler()
98    {
99        // use jQuery for popup window
100        $this->require_plugin('jqueryui'); 
101
102        // include js script and localization
103        $this->init_ui();
104
105        // include styles
106        $skin = $this->rc->config->get('skin');
107        if (!file_exists($this->home."/skins/$skin/managesieve_mail.css"))
108            $skin = 'default';
109        $this->include_stylesheet("skins/$skin/managesieve_mail.css");
110
111        // add 'Create filter' item to message menu
112        $this->api->add_content(html::tag('li', null, 
113            $this->api->output->button(array(
114                'command'  => 'managesieve-create',
115                'label'    => 'managesieve.filtercreate',
116                'type'     => 'link',
117                'classact' => 'filterlink active',
118                'class'    => 'filterlink',
119            ))), 'messagemenu');
120
121        // register some labels/messages
122        $this->rc->output->add_label('managesieve.newfilter', 'managesieve.usedata',
123            'managesieve.nodata', 'managesieve.nextstep', 'save');
124
125        $this->rc->session->remove('managesieve_current');
126    }
127
128    /**
129     * Get message headers for popup window
130     */
131    function mail_headers($args)
132    {
133        $headers = $args['headers'];
134        $ret     = array();
135
136        if ($headers->subject)
137            $ret[] = array('Subject', $this->rc->imap->decode_header($headers->subject));
138
139        // @TODO: List-Id, others?
140        foreach (array('From', 'To') as $h) {
141            $hl = strtolower($h);
142            if ($headers->$hl) {
143                $list = $this->rc->imap->decode_address_list($headers->$hl);
144                foreach ($list as $item) {
145                    if ($item['mailto']) {
146                        $ret[] = array($h, $item['mailto']);
147                    }
148                }
149            }
150        }
151
152        if ($this->rc->action == 'preview')
153            $this->rc->output->command('parent.set_env', array('sieve_headers' => $ret));
154        else
155            $this->rc->output->set_env('sieve_headers', $ret);
156
157
158        return $args;
159    }
160
161    /**
162     * Loads configuration, initializes plugin (including sieve connection)
163     */
164    function managesieve_start()
165    {
166        $this->load_config();
167
168        // register UI objects
169        $this->rc->output->add_handlers(array(
170            'filterslist'    => array($this, 'filters_list'),
171            'filtersetslist' => array($this, 'filtersets_list'),
172            'filterframe'    => array($this, 'filter_frame'),
173            'filterform'     => array($this, 'filter_form'),
174            'filtersetform'  => array($this, 'filterset_form'),
175        ));
176
177        // Add include path for internal classes
178        $include_path = $this->home . '/lib' . PATH_SEPARATOR;
179        $include_path .= ini_get('include_path');
180        set_include_path($include_path);
181
182        $host = rcube_parse_host($this->rc->config->get('managesieve_host', 'localhost'));
183        $port = $this->rc->config->get('managesieve_port', 2000);
184
185        $host = rcube_idn_to_ascii($host);
186
187        $plugin = $this->rc->plugins->exec_hook('managesieve_connect', array(
188            'user'      => $_SESSION['username'],
189            'password'  => $this->rc->decrypt($_SESSION['password']),
190            'host'      => $host,
191            'port'      => $port,
192            'auth_type' => $this->rc->config->get('managesieve_auth_type'),
193            'usetls'    => $this->rc->config->get('managesieve_usetls', false),
194            'disabled'  => $this->rc->config->get('managesieve_disabled_extensions'),
195            'debug'     => $this->rc->config->get('managesieve_debug', false),
196            'auth_cid'  => $this->rc->config->get('managesieve_auth_cid'),
197            'auth_pw'   => $this->rc->config->get('managesieve_auth_pw'),
198        ));
199
200        // try to connect to managesieve server and to fetch the script
201        $this->sieve = new rcube_sieve(
202            $plugin['user'],
203            $plugin['password'],
204            $plugin['host'],
205            $plugin['port'],
206            $plugin['auth_type'],
207            $plugin['usetls'],
208            $plugin['disabled'],
209            $plugin['debug'],
210            $plugin['auth_cid'],
211            $plugin['auth_pw']
212        );
213
214        if (!($error = $this->sieve->error())) {
215            // Get list of scripts
216            $list = $this->list_scripts();
217
218            if (!empty($_GET['_set']) || !empty($_POST['_set'])) {
219                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
220            }
221            else if (!empty($_SESSION['managesieve_current'])) {
222                $script_name = $_SESSION['managesieve_current'];
223            }
224            else {
225                // get (first) active script
226                if (!empty($this->active[0])) {
227                    $script_name = $this->active[0];
228                }
229                else if ($list) {
230                    $script_name = $list[0];
231                }
232                // create a new (initial) script
233                else {
234                    // if script not exists build default script contents
235                    $script_file = $this->rc->config->get('managesieve_default');
236                    $script_name = $this->rc->config->get('managesieve_script_name');
237
238                    if (empty($script_name))
239                        $script_name = 'roundcube';
240
241                    if ($script_file && is_readable($script_file))
242                        $content = file_get_contents($script_file);
243
244                    // add script and set it active
245                    if ($this->sieve->save_script($script_name, $content)) {
246                        $this->activate_script($script_name);
247                    }
248                }
249            }
250
251            if ($script_name) {
252                $this->sieve->load($script_name);
253            }
254
255            $error = $this->sieve->error();
256        }
257
258        // finally set script objects
259        if ($error) {
260            switch ($error) {
261                case SIEVE_ERROR_CONNECTION:
262                case SIEVE_ERROR_LOGIN:
263                    $this->rc->output->show_message('managesieve.filterconnerror', 'error');
264                    break;
265                default:
266                    $this->rc->output->show_message('managesieve.filterunknownerror', 'error');
267                    break;
268            }
269
270            raise_error(array('code' => 403, 'type' => 'php',
271                'file' => __FILE__, 'line' => __LINE__,
272                'message' => "Unable to connect to managesieve on $host:$port"), true, false);
273
274            // to disable 'Add filter' button set env variable
275            $this->rc->output->set_env('filterconnerror', true);
276            $this->script = array();
277        }
278        else {
279            $this->exts = $this->sieve->get_extensions();
280            $this->script = $this->sieve->script->as_array();
281            if (empty($_GET['act']))
282                $this->rc->output->set_env('active_sets', $this->active);
283            $_SESSION['managesieve_current'] = $this->sieve->current;
284        }
285
286        return $error;
287    }
288
289    function managesieve_actions()
290    {
291        $this->init_ui();
292
293        $error = $this->managesieve_start();
294
295        // Handle user requests
296        if ($action = get_input_value('_act', RCUBE_INPUT_GPC)) {
297            $fid = (int) get_input_value('_fid', RCUBE_INPUT_GET);
298
299            if ($action == 'up' && !$error) {
300                if ($fid && isset($this->script[$fid]) && isset($this->script[$fid-1])) {
301                    if ($this->sieve->script->update_rule($fid, $this->script[$fid-1]) !== false
302                        && $this->sieve->script->update_rule($fid-1, $this->script[$fid]) !== false) {
303                        $result = $this->save_script();
304                    }
305
306                    if ($result) {
307//                      $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
308                        $this->rc->output->command('managesieve_updatelist', 'up', '', $fid);
309                    } else
310                        $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
311                }
312            }
313            else if ($action == 'down' && !$error) {
314                if (isset($this->script[$fid]) && isset($this->script[$fid+1])) {
315                    if ($this->sieve->script->update_rule($fid, $this->script[$fid+1]) !== false
316                        && $this->sieve->script->update_rule($fid+1, $this->script[$fid]) !== false) {
317                        $result = $this->save_script();
318                    }
319
320                    if ($result === true) {
321//                      $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
322                        $this->rc->output->command('managesieve_updatelist', 'down', '', $fid);
323                    } else {
324                        $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
325                    }
326                }
327            }
328            else if ($action == 'delete' && !$error) {
329                if (isset($this->script[$fid])) {
330                    if ($this->sieve->script->delete_rule($fid))
331                        $result = $this->save_script();
332
333                    if ($result === true) {
334                        $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
335                        $this->rc->output->command('managesieve_updatelist', 'delete', '', $fid);
336                    } else {
337                        $this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
338                    }
339                }
340            }
341            else if ($action == 'setact' && !$error) {
342                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
343                $result = $this->activate_script($script_name);
344
345                if ($result === true) {
346                    $this->rc->output->set_env('active_sets', $this->active);
347                    $this->rc->output->show_message('managesieve.setactivated', 'confirmation');
348                    $this->rc->output->command('managesieve_reset');
349                } else {
350                    $this->rc->output->show_message('managesieve.setactivateerror', 'error');
351                }
352            }
353            else if ($action == 'deact' && !$error) {
354                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
355                $result = $this->deactivate_script($script_name);
356
357                if ($result === true) {
358                    $this->rc->output->set_env('active_sets', $this->active);
359                    $this->rc->output->show_message('managesieve.setdeactivated', 'confirmation');
360                    $this->rc->output->command('managesieve_reset');
361                } else {
362                    $this->rc->output->show_message('managesieve.setdeactivateerror', 'error');
363                }
364            }
365            else if ($action == 'setdel' && !$error) {
366                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
367                $result = $this->remove_script($script_name);
368
369                if ($result === true) {
370                    $this->rc->output->show_message('managesieve.setdeleted', 'confirmation');
371                    $this->rc->output->command('managesieve_reload');
372                    $this->rc->session->remove('managesieve_current');
373                } else {
374                    $this->rc->output->show_message('managesieve.setdeleteerror', 'error');
375                }
376            }
377            else if ($action == 'setget') {
378                $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
379                $script = $this->sieve->get_script($script_name);
380
381                if (PEAR::isError($script))
382                    exit;
383
384                $browser = new rcube_browser;
385
386                // send download headers
387                header("Content-Type: application/octet-stream");
388                header("Content-Length: ".strlen($script));
389
390                if ($browser->ie)
391                    header("Content-Type: application/force-download");
392                if ($browser->ie && $browser->ver < 7)
393                    $filename = rawurlencode(abbreviate_string($script_name, 55));
394                else if ($browser->ie)
395                    $filename = rawurlencode($script_name);
396                else
397                    $filename = addcslashes($script_name, '\\"');
398
399                header("Content-Disposition: attachment; filename=\"$filename.txt\"");
400                echo $script;
401                exit;
402            }
403            elseif ($action == 'ruleadd') {
404                $rid = get_input_value('_rid', RCUBE_INPUT_GPC);
405                $id = $this->genid();
406                $content = $this->rule_div($fid, $id, false);
407
408                $this->rc->output->command('managesieve_rulefill', $content, $id, $rid);
409            }
410            elseif ($action == 'actionadd') {
411                $aid = get_input_value('_aid', RCUBE_INPUT_GPC);
412                $id = $this->genid();
413                $content = $this->action_div($fid, $id, false);
414
415                $this->rc->output->command('managesieve_actionfill', $content, $id, $aid);
416            }
417
418            $this->rc->output->send();
419        }
420        else if ($this->rc->task == 'mail') {
421            // Initialize the form
422            $rules = get_input_value('r', RCUBE_INPUT_GET);
423            if (!empty($rules)) {
424                $i = 0;
425                foreach ($rules as $rule) {
426                    list($header, $value) = explode(':', $rule, 2);
427                    $tests[$i] = array(
428                        'type' => 'contains',
429                        'test' => 'header',
430                        'arg1' => $header,
431                        'arg2' => $value,
432                    );
433                    $i++;
434                }
435
436                $this->form = array(
437                    'join'  => count($tests) > 1 ? 'allof' : 'anyof',
438                    'name'  => '',
439                    'tests' => $tests,
440                    'actions' => array(
441                        0 => array('type' => 'fileinto'),
442                        1 => array('type' => 'stop'),
443                    ),
444                );
445            }
446        }
447
448        $this->managesieve_send();
449    }
450
451    function managesieve_save()
452    {
453        // load localization
454        $this->add_texts('localization/', array('filters','managefilters'));
455
456        // include main js script
457        if ($this->api->output->type == 'html') {
458            $this->include_script('managesieve.js');
459        }
460
461        // Init plugin and handle managesieve connection
462        $error = $this->managesieve_start();
463
464        // filters set add action
465        if (!empty($_POST['_newset'])) {
466
467            $name = get_input_value('_name', RCUBE_INPUT_POST);
468            $copy = get_input_value('_copy', RCUBE_INPUT_POST);
469            $from = get_input_value('_from', RCUBE_INPUT_POST);
470
471            if (!$name)
472                $error = 'managesieve.emptyname';
473            else if (mb_strlen($name)>128)
474                $error = 'managesieve.nametoolong';
475            else if ($from == 'file') {
476                // from file
477                if (is_uploaded_file($_FILES['_file']['tmp_name'])) {
478                    $file = file_get_contents($_FILES['_file']['tmp_name']);
479                    $file = preg_replace('/\r/', '', $file);
480                    // for security don't save script directly
481                    // check syntax before, like this...
482                    $this->sieve->load_script($file);
483                    if (!$this->save_script($name)) {
484                        $error = 'managesieve.setcreateerror';
485                    }
486                }
487                else {  // upload failed
488                    $err = $_FILES['_file']['error'];
489                    $error = true;
490
491                    if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
492                        $msg = rcube_label(array('name' => 'filesizeerror',
493                            'vars' => array('size' =>
494                                show_bytes(parse_bytes(ini_get('upload_max_filesize'))))));
495                    }
496                    else {
497                        $error = 'fileuploaderror';
498                    }
499                }
500            }
501            else if (!$this->sieve->copy($name, $from == 'set' ? $copy : '')) {
502                $error = 'managesieve.setcreateerror';
503            }
504
505            if (!$error) {
506                $this->rc->output->show_message('managesieve.setcreated', 'confirmation');
507                $this->rc->output->command('parent.managesieve_reload', $name);
508            } else if ($msg) {
509                $this->rc->output->command('display_message', $msg, 'error');
510            } else {
511                $this->rc->output->show_message($error, 'error');
512            }
513        }
514        // filter add/edit action
515        else if (isset($_POST['_name'])) {
516            $name = trim(get_input_value('_name', RCUBE_INPUT_POST, true));
517            $fid  = trim(get_input_value('_fid', RCUBE_INPUT_POST));
518            $join = trim(get_input_value('_join', RCUBE_INPUT_POST));
519
520            // and arrays
521            $headers        = get_input_value('_header', RCUBE_INPUT_POST);
522            $cust_headers   = get_input_value('_custom_header', RCUBE_INPUT_POST);
523            $ops            = get_input_value('_rule_op', RCUBE_INPUT_POST);
524            $sizeops        = get_input_value('_rule_size_op', RCUBE_INPUT_POST);
525            $sizeitems      = get_input_value('_rule_size_item', RCUBE_INPUT_POST);
526            $sizetargets    = get_input_value('_rule_size_target', RCUBE_INPUT_POST);
527            $targets        = get_input_value('_rule_target', RCUBE_INPUT_POST, true);
528            $act_types      = get_input_value('_action_type', RCUBE_INPUT_POST, true);
529            $mailboxes      = get_input_value('_action_mailbox', RCUBE_INPUT_POST, true);
530            $act_targets    = get_input_value('_action_target', RCUBE_INPUT_POST, true);
531            $area_targets   = get_input_value('_action_target_area', RCUBE_INPUT_POST, true);
532            $reasons        = get_input_value('_action_reason', RCUBE_INPUT_POST, true);
533            $addresses      = get_input_value('_action_addresses', RCUBE_INPUT_POST, true);
534            $days           = get_input_value('_action_days', RCUBE_INPUT_POST);
535            $subject        = get_input_value('_action_subject', RCUBE_INPUT_POST, true);
536            $flags          = get_input_value('_action_flags', RCUBE_INPUT_POST);
537
538            // we need a "hack" for radiobuttons
539            foreach ($sizeitems as $item)
540                $items[] = $item;
541
542            $this->form['disabled'] = $_POST['_disabled'] ? true : false;
543            $this->form['join']     = $join=='allof' ? true : false;
544            $this->form['name']     = $name;
545            $this->form['tests']    = array();
546            $this->form['actions']  = array();
547
548            if ($name == '')
549                $this->errors['name'] = $this->gettext('cannotbeempty');
550            else {
551                foreach($this->script as $idx => $rule)
552                    if($rule['name'] == $name && $idx != $fid) {
553                        $this->errors['name'] = $this->gettext('ruleexist');
554                        break;
555                    }
556            }
557
558            $i = 0;
559            // rules
560            if ($join == 'any') {
561                $this->form['tests'][0]['test'] = 'true';
562            }
563            else {
564                foreach ($headers as $idx => $header) {
565                    $header = $this->strip_value($header);
566                    $target = $this->strip_value($targets[$idx], true);
567                    $op     = $this->strip_value($ops[$idx]);
568
569                    // normal header
570                    if (in_array($header, $this->headers)) {
571                        if (preg_match('/^not/', $op))
572                            $this->form['tests'][$i]['not'] = true;
573                        $type = preg_replace('/^not/', '', $op);
574
575                        if ($type == 'exists') {
576                            $this->form['tests'][$i]['test'] = 'exists';
577                            $this->form['tests'][$i]['arg'] = $header;
578                        }
579                        else {
580                            $this->form['tests'][$i]['type'] = $type;
581                            $this->form['tests'][$i]['test'] = 'header';
582                            $this->form['tests'][$i]['arg1'] = $header;
583                            $this->form['tests'][$i]['arg2'] = $target;
584
585                            if ($target == '')
586                                $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
587                            else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
588                                $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
589                        }
590                    }
591                    else
592                        switch ($header) {
593                        case 'size':
594                            $sizeop     = $this->strip_value($sizeops[$idx]);
595                            $sizeitem   = $this->strip_value($items[$idx]);
596                            $sizetarget = $this->strip_value($sizetargets[$idx]);
597
598                            $this->form['tests'][$i]['test'] = 'size';
599                            $this->form['tests'][$i]['type'] = $sizeop;
600                            $this->form['tests'][$i]['arg']  = $sizetarget.$sizeitem;
601
602                            if ($sizetarget == '')
603                                $this->errors['tests'][$i]['sizetarget'] = $this->gettext('cannotbeempty');
604                            else if (!preg_match('/^[0-9]+(K|M|G)*$/i', $sizetarget))
605                                $this->errors['tests'][$i]['sizetarget'] = $this->gettext('forbiddenchars');
606                            break;
607                        case '...':
608                            $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
609
610                            if (preg_match('/^not/', $op))
611                                $this->form['tests'][$i]['not'] = true;
612                            $type = preg_replace('/^not/', '', $op);
613
614                            if ($cust_header == '')
615                                $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
616                            else {
617                                $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
618
619                                if (!count($headers))
620                                    $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
621                                else {
622                                    foreach ($headers as $hr)
623                                        if (!preg_match('/^[a-z0-9-]+$/i', $hr))
624                                            $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
625                                }
626                            }
627
628                            if (empty($this->errors['tests'][$i]['header']))
629                                $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
630
631                            if ($type == 'exists') {
632                                $this->form['tests'][$i]['test'] = 'exists';
633                                $this->form['tests'][$i]['arg']  = $cust_header;
634                            }
635                            else {
636                                $this->form['tests'][$i]['test'] = 'header';
637                                $this->form['tests'][$i]['type'] = $type;
638                                $this->form['tests'][$i]['arg1'] = $cust_header;
639                                $this->form['tests'][$i]['arg2'] = $target;
640
641                                if ($target == '')
642                                    $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty');
643                                else if (preg_match('/^(value|count)-/', $type) && !preg_match('/[0-9]+/', $target))
644                                    $this->errors['tests'][$i]['target'] = $this->gettext('forbiddenchars');
645                            }
646                            break;
647                        }
648                    $i++;
649                }
650            }
651
652            $i = 0;
653            // actions
654            foreach($act_types as $idx => $type) {
655                $type   = $this->strip_value($type);
656                $target = $this->strip_value($act_targets[$idx]);
657
658                switch ($type) {
659
660                case 'fileinto':
661                case 'fileinto_copy':
662                    $mailbox = $this->strip_value($mailboxes[$idx]);
663                    $this->form['actions'][$i]['target'] = $this->mod_mailbox($mailbox, 'in');
664                    if ($type == 'fileinto_copy') {
665                        $type = 'fileinto';
666                        $this->form['actions'][$i]['copy'] = true;
667                    }
668                    break;
669
670                case 'reject':
671                case 'ereject':
672                    $target = $this->strip_value($area_targets[$idx]);
673                    $this->form['actions'][$i]['target'] = str_replace("\r\n", "\n", $target);
674
675 //                 if ($target == '')
676//                      $this->errors['actions'][$i]['targetarea'] = $this->gettext('cannotbeempty');
677                    break;
678
679                case 'redirect':
680                case 'redirect_copy':
681                    $this->form['actions'][$i]['target'] = $target;
682
683                    if ($this->form['actions'][$i]['target'] == '')
684                        $this->errors['actions'][$i]['target'] = $this->gettext('cannotbeempty');
685                    else if (!check_email($this->form['actions'][$i]['target']))
686                        $this->errors['actions'][$i]['target'] = $this->gettext('noemailwarning');
687
688                    if ($type == 'redirect_copy') {
689                        $type = 'redirect';
690                        $this->form['actions'][$i]['copy'] = true;
691                    }
692                    break;
693
694                case 'addflag':
695                case 'setflag':
696                case 'removeflag':
697                    $_target = array();
698                    if (empty($flags[$idx])) {
699                        $this->errors['actions'][$i]['target'] = $this->gettext('noflagset');
700                    }
701                    else {
702                        foreach ($flags[$idx] as $flag) {
703                            $_target[] = $this->strip_value($flag);
704                        }
705                    }
706                    $this->form['actions'][$i]['target'] = $_target;
707                    break;
708
709                case 'vacation':
710                    $reason = $this->strip_value($reasons[$idx]);
711                    $this->form['actions'][$i]['reason']    = str_replace("\r\n", "\n", $reason);
712                    $this->form['actions'][$i]['days']      = $days[$idx];
713                    $this->form['actions'][$i]['subject']   = $subject[$idx];
714                    $this->form['actions'][$i]['addresses'] = explode(',', $addresses[$idx]);
715// @TODO: vacation :mime, :from, :handle
716
717                    if ($this->form['actions'][$i]['addresses']) {
718                        foreach($this->form['actions'][$i]['addresses'] as $aidx => $address) {
719                            $address = trim($address);
720                            if (!$address)
721                                unset($this->form['actions'][$i]['addresses'][$aidx]);
722                            else if(!check_email($address)) {
723                                $this->errors['actions'][$i]['addresses'] = $this->gettext('noemailwarning');
724                                break;
725                            } else
726                                $this->form['actions'][$i]['addresses'][$aidx] = $address;
727                        }
728                    }
729
730                    if ($this->form['actions'][$i]['reason'] == '')
731                        $this->errors['actions'][$i]['reason'] = $this->gettext('cannotbeempty');
732                    if ($this->form['actions'][$i]['days'] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i]['days']))
733                        $this->errors['actions'][$i]['days'] = $this->gettext('forbiddenchars');
734                    break;
735                }
736
737                $this->form['actions'][$i]['type'] = $type;
738                $i++;
739            }
740
741            if (!$this->errors && !$error) {
742                // zapis skryptu
743                if (!isset($this->script[$fid])) {
744                    $fid = $this->sieve->script->add_rule($this->form);
745                    $new = true;
746                } else
747                    $fid = $this->sieve->script->update_rule($fid, $this->form);
748
749                if ($fid !== false)
750                    $save = $this->save_script();
751
752                if ($save && $fid !== false) {
753                    $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
754                    if ($this->rc->task != 'mail') {
755                        $this->rc->output->add_script(
756                            sprintf("rcmail.managesieve_updatelist('%s', '%s', %d, %d);",
757                                isset($new) ? 'add' : 'update', Q($this->form['name']),
758                                $fid, $this->form['disabled']),
759                            'foot');
760                    }
761                    else {
762                        $this->rc->output->command('managesieve_dialog_close');
763                        $this->rc->output->send('iframe');
764                    }
765                }
766                else {
767                    $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
768//                  $this->rc->output->send();
769                }
770            }
771        }
772
773        $this->managesieve_send();
774    }
775
776    private function managesieve_send()
777    {
778        // Handle form action
779        if (isset($_GET['_framed']) || isset($_POST['_framed'])) {
780            if (isset($_GET['_newset']) || isset($_POST['_newset'])) {
781                $this->rc->output->send('managesieve.setedit');
782            }
783            else {
784                $this->rc->output->send('managesieve.filteredit');
785            }
786        } else {
787            $this->rc->output->set_pagetitle($this->gettext('filters'));
788            $this->rc->output->send('managesieve.managesieve');
789        }
790    }
791
792    // return the filters list as HTML table
793    function filters_list($attrib)
794    {
795        // add id to message list table if not specified
796        if (!strlen($attrib['id']))
797            $attrib['id'] = 'rcmfilterslist';
798
799        // define list of cols to be displayed
800        $a_show_cols = array('managesieve.filtername');
801
802        $i = 1;
803        foreach ($this->script as $idx => $filter) {
804            if ($filter['type'] != 'if') {
805                continue;
806            }
807            $fname = $filter['name'] ? $filter['name'] : "#$i";
808            $result[] = array(
809                'managesieve.filtername' => $fname,
810                'id' => $idx,
811                'class' => $filter['disabled'] ? 'disabled' : '',
812            );
813            $i++;
814        }
815
816        // create XHTML table
817        $out = rcube_table_output($attrib, $result, $a_show_cols, 'id');
818
819        // set client env
820        $this->rc->output->add_gui_object('filterslist', $attrib['id']);
821        $this->rc->output->include_script('list.js');
822
823        // add some labels to client
824        $this->rc->output->add_label('managesieve.filterdeleteconfirm');
825
826        return $out;
827    }
828
829    // return the filters list as <SELECT>
830    function filtersets_list($attrib, $no_env = false)
831    {
832        // add id to message list table if not specified
833        if (!strlen($attrib['id']))
834            $attrib['id'] = 'rcmfiltersetslist';
835
836        $list = $this->list_scripts();
837
838        $select = new html_select(array('name' => '_set', 'id' => $attrib['id'],
839            'onchange' => $this->rc->task != 'mail' ? 'rcmail.managesieve_set()' : ''));
840
841        if ($list) {
842            asort($list, SORT_LOCALE_STRING);
843
844            foreach ($list as $set)
845                $select->add($set . (in_array($set, $this->active) ? ' ('.$this->gettext('active').')' : ''), $set);
846        }
847
848        $out = $select->show($this->sieve->current);
849
850        // set client env
851        if (!$no_env) {
852            $this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
853            $this->rc->output->add_label(
854                'managesieve.setdeleteconfirm',
855                'managesieve.active',
856                'managesieve.filtersetact',
857                'managesieve.filtersetdeact'
858            );
859        }
860
861        return $out;
862    }
863
864    function filter_frame($attrib)
865    {
866        if (!$attrib['id'])
867            $attrib['id'] = 'rcmfilterframe';
868
869        $attrib['name'] = $attrib['id'];
870
871        $this->rc->output->set_env('contentframe', $attrib['name']);
872        $this->rc->output->set_env('blankpage', $attrib['src'] ?
873        $this->rc->output->abs_url($attrib['src']) : 'program/blank.gif');
874
875        return html::tag('iframe', $attrib);
876    }
877
878    function filterset_form($attrib)
879    {
880        if (!$attrib['id'])
881            $attrib['id'] = 'rcmfiltersetform';
882
883        $out = '<form name="filtersetform" action="./" method="post" enctype="multipart/form-data">'."\n";
884
885        $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
886        $hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
887        $hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
888        $hiddenfields->add(array('name' => '_newset', 'value' => 1));
889
890        $out .= $hiddenfields->show();
891
892        $name     = get_input_value('_name', RCUBE_INPUT_POST);
893        $copy     = get_input_value('_copy', RCUBE_INPUT_POST);
894        $selected = get_input_value('_from', RCUBE_INPUT_POST);
895
896        // filter set name input
897        $input_name = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30,
898            'class' => ($this->errors['name'] ? 'error' : '')));
899
900        $out .= sprintf('<label for="%s"><b>%s:</b></label> %s<br /><br />',
901            '_name', Q($this->gettext('filtersetname')), $input_name->show($name));
902
903        $out .="\n<fieldset class=\"itemlist\"><legend>" . $this->gettext('filters') . ":</legend>\n";
904        $out .= '<input type="radio" id="from_none" name="_from" value="none"'
905            .(!$selected || $selected=='none' ? ' checked="checked"' : '').'></input>';
906        $out .= sprintf('<label for="%s">%s</label> ', 'from_none', Q($this->gettext('none')));
907
908        // filters set list
909        $list   = $this->list_scripts();
910        $select = new html_select(array('name' => '_copy', 'id' => '_copy'));
911
912        if (is_array($list)) {
913            asort($list, SORT_LOCALE_STRING);
914
915            foreach ($list as $set)
916                $select->add($set . (in_array($set, $this->active) ? ' ('.$this->gettext('active').')' : ''), $set);
917
918            $out .= '<br /><input type="radio" id="from_set" name="_from" value="set"'
919                .($selected=='set' ? ' checked="checked"' : '').'></input>';
920            $out .= sprintf('<label for="%s">%s:</label> ', 'from_set', Q($this->gettext('fromset')));
921            $out .= $select->show($copy);
922        }
923
924        // script upload box
925        $upload = new html_inputfield(array('name' => '_file', 'id' => '_file', 'size' => 30,
926            'type' => 'file', 'class' => ($this->errors['name'] ? 'error' : '')));
927
928        $out .= '<br /><input type="radio" id="from_file" name="_from" value="file"'
929            .($selected=='file' ? ' checked="checked"' : '').'></input>';
930        $out .= sprintf('<label for="%s">%s:</label> ', 'from_file', Q($this->gettext('fromfile')));
931        $out .= $upload->show();
932        $out .= '</fieldset>';
933
934        $this->rc->output->add_gui_object('sieveform', 'filtersetform');
935
936        return $out;
937    }
938
939
940    function filter_form($attrib)
941    {
942        if (!$attrib['id'])
943            $attrib['id'] = 'rcmfilterform';
944
945        $fid = get_input_value('_fid', RCUBE_INPUT_GPC);
946        $scr = isset($this->form) ? $this->form : $this->script[$fid];
947
948        $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task));
949        $hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save'));
950        $hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0)));
951        $hiddenfields->add(array('name' => '_fid', 'value' => $fid));
952
953        $out = '<form name="filterform" action="./" method="post">'."\n";
954        $out .= $hiddenfields->show();
955
956        // 'any' flag
957        if (sizeof($scr['tests']) == 1 && $scr['tests'][0]['test'] == 'true' && !$scr['tests'][0]['not'])
958            $any = true;
959
960        // filter name input
961        $field_id = '_name';
962        $input_name = new html_inputfield(array('name' => '_name', 'id' => $field_id, 'size' => 30,
963            'class' => ($this->errors['name'] ? 'error' : '')));
964
965        if ($this->errors['name'])
966            $this->add_tip($field_id, $this->errors['name'], true);
967
968        if (isset($scr))
969            $input_name = $input_name->show($scr['name']);
970        else
971            $input_name = $input_name->show();
972
973        $out .= sprintf("\n<label for=\"%s\"><b>%s:</b></label> %s\n",
974            $field_id, Q($this->gettext('filtername')), $input_name);
975
976        // filter set selector
977        if ($this->rc->task == 'mail') {
978            $out .= sprintf("\n&nbsp;<label for=\"%s\"><b>%s:</b></label> %s\n",
979                $field_id, Q($this->gettext('filterset')),
980                $this->filtersets_list(array('id' => 'sievescriptname'), true));
981        }
982
983        $out .= '<br /><br /><fieldset><legend>' . Q($this->gettext('messagesrules')) . "</legend>\n";
984
985        // any, allof, anyof radio buttons
986        $field_id = '_allof';
987        $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'allof',
988            'onclick' => 'rule_join_radio(\'allof\')', 'class' => 'radio'));
989
990        if (isset($scr) && !$any)
991            $input_join = $input_join->show($scr['join'] ? 'allof' : '');
992        else
993            $input_join = $input_join->show();
994
995        $out .= sprintf("%s<label for=\"%s\">%s</label>&nbsp;\n",
996            $input_join, $field_id, Q($this->gettext('filterallof')));
997
998        $field_id = '_anyof';
999        $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'anyof',
1000            'onclick' => 'rule_join_radio(\'anyof\')', 'class' => 'radio'));
1001
1002        if (isset($scr) && !$any)
1003            $input_join = $input_join->show($scr['join'] ? '' : 'anyof');
1004        else
1005            $input_join = $input_join->show('anyof'); // default
1006
1007        $out .= sprintf("%s<label for=\"%s\">%s</label>\n",
1008            $input_join, $field_id, Q($this->gettext('filteranyof')));
1009
1010        $field_id = '_any';
1011        $input_join = new html_radiobutton(array('name' => '_join', 'id' => $field_id, 'value' => 'any',
1012            'onclick' => 'rule_join_radio(\'any\')', 'class' => 'radio'));
1013
1014        $input_join = $input_join->show($any ? 'any' : '');
1015
1016        $out .= sprintf("%s<label for=\"%s\">%s</label>\n",
1017            $input_join, $field_id, Q($this->gettext('filterany')));
1018
1019        $rows_num = isset($scr) ? sizeof($scr['tests']) : 1;
1020
1021        $out .= '<div id="rules"'.($any ? ' style="display: none"' : '').'>';
1022        for ($x=0; $x<$rows_num; $x++)
1023            $out .= $this->rule_div($fid, $x);
1024        $out .= "</div>\n";
1025
1026        $out .= "</fieldset>\n";
1027
1028        // actions
1029        $out .= '<fieldset><legend>' . Q($this->gettext('messagesactions')) . "</legend>\n";
1030
1031        $rows_num = isset($scr) ? sizeof($scr['actions']) : 1;
1032
1033        $out .= '<div id="actions">';
1034        for ($x=0; $x<$rows_num; $x++)
1035            $out .= $this->action_div($fid, $x);
1036        $out .= "</div>\n";
1037
1038        $out .= "</fieldset>\n";
1039
1040        $this->print_tips();
1041
1042        if ($scr['disabled']) {
1043            $this->rc->output->set_env('rule_disabled', true);
1044        }
1045        $this->rc->output->add_label(
1046            'managesieve.ruledeleteconfirm',
1047            'managesieve.actiondeleteconfirm'
1048        );
1049        $this->rc->output->add_gui_object('sieveform', 'filterform');
1050
1051        return $out;
1052    }
1053
1054    function rule_div($fid, $id, $div=true)
1055    {
1056        $rule     = isset($this->form) ? $this->form['tests'][$id] : $this->script[$fid]['tests'][$id];
1057        $rows_num = isset($this->form) ? sizeof($this->form['tests']) : sizeof($this->script[$fid]['tests']);
1058
1059        $out = $div ? '<div class="rulerow" id="rulerow' .$id .'">'."\n" : '';
1060
1061        $out .= '<table><tr><td class="rowactions">';
1062
1063        // headers select
1064        $select_header = new html_select(array('name' => "_header[]", 'id' => 'header'.$id,
1065            'onchange' => 'rule_header_select(' .$id .')'));
1066        foreach($this->headers as $name => $val)
1067            $select_header->add(Q($this->gettext($name)), Q($val));
1068        $select_header->add(Q($this->gettext('size')), 'size');
1069        $select_header->add(Q($this->gettext('...')), '...');
1070
1071        // TODO: list arguments
1072
1073        if ((isset($rule['test']) && $rule['test'] == 'header')
1074            && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers))
1075            $out .= $select_header->show($rule['arg1']);
1076        else if ((isset($rule['test']) && $rule['test'] == 'exists')
1077            && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers))
1078            $out .= $select_header->show($rule['arg']);
1079        else if (isset($rule['test']) && $rule['test'] == 'size')
1080            $out .= $select_header->show('size');
1081        else if (isset($rule['test']) && $rule['test'] != 'true')
1082            $out .= $select_header->show('...');
1083        else
1084            $out .= $select_header->show();
1085
1086        $out .= '</td><td class="rowtargets">';
1087
1088        if ((isset($rule['test']) && $rule['test'] == 'header')
1089            && (is_array($rule['arg1']) || !in_array($rule['arg1'], $this->headers)))
1090            $custom = is_array($rule['arg1']) ? implode(', ', $rule['arg1']) : $rule['arg1'];
1091        else if ((isset($rule['test']) && $rule['test'] == 'exists')
1092            && (is_array($rule['arg']) || !in_array($rule['arg'], $this->headers)))
1093            $custom = is_array($rule['arg']) ? implode(', ', $rule['arg']) : $rule['arg'];
1094
1095        $out .= '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
1096            <input type="text" name="_custom_header[]" id="custom_header_i'.$id.'" '
1097            . $this->error_class($id, 'test', 'header', 'custom_header_i')
1098            .' value="' .Q($custom). '" size="20" />&nbsp;</div>' . "\n";
1099
1100        // matching type select (operator)
1101        $select_op = new html_select(array('name' => "_rule_op[]", 'id' => 'rule_op'.$id,
1102            'style' => 'display:' .($rule['test']!='size' ? 'inline' : 'none'),
1103            'onchange' => 'rule_op_select('.$id.')'));
1104        $select_op->add(Q($this->gettext('filtercontains')), 'contains');
1105        $select_op->add(Q($this->gettext('filternotcontains')), 'notcontains');
1106        $select_op->add(Q($this->gettext('filteris')), 'is');
1107        $select_op->add(Q($this->gettext('filterisnot')), 'notis');
1108        $select_op->add(Q($this->gettext('filterexists')), 'exists');
1109        $select_op->add(Q($this->gettext('filternotexists')), 'notexists');
1110        $select_op->add(Q($this->gettext('filtermatches')), 'matches');
1111        $select_op->add(Q($this->gettext('filternotmatches')), 'notmatches');
1112        if (in_array('regex', $this->exts)) {
1113            $select_op->add(Q($this->gettext('filterregex')), 'regex');
1114            $select_op->add(Q($this->gettext('filternotregex')), 'notregex');
1115        }
1116        if (in_array('relational', $this->exts)) {
1117            $select_op->add(Q($this->gettext('countisgreaterthan')), 'count-gt');
1118            $select_op->add(Q($this->gettext('countisgreaterthanequal')), 'count-ge');
1119            $select_op->add(Q($this->gettext('countislessthan')), 'count-lt');
1120            $select_op->add(Q($this->gettext('countislessthanequal')), 'count-le');
1121            $select_op->add(Q($this->gettext('countequals')), 'count-eq');
1122            $select_op->add(Q($this->gettext('countnotequals')), 'count-ne');
1123            $select_op->add(Q($this->gettext('valueisgreaterthan')), 'value-gt');
1124            $select_op->add(Q($this->gettext('valueisgreaterthanequal')), 'value-ge');
1125            $select_op->add(Q($this->gettext('valueislessthan')), 'value-lt');
1126            $select_op->add(Q($this->gettext('valueislessthanequal')), 'value-le');
1127            $select_op->add(Q($this->gettext('valueequals')), 'value-eq');
1128            $select_op->add(Q($this->gettext('valuenotequals')), 'value-ne');
1129        }
1130
1131        // target input (TODO: lists)
1132
1133        if ($rule['test'] == 'header') {
1134            $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['type']);
1135            $target = $rule['arg2'];
1136        }
1137        else if ($rule['test'] == 'size') {
1138            $out .= $select_op->show();
1139            if (preg_match('/^([0-9]+)(K|M|G)*$/', $rule['arg'], $matches)) {
1140                $sizetarget = $matches[1];
1141                $sizeitem = $matches[2];
1142            }
1143        }
1144        else {
1145            $out .= $select_op->show(($rule['not'] ? 'not' : '').$rule['test']);
1146            $target = '';
1147        }
1148
1149        $out .= '<input type="text" name="_rule_target[]" id="rule_target' .$id. '"
1150            value="' .Q($target). '" size="20" ' . $this->error_class($id, 'test', 'target', 'rule_target')
1151            . ' style="display:' . ($rule['test']!='size' && $rule['test'] != 'exists' ? 'inline' : 'none') . '" />'."\n";
1152
1153        $select_size_op = new html_select(array('name' => "_rule_size_op[]", 'id' => 'rule_size_op'.$id));
1154        $select_size_op->add(Q($this->gettext('filterunder')), 'under');
1155        $select_size_op->add(Q($this->gettext('filterover')), 'over');
1156
1157        $out .= '<div id="rule_size' .$id. '" style="display:' . ($rule['test']=='size' ? 'inline' : 'none') .'">';
1158        $out .= $select_size_op->show($rule['test']=='size' ? $rule['type'] : '');
1159        $out .= '<input type="text" name="_rule_size_target[]" id="rule_size_i'.$id.'" value="'.$sizetarget.'" size="10" ' 
1160            . $this->error_class($id, 'test', 'sizetarget', 'rule_size_i') .' />
1161            <input type="radio" name="_rule_size_item['.$id.']" value=""'
1162                . (!$sizeitem ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('B').'
1163            <input type="radio" name="_rule_size_item['.$id.']" value="K"'
1164                . ($sizeitem=='K' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('KB').'
1165            <input type="radio" name="_rule_size_item['.$id.']" value="M"'
1166                . ($sizeitem=='M' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('MB').'
1167            <input type="radio" name="_rule_size_item['.$id.']" value="G"'
1168                . ($sizeitem=='G' ? ' checked="checked"' : '') .' class="radio" />'.rcube_label('GB');
1169        $out .= '</div>';
1170        $out .= '</td>';
1171
1172        // add/del buttons
1173        $out .= '<td class="rowbuttons">';
1174        $out .= '<a href="#" id="ruleadd' . $id .'" title="'. Q($this->gettext('add')). '"
1175            onclick="rcmail.managesieve_ruleadd(' . $id .')" class="button add"></a>';
1176        $out .= '<a href="#" id="ruledel' . $id .'" title="'. Q($this->gettext('del')). '"
1177            onclick="rcmail.managesieve_ruledel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
1178        $out .= '</td></tr></table>';
1179
1180        $out .= $div ? "</div>\n" : '';
1181
1182        return $out;
1183    }
1184
1185    function action_div($fid, $id, $div=true)
1186    {
1187        $action   = isset($this->form) ? $this->form['actions'][$id] : $this->script[$fid]['actions'][$id];
1188        $rows_num = isset($this->form) ? sizeof($this->form['actions']) : sizeof($this->script[$fid]['actions']);
1189
1190        $out = $div ? '<div class="actionrow" id="actionrow' .$id .'">'."\n" : '';
1191
1192        $out .= '<table><tr><td class="rowactions">';
1193
1194        // action select
1195        $select_action = new html_select(array('name' => "_action_type[$id]", 'id' => 'action_type'.$id,
1196            'onchange' => 'action_type_select(' .$id .')'));
1197        if (in_array('fileinto', $this->exts))
1198            $select_action->add(Q($this->gettext('messagemoveto')), 'fileinto');
1199        if (in_array('fileinto', $this->exts) && in_array('copy', $this->exts))
1200            $select_action->add(Q($this->gettext('messagecopyto')), 'fileinto_copy');
1201        $select_action->add(Q($this->gettext('messageredirect')), 'redirect');
1202        if (in_array('copy', $this->exts))
1203            $select_action->add(Q($this->gettext('messagesendcopy')), 'redirect_copy');
1204        if (in_array('reject', $this->exts))
1205            $select_action->add(Q($this->gettext('messagediscard')), 'reject');
1206        else if (in_array('ereject', $this->exts))
1207            $select_action->add(Q($this->gettext('messagediscard')), 'ereject');
1208        if (in_array('vacation', $this->exts))
1209            $select_action->add(Q($this->gettext('messagereply')), 'vacation');
1210        $select_action->add(Q($this->gettext('messagedelete')), 'discard');
1211        if (in_array('imapflags', $this->exts) || in_array('imap4flags', $this->exts)) {
1212            $select_action->add(Q($this->gettext('setflags')), 'setflag');
1213            $select_action->add(Q($this->gettext('addflags')), 'addflag');
1214            $select_action->add(Q($this->gettext('removeflags')), 'removeflag');
1215        }
1216        $select_action->add(Q($this->gettext('rulestop')), 'stop');
1217
1218        $select_type = $action['type'];
1219        if (in_array($action['type'], array('fileinto', 'redirect')) && $action['copy']) {
1220            $select_type .= '_copy';
1221        }
1222
1223        $out .= $select_action->show($select_type);
1224        $out .= '</td>';
1225
1226        // actions target inputs
1227        $out .= '<td class="rowtargets">';
1228        // shared targets
1229        $out .= '<input type="text" name="_action_target['.$id.']" id="action_target' .$id. '" '
1230            .'value="' .($action['type']=='redirect' ? Q($action['target'], 'strict', false) : ''). '" size="40" '
1231            .'style="display:' .($action['type']=='redirect' ? 'inline' : 'none') .'" '
1232            . $this->error_class($id, 'action', 'target', 'action_target') .' />';
1233        $out .= '<textarea name="_action_target_area['.$id.']" id="action_target_area' .$id. '" '
1234            .'rows="3" cols="40" '. $this->error_class($id, 'action', 'targetarea', 'action_target_area')
1235            .'style="display:' .(in_array($action['type'], array('reject', 'ereject')) ? 'inline' : 'none') .'">'
1236            . (in_array($action['type'], array('reject', 'ereject')) ? Q($action['target'], 'strict', false) : '')
1237            . "</textarea>\n";
1238
1239        // vacation
1240        $out .= '<div id="action_vacation' .$id.'" style="display:' .($action['type']=='vacation' ? 'inline' : 'none') .'">';
1241        $out .= '<span class="label">'. Q($this->gettext('vacationreason')) .'</span><br />'
1242            .'<textarea name="_action_reason['.$id.']" id="action_reason' .$id. '" '
1243            .'rows="3" cols="45" '. $this->error_class($id, 'action', 'reason', 'action_reason') . '>'
1244            . Q($action['reason'], 'strict', false) . "</textarea>\n";
1245        $out .= '<br /><span class="label">' .Q($this->gettext('vacationsubject')) . '</span><br />'
1246            .'<input type="text" name="_action_subject['.$id.']" id="action_subject'.$id.'" '
1247            .'value="' . (is_array($action['subject']) ? Q(implode(', ', $action['subject']), 'strict', false) : $action['subject']) . '" size="50" '
1248            . $this->error_class($id, 'action', 'subject', 'action_subject') .' />';
1249        $out .= '<br /><span class="label">' .Q($this->gettext('vacationaddresses')) . '</span><br />'
1250            .'<input type="text" name="_action_addresses['.$id.']" id="action_addr'.$id.'" '
1251            .'value="' . (is_array($action['addresses']) ? Q(implode(', ', $action['addresses']), 'strict', false) : $action['addresses']) . '" size="50" '
1252            . $this->error_class($id, 'action', 'addresses', 'action_addr') .' />';
1253        $out .= '<br /><span class="label">' . Q($this->gettext('vacationdays')) . '</span><br />'
1254            .'<input type="text" name="_action_days['.$id.']" id="action_days'.$id.'" '
1255            .'value="' .Q($action['days'], 'strict', false) . '" size="2" '
1256            . $this->error_class($id, 'action', 'days', 'action_days') .' />';
1257        $out .= '</div>';
1258
1259        // flags
1260        $flags = array(
1261            'read'      => '\\Seen',
1262            'answered'  => '\\Answered',
1263            'flagged'   => '\\Flagged',
1264            'deleted'   => '\\Deleted',
1265            'draft'     => '\\Draft',
1266        );
1267        $flags_target = (array)$action['target'];
1268
1269        $out .= '<div id="action_flags' .$id.'" style="display:' 
1270            . (preg_match('/^(set|add|remove)flag$/', $action['type']) ? 'inline' : 'none') . '"'
1271            . $this->error_class($id, 'action', 'flags', 'action_flags') . '>';
1272        foreach ($flags as $fidx => $flag) {
1273            $out .= '<input type="checkbox" name="_action_flags[' .$id .'][]" value="' . $flag . '"'
1274                . (in_array_nocase($flag, $flags_target) ? 'checked="checked"' : '') . ' />'
1275                . Q($this->gettext('flag'.$fidx)) .'<br>';
1276        }
1277        $out .= '</div>';
1278
1279        // mailbox select
1280        if ($action['type'] == 'fileinto')
1281            $mailbox = $this->mod_mailbox($action['target'], 'out');
1282        else
1283            $mailbox = '';
1284
1285        $this->rc->imap_connect();
1286        $select = rcmail_mailbox_select(array(
1287            'realnames' => false,
1288            'maxlength' => 100,
1289            'id' => 'action_mailbox' . $id,
1290            'name' => "_action_mailbox[$id]",
1291            'style' => 'display:'.(!isset($action) || $action['type']=='fileinto' ? 'inline' : 'none')
1292        ));
1293        $out .= $select->show($mailbox);
1294        $out .= '</td>';
1295
1296        // add/del buttons
1297        $out .= '<td class="rowbuttons">';
1298        $out .= '<a href="#" id="actionadd' . $id .'" title="'. Q($this->gettext('add')). '"
1299            onclick="rcmail.managesieve_actionadd(' . $id .')" class="button add"></a>';
1300        $out .= '<a href="#" id="actiondel' . $id .'" title="'. Q($this->gettext('del')). '"
1301            onclick="rcmail.managesieve_actiondel(' . $id .')" class="button del' . ($rows_num<2 ? ' disabled' : '') .'"></a>';
1302        $out .= '</td>';
1303
1304        $out .= '</tr></table>';
1305
1306        $out .= $div ? "</div>\n" : '';
1307
1308        return $out;
1309    }
1310
1311    private function genid()
1312    {
1313        $result = intval(rcube_timer());
1314        return $result;
1315    }
1316
1317    private function strip_value($str, $allow_html=false)
1318    {
1319        if (!$allow_html)
1320            $str = strip_tags($str);
1321
1322        return trim($str);
1323    }
1324
1325    private function error_class($id, $type, $target, $elem_prefix='')
1326    {
1327        // TODO: tooltips
1328        if (($type == 'test' && ($str = $this->errors['tests'][$id][$target])) ||
1329            ($type == 'action' && ($str = $this->errors['actions'][$id][$target]))
1330        ) {
1331            $this->add_tip($elem_prefix.$id, $str, true);
1332            return ' class="error"';
1333        }
1334
1335        return '';
1336    }
1337
1338    private function add_tip($id, $str, $error=false)
1339    {
1340        if ($error)
1341            $str = html::span('sieve error', $str);
1342
1343        $this->tips[] = array($id, $str);
1344    }
1345
1346    private function print_tips()
1347    {
1348        if (empty($this->tips))
1349            return;
1350
1351        $script = JS_OBJECT_NAME.'.managesieve_tip_register('.json_encode($this->tips).');';
1352        $this->rc->output->add_script($script, 'foot');
1353    }
1354
1355    /**
1356     * Converts mailbox name from/to UTF7-IMAP from/to internal Sieve encoding
1357     * with delimiter replacement.
1358     *
1359     * @param string $mailbox Mailbox name
1360     * @param string $mode    Conversion direction ('in'|'out')
1361     *
1362     * @return string Mailbox name
1363     */
1364    private function mod_mailbox($mailbox, $mode = 'out')
1365    {
1366        $delimiter         = $_SESSION['imap_delimiter'];
1367        $replace_delimiter = $this->rc->config->get('managesieve_replace_delimiter');
1368        $mbox_encoding     = $this->rc->config->get('managesieve_mbox_encoding', 'UTF7-IMAP');
1369
1370        if ($mode == 'out') {
1371            $mailbox = rcube_charset_convert($mailbox, $mbox_encoding, 'UTF7-IMAP');
1372            if ($replace_delimiter && $replace_delimiter != $delimiter)
1373                $mailbox = str_replace($replace_delimiter, $delimiter, $mailbox);
1374        }
1375        else {
1376            $mailbox = rcube_charset_convert($mailbox, 'UTF7-IMAP', $mbox_encoding);
1377            if ($replace_delimiter && $replace_delimiter != $delimiter)
1378                $mailbox = str_replace($delimiter, $replace_delimiter, $mailbox);
1379        }
1380
1381        return $mailbox;
1382    }
1383
1384    /**
1385     * List sieve scripts
1386     *
1387     * @return array Scripts list
1388     */
1389    public function list_scripts()
1390    {
1391        if ($this->list !== null) {
1392            return $this->list;
1393        }
1394
1395        $this->list = $this->sieve->get_scripts();
1396
1397        // Handle active script(s) and list of scripts according to Kolab's KEP:14
1398        if ($this->rc->config->get('managesieve_kolab_master')) {
1399
1400            // Skip protected names
1401            foreach ((array)$this->list as $idx => $name) {
1402                $_name = strtoupper($name);
1403                if ($_name == 'MASTER')
1404                    $master_script = $name;
1405                else if ($_name == 'MANAGEMENT')
1406                    $management_script = $name;
1407                else if($_name == 'USER')
1408                    $user_script = $name;
1409                else
1410                    continue;
1411
1412                unset($this->list[$idx]);
1413            }
1414
1415            // get active script(s), read USER script
1416            if ($user_script) {
1417                $extension = $this->rc->config->get('managesieve_filename_extension', '.sieve');
1418                $filename_regex = '/'.preg_quote($extension, '/').'$/';
1419                $_SESSION['managesieve_user_script'] = $user_script;
1420
1421                $this->sieve->load($user_script);
1422
1423                foreach ($this->sieve->script->as_array() as $rules) {
1424                    foreach ($rules['actions'] as $action) {
1425                        if ($action['type'] == 'include' && empty($action['global'])) {
1426                            $name = preg_replace($filename_regex, '', $action['target']);
1427                            $this->active[] = $name;
1428                        }
1429                    }
1430                }
1431            }
1432            // create USER script if it doesn't exist
1433            else {
1434                $content = "# USER Management Script\n"
1435                    ."#\n"
1436                    ."# This script includes the various active sieve scripts\n"
1437                    ."# it is AUTOMATICALLY GENERATED. DO NOT EDIT MANUALLY!\n"
1438                    ."#\n"
1439                    ."# For more information, see http://wiki.kolab.org/KEP:14#USER\n"
1440                    ."#\n";
1441                if ($this->sieve->save_script('USER', $content)) {
1442                    $_SESSION['managesieve_user_script'] = 'USER';
1443                    if (empty($this->master_file))
1444                        $this->sieve->activate('USER');
1445                }
1446            }
1447        }
1448        else if (!empty($this->list)) {
1449            // Get active script name
1450            if ($active = $this->sieve->get_active()) {
1451                $this->active = array($active);
1452            }
1453        }
1454
1455        return $this->list;
1456    }
1457
1458    /**
1459     * Removes sieve script
1460     *
1461     * @param string $name Script name
1462     *
1463     * @return bool True on success, False on failure
1464     */
1465    public function remove_script($name)
1466    {
1467        $result = $this->sieve->remove($name);
1468
1469        // Kolab's KEP:14
1470        if ($result && $this->rc->config->get('managesieve_kolab_master')) {
1471            $this->deactivate_script($name);
1472        }
1473
1474        return $result;
1475    }
1476
1477    /**
1478     * Activates sieve script
1479     *
1480     * @param string $name Script name
1481     *
1482     * @return bool True on success, False on failure
1483     */
1484    public function activate_script($name)
1485    {
1486        // Kolab's KEP:14
1487        if ($this->rc->config->get('managesieve_kolab_master')) {
1488            $extension   = $this->rc->config->get('managesieve_filename_extension', '.sieve');
1489            $user_script = $_SESSION['managesieve_user_script'];
1490
1491            // if the script is not active...
1492            if ($user_script && ($key = array_search($name, $this->active)) === false) {
1493                // ...rewrite USER file adding appropriate include command
1494                if ($this->sieve->load($user_script)) {
1495                    // @TODO: include order
1496                    $this->sieve->script->add_rule(array(
1497                        'actions' => array(
1498                            0 => array(
1499                                'target'   => $name.$extension,
1500                                'type'     => 'include',
1501                                'personal' => true,
1502                    ))));
1503
1504                    $result = $this->sieve->save();
1505                    if ($result) {
1506                        $this->active[] = $name;
1507                    }
1508                }
1509            }
1510        }
1511        else {
1512            $result = $this->sieve->activate($name);
1513            if ($result)
1514                $this->active = array($name);
1515        }
1516
1517        return $result;
1518    }
1519
1520    /**
1521     * Deactivates sieve script
1522     *
1523     * @param string $name Script name
1524     *
1525     * @return bool True on success, False on failure
1526     */
1527    public function deactivate_script($name)
1528    {
1529        // Kolab's KEP:14
1530        if ($this->rc->config->get('managesieve_kolab_master')) {
1531            $extension   = $this->rc->config->get('managesieve_filename_extension', '.sieve');
1532            $user_script = $_SESSION['managesieve_user_script'];
1533
1534            // if the script is active...
1535            if ($user_script && ($key = array_search($name, $this->active)) !== false) {
1536                // ...rewrite USER file removing appropriate include command
1537                if ($this->sieve->load($user_script)) {
1538                    $script = $this->sieve->script->as_array();
1539                    $name   = $name.$extension;
1540
1541                    foreach ($script as $rid => $rules) {
1542                        foreach ($rules['actions'] as $aid => $action) {
1543                            if ($action['type'] == 'include' && empty($action['global'])
1544                                && $action['target'] == $name
1545                            ) {
1546                                break 2;
1547                            }
1548                        }
1549                    }
1550
1551                    // Entry found
1552                    if ($rid < count($script)) {
1553                        $this->sieve->script->delete_rule($rid);
1554                        $result = $this->sieve->save();
1555                        if ($result) {
1556                            unset($this->active[$key]);
1557                        }
1558                    }
1559                }
1560            }
1561        }
1562        else {
1563            $result = $this->sieve->deactivate();
1564            if ($result)
1565                $this->active = array();
1566        }
1567
1568        return $result;
1569    }
1570
1571    /**
1572     * Saves current script (adding some variables)
1573     */
1574    public function save_script($name = null)
1575    {
1576        // Kolab's KEP:14
1577        if ($this->rc->config->get('managesieve_kolab_master')) {
1578            $this->sieve->script->set_var('editor', self::PROGNAME);
1579            $this->sieve->script->set_var('editor_version', self::VERSION);
1580        }
1581
1582        return $this->sieve->save($name);
1583    }
1584}
Note: See TracBrowser for help on using the repository browser.