* * Configuration (main.inc.php): // managesieve server port $rcmail_config['managesieve_port'] = 2000; // managesieve server address $rcmail_config['managesieve_host'] = 'localhost'; // use or not TLS for managesieve server connection // it's because I've problems with TLS and dovecot's managesieve plugin // and it's not needed on localhost $rcmail_config['managesieve_usetls'] = false; // default contents of filters script (eg. default spam filter) $rcmail_config['managesieve_default'] = '/etc/dovecot/sieve/global'; // I need this because my dovecot (with listescape plugin) uses // ':' delimiter, but creates folders with dot delimiter $rcmail_config['managesieve_replace_delimiter'] = ''; // disabled sieve extensions (body, copy, date, editheader, encoded-character, // envelope, environment, ereject, fileinto, ihave, imap4flags, index, // mailbox, mboxmetadata, regex, reject, relational, servermetadata, // spamtest, spamtestplus, subaddress, vacation, variables, virustest, etc. // Note: not all extensions are implemented $rcmail_config['managesieve_disabled_extensions'] = array(); */ class managesieve extends rcube_plugin { public $task = 'settings'; private $rc; private $sieve; private $errors; private $form; private $script = array(); private $exts = array(); private $headers = array( 'subject' => 'Subject', 'sender' => 'From', 'recipient' => 'To', ); function init() { // add Tab label/title $this->add_texts('localization/', array('filters','managefilters')); // register actions $this->register_action('plugin.managesieve', array($this, 'managesieve_init')); $this->register_action('plugin.managesieve-save', array($this, 'managesieve_save')); // include main js script $this->include_script('managesieve.js'); } function managesieve_start() { $rcmail = rcmail::get_instance(); $this->rc = &$rcmail; // register UI objects $this->rc->output->add_handlers(array( 'filterslist' => array($this, 'filters_list'), 'filterframe' => array($this, 'filter_frame'), 'filterform' => array($this, 'filter_form'), )); require_once($this->home . '/lib/Net/Sieve.php'); require_once($this->home . '/lib/rcube_sieve.php'); // try to connect to managesieve server and to fetch the script $this->sieve = new rcube_sieve($_SESSION['username'], $this->rc->decrypt($_SESSION['password']), $this->rc->config->get('managesieve_host', 'localhost'), $this->rc->config->get('managesieve_port', 2000), $this->rc->config->get('managesieve_usetls', false), $this->rc->config->get('managesieve_disabled_extensions')); $error = $this->sieve->error(); if ($error == SIEVE_ERROR_NOT_EXISTS) { // if script not exists build default script contents $script_file = $this->rc->config->get('managesieve_default'); if ($script_file && is_readable($script_file)) $this->sieve->script->add_text(file_get_contents($script_file)); // that's not exactly an error $error = false; } elseif ($error) { switch ($error) { case SIEVE_ERROR_CONNECTION: case SIEVE_ERROR_LOGIN: $this->rc->output->show_message('managesieve.filterconnerror', 'error'); break; default: $this->rc->output->show_message('managesieve.filterunknownerror', 'error'); break; } // to disable 'Add filter' button set env variable $this->rc->output->set_env('filterconnerror', true); } // finally set script objects if ($error) { $this->script = array(); } else { $this->script = $this->sieve->script->as_array(); $this->exts = $this->sieve->get_extensions(); } return $error; } function managesieve_init() { // Init plugin and handle managesieve connection $error = $this->managesieve_start(); // Handle user requests if ($action = get_input_value('_act', RCUBE_INPUT_GPC)) { $fid = (int) get_input_value('_fid', RCUBE_INPUT_GET); if ($action=='up' && !$error) { if ($fid && isset($this->script[$fid]) && isset($this->script[$fid-1])) { if ($this->sieve->script->update_rule($fid, $this->script[$fid-1]) !== false && $this->sieve->script->update_rule($fid-1, $this->script[$fid]) !== false) $result = $this->sieve->save(); if ($result) { // $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); $this->rc->output->command('managesieve_updatelist', 'up', '', $fid); } else $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); } } elseif ($action=='down' && !$error) { if (isset($this->script[$fid]) && isset($this->script[$fid+1])) { if ($this->sieve->script->update_rule($fid, $this->script[$fid+1]) !== false && $this->sieve->script->update_rule($fid+1, $this->script[$fid]) !== false) $result = $this->sieve->save(); if ($result) { // $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); $this->rc->output->command('managesieve_updatelist', 'down', '', $fid); } else $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); } } elseif ($action=='delete' && !$error) { if (isset($this->script[$fid])) { if ($this->sieve->script->delete_rule($fid)) $result = $this->sieve->save(); if (!$result) $this->rc->output->show_message('managesieve.filterdeleteerror', 'error'); else { $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation'); $this->rc->output->command('managesieve_updatelist', 'delete', '', $fid); } } } elseif ($action=='ruleadd') { $rid = get_input_value('_rid', RCUBE_INPUT_GPC); $id = $this->genid(); $content = $this->rule_div($fid, $id, false); $this->rc->output->command('managesieve_rulefill', $content, $id, $rid); } elseif ($action=='actionadd') { $aid = get_input_value('_aid', RCUBE_INPUT_GPC); $id = $this->genid(); $content = $this->action_div($fid, $id, false); $this->rc->output->command('managesieve_actionfill', $content, $id, $aid); } $this->rc->output->send(); } $this->managesieve_send(); } function managesieve_save() { // Init plugin and handle managesieve connection $error = $this->managesieve_start(); // add/edit action if (isset($_POST['_name'])) { $name = trim(get_input_value('_name', RCUBE_INPUT_POST)); $fid = trim(get_input_value('_fid', RCUBE_INPUT_POST)); $join = trim(get_input_value('_join', RCUBE_INPUT_POST)); // and arrays $headers = $_POST['_header']; $cust_headers = $_POST['_custom_header']; $ops = $_POST['_rule_op']; $sizeops = $_POST['_rule_size_op']; $sizeitems = $_POST['_rule_size_item']; $sizetargets = $_POST['_rule_size_target']; $targets = $_POST['_rule_target']; $act_types = $_POST['_action_type']; $mailboxes = $_POST['_action_mailbox']; $act_targets = $_POST['_action_target']; $area_targets = $_POST['_action_target_area']; $reasons = $_POST['_action_reason']; $addresses = $_POST['_action_addresses']; $days = $_POST['_action_days']; // we need a "hack" for radiobuttons foreach ($sizeitems as $item) $items[] = $item; $this->form['join'] = $join=='allof' ? true : false; $this->form['name'] = $name; $this->form['tests'] = array(); $this->form['actions'] = array(); if ($name == '') $this->errors['name'] = $this->gettext('cannotbeempty'); else foreach($this->script as $idx => $rule) if($rule['name'] == $name && $idx != $fid) { $this->errors['name'] = $this->gettext('ruleexist'); break; } $i = 0; // rules if ($join == 'any') { $this->form['tests'][0]['test'] = 'true'; } else foreach($headers as $idx => $header) { $header = $this->strip_value($header); $target = $this->strip_value($targets[$idx]); $op = $this->strip_value($ops[$idx]); // normal header if (in_array($header, $this->headers)) { if(preg_match('/^not/', $op)) $this->form['tests'][$i]['not'] = true; $type = preg_replace('/^not/', '', $op); if ($type == 'exists') { $this->form['tests'][$i]['test'] = 'exists'; $this->form['tests'][$i]['arg'] = $header; } else { $this->form['tests'][$i]['type'] = $type; $this->form['tests'][$i]['test'] = 'header'; $this->form['tests'][$i]['arg1'] = $header; $this->form['tests'][$i]['arg2'] = $target; if ($target == '') $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty'); } } else switch ($header) { case 'size': $sizeop = $this->strip_value($sizeops[$idx]); $sizeitem = $this->strip_value($items[$idx]); $sizetarget = $this->strip_value($sizetargets[$idx]); $this->form['tests'][$i]['test'] = 'size'; $this->form['tests'][$i]['type'] = $sizeop; $this->form['tests'][$i]['arg'] = $sizetarget.$sizeitem; if (!preg_match('/^[0-9]+(K|M|G)*$/i', $sizetarget)) $this->errors['tests'][$i]['sizetarget'] = $this->gettext('wrongformat'); break; case '...': $cust_header = $this->strip_value($cust_headers[$idx]); if(preg_match('/^not/', $op)) $this->form['tests'][$i]['not'] = true; $type = preg_replace('/^not/', '', $op); if ($cust_header == '') $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty'); elseif (!preg_match('/^[a-z0-9-]+$/i', $cust_header)) $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars'); if ($type == 'exists') { $this->form['tests'][$i]['test'] = 'exists'; $this->form['tests'][$i]['arg'] = $cust_header; } else { $this->form['tests'][$i]['test'] = 'header'; $this->form['tests'][$i]['type'] = $type; $this->form['tests'][$i]['arg1'] = $cust_header; $this->form['tests'][$i]['arg2'] = $target; if ($target == '') $this->errors['tests'][$i]['target'] = $this->gettext('cannotbeempty'); } break; } $i++; } $i = 0; // actions foreach($act_types as $idx => $type) { $type = $this->strip_value($type); $target = $this->strip_value($act_targets[$idx]); $this->form['actions'][$i]['type'] = $type; switch ($type) { case 'fileinto': $mailbox = $this->strip_value($mailboxes[$idx]); $this->form['actions'][$i]['target'] = $mailbox; break; case 'reject': case 'ereject': $target = $this->strip_value($area_targets[$idx]); $this->form['actions'][$i]['target'] = str_replace("\r\n", "\n", $target); // if ($target == '') // $this->errors['actions'][$i]['targetarea'] = $this->gettext('cannotbeempty'); break; case 'redirect': $this->form['actions'][$i]['target'] = $target; if ($this->form['actions'][$i]['target'] == '') $this->errors['actions'][$i]['target'] = $this->gettext('cannotbeempty'); else if (!$this->check_email($this->form['actions'][$i]['target'])) $this->errors['actions'][$i]['target'] = $this->gettext('noemailwarning'); break; case 'vacation': $reason = $this->strip_value($reasons[$idx]); $this->form['actions'][$i]['reason'] = str_replace("\r\n", "\n", $reason); $this->form['actions'][$i]['days'] = $days[$idx]; $this->form['actions'][$i]['addresses'] = explode(',', $addresses[$idx]); // @TODO: vacation :subject, :mime, :from, :handle if ($this->form['actions'][$i]['addresses']) { foreach($this->form['actions'][$i]['addresses'] as $aidx => $address) { $address = trim($address); if (!$address) unset($this->form['actions'][$i]['addresses'][$aidx]); else if(!$this->check_email($address)) { $this->errors['actions'][$i]['addresses'] = $this->gettext('noemailwarning'); break; } else $this->form['actions'][$i]['addresses'][$aidx] = $address; } } if ($this->form['actions'][$i]['reason'] == '') $this->errors['actions'][$i]['reason'] = $this->gettext('cannotbeempty'); if ($this->form['actions'][$i]['days'] && !preg_match('/^[0-9]+$/', $this->form['actions'][$i]['days'])) $this->errors['actions'][$i]['days'] = $this->gettext('forbiddenchars'); break; } $i++; } if (!$this->errors) { // zapis skryptu if (!isset($this->script[$fid])) { $fid = $this->sieve->script->add_rule($this->form); $new = true; } else $fid = $this->sieve->script->update_rule($fid, $this->form); if ($fid !== false) $save = $this->sieve->save(); if ($save && $fid !== false) { $this->rc->output->show_message('managesieve.filtersaved', 'confirmation'); $this->rc->output->add_script(sprintf("rcmail.managesieve_updatelist('%s', '%s', %d);", isset($new) ? 'add' : 'update', $this->form['name'], $fid), 'foot'); // $this->rc->output->command('managesieve_updatelist', isset($new) ? 'add' : 'update', $this->form['name'], $fid); // $this->rc->output->send(); } else { $this->rc->output->show_message('managesieve.filtersaveerror', 'error'); // $this->rc->output->send(); } } } $this->managesieve_send(); } private function managesieve_send() { // Handle form action if (isset($_GET['_framed']) || isset($_POST['_framed'])) $this->rc->output->send('managesieve.managesieveedit'); else { $this->rc->output->set_pagetitle($this->gettext('filters')); $this->rc->output->send('managesieve.managesieve'); } } // return the filters list as HTML table function filters_list($attrib) { // add id to message list table if not specified if (!strlen($attrib['id'])) $attrib['id'] = 'rcmfilterslist'; // define list of cols to be displayed $a_show_cols = array('managesieve.filtername'); foreach($this->script as $idx => $filter) $result[] = array('managesieve.filtername' => $filter['name'], 'id' => $idx); // create XHTML table $out = rcube_table_output($attrib, $result, $a_show_cols, 'id'); // set client env $this->rc->output->add_gui_object('filterslist', $attrib['id']); $this->rc->output->include_script('list.js'); // add some labels to client $this->rc->output->add_label('managesieve.filterconfirmdelete'); return $out; } function filter_frame($attrib) { if (!$attrib['id']) $attrib['id'] = 'rcmfilterframe'; $attrib['name'] = $attrib['id']; $this->rc->output->set_env('contentframe', $attrib['name']); $this->rc->output->set_env('blankpage', $attrib['src'] ? $this->rc->output->abs_url($attrib['src']) : 'program/blank.gif'); return html::tag('iframe', $attrib); } function filter_form($attrib) { if (!$attrib['id']) $attrib['id'] = 'rcmfilterform'; $fid = get_input_value('_fid', RCUBE_INPUT_GPC); $scr = isset($this->form) ? $this->form : $this->script[$fid]; $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $this->rc->task)); $hiddenfields->add(array('name' => '_action', 'value' => 'plugin.managesieve-save')); $hiddenfields->add(array('name' => '_framed', 'value' => ($_POST['_framed'] || $_GET['_framed'] ? 1 : 0))); $hiddenfields->add(array('name' => '_fid', 'value' => $fid)); $out = '