alecpl
2009-11-02 ac67db19802abd7d2a18c1bb804c3bfd887e9bdf
plugins/managesieve/managesieve.php
@@ -7,36 +7,10 @@
 * It's clickable interface which operates on text scripts and communicates
 * with server using managesieve protocol. Adds Filters tab in Settings.
 *
 * @version 1.0
 * @version 2.0
 * @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl>
 *
 * 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();
 * Configuration (see config.inc.php.dist):
 */
class managesieve extends rcube_plugin
@@ -61,7 +35,7 @@
    $this->add_texts('localization/', array('filters','managefilters'));
    // register actions
    $this->register_action('plugin.managesieve', array($this, 'managesieve_init'));
    $this->register_action('plugin.managesieve', array($this, 'managesieve_actions'));
    $this->register_action('plugin.managesieve-save', array($this, 'managesieve_save'));
    // include main js script
@@ -73,11 +47,15 @@
    $rcmail = rcmail::get_instance();
    $this->rc = &$rcmail;
    $this->load_config();
    // register UI objects
    $this->rc->output->add_handlers(array(
   'filterslist' => array($this, 'filters_list'),
   'filtersetslist' => array($this, 'filtersets_list'),
   'filterframe' => array($this, 'filter_frame'),
   'filterform' => array($this, 'filter_form'),
   'filtersetform' => array($this, 'filterset_form'),
    ));
    require_once($this->home . '/lib/Net/Sieve.php');
@@ -86,26 +64,54 @@
    // 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'),
   str_replace('%h', $_SESSION['imap_host'], $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'));
   $this->rc->config->get('managesieve_disabled_extensions'),
   $this->rc->config->get('managesieve_debug', false)
    );
    $error = $this->sieve->error();
    if (!($error = $this->sieve->error())) {
      $list = $this->sieve->get_scripts();
      $active = $this->sieve->get_active();
      $_SESSION['managesieve_active'] = $active;
      if (!empty($_GET['_sid'])) {
        $script_name = get_input_value('_sid', RCUBE_INPUT_GET);
      } else if (!empty($_SESSION['managesieve_current'])) {
        $script_name = $_SESSION['managesieve_current'];
      } else {
        // get active script
   if ($active) {
     $script_name = $active;
        } else if ($list) {
          $script_name = $list[0];
        // create a new (initial) script
        } else {
          // if script not exists build default script contents
          $script_file = $this->rc->config->get('managesieve_default');
     $script_name = 'roundcube';
          if ($script_file && is_readable($script_file))
       $content = file_get_contents($script_file);
    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;
     // add script and set it active
     if ($this->sieve->save_script($script_name, $content))
            if ($this->sieve->activate($script_name))
         $_SESSION['managesieve_active'] = $script_name;
   }
      }
      if ($script_name)
        $this->sieve->load($script_name);
      $error = $this->sieve->error();
    }
    elseif ($error)
    // finally set script objects
    if ($error)
    {
      switch ($error)
      {
      switch ($error) {
   case SIEVE_ERROR_CONNECTION:
   case SIEVE_ERROR_LOGIN:
          $this->rc->output->show_message('managesieve.filterconnerror', 'error');  
@@ -114,26 +120,22 @@
          $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();
      $this->rc->output->set_env('active_set', $_SESSION['managesieve_active']);
      $_SESSION['managesieve_current'] = $this->sieve->current;
    }
    
    return $error;
  }
  function managesieve_init()
  function managesieve_actions()
  {
    // Init plugin and handle managesieve connection
    $error = $this->managesieve_start();
@@ -158,7 +160,7 @@
            $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
        }
      }
      elseif ($action=='down' && !$error)
      else if ($action=='down' && !$error)
      {
        if (isset($this->script[$fid]) && isset($this->script[$fid+1]))
        {
@@ -166,27 +168,56 @@
          && $this->sieve->script->update_rule($fid+1, $this->script[$fid]) !== false)
       $result = $this->sieve->save();
      
          if ($result) {
          if ($result === true) {
//          $this->rc->output->show_message('managesieve.filtersaved', 'confirmation');
       $this->rc->output->command('managesieve_updatelist', 'down', '', $fid);
          } else
          } else {
            $this->rc->output->show_message('managesieve.filtersaveerror', 'error');
        }
          }
   }
      }
      elseif ($action=='delete' && !$error)
      else if ($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 {
          if ($result === true) {
       $this->rc->output->show_message('managesieve.filterdeleted', 'confirmation');
       $this->rc->output->command('managesieve_updatelist', 'delete', '', $fid);
          } else {
            $this->rc->output->show_message('managesieve.filterdeleteerror', 'error');
          }
        }
   }
      }
      else if ($action=='setact' && !$error)
      {
        $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
   $result = $this->sieve->activate($script_name);
   if ($result === true) {
          $this->rc->output->set_env('active_set', $script_name);
     $this->rc->output->show_message('managesieve.setactivated', 'confirmation');
     $this->rc->output->command('enable_command', 'plugin.managesieve-setact', false);
     $this->rc->output->command('managesieve_reset', $script_name);
     $_SESSION['managesieve_active'] = $script_name;
   } else {
          $this->rc->output->show_message('managesieve.setactivateerror', 'error');
   }
      }
      else if ($action=='setdel' && !$error)
      {
        $script_name = get_input_value('_set', RCUBE_INPUT_GPC);
   $result = $this->sieve->remove($script_name);
   if ($result === true) {
     $this->rc->output->show_message('managesieve.setdeleted', 'confirmation');
     $this->rc->output->command('managesieve_reload');
     rcube_sess_unset('managesieve_current');
   } else {
          $this->rc->output->show_message('managesieve.setdeleteerror', 'error');
   }
      }
      elseif ($action=='ruleadd')
      {
@@ -216,8 +247,29 @@
    // Init plugin and handle managesieve connection
    $error = $this->managesieve_start();
    // add/edit action
    if (isset($_POST['_name']))
    // filters set add action
    if (!empty($_POST['_newset']))
    {
      $name = get_input_value('_name', RCUBE_INPUT_GPC);
      $copy = get_input_value('_copy', RCUBE_INPUT_GPC);
      if (!$name)
   $error = 'managesieve.emptyname';
      else if (mb_strlen($name)>128)
   $error = 'managesieve.nametoolong';
      else if (!$this->sieve->copy($name, $copy))
   $error = 'managesieve.setcreateerror';
      if (!$error) {
   $this->rc->output->show_message('managesieve.setcreated', 'confirmation');
   $this->rc->output->command('parent.managesieve_reload', $name);
//   rcube_sess_unset('managesieve_current');
      } else {
        $this->rc->output->show_message($error, 'error');
      }
    }
    // filter add/edit action
    else if (isset($_POST['_name']))
    {
      $name = trim(get_input_value('_name', RCUBE_INPUT_POST));
      $fid = trim(get_input_value('_fid', RCUBE_INPUT_POST));
@@ -308,7 +360,7 @@
      $this->errors['tests'][$i]['sizetarget'] = $this->gettext('wrongformat');
         break;
       case '...':
              $cust_header = $this->strip_value($cust_headers[$idx]);
              $cust_header = $headers = $this->strip_value($cust_headers[$idx]);
              if(preg_match('/^not/', $op))
      $this->form['tests'][$i]['not'] = true;
@@ -316,10 +368,22 @@
              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')
              else {
           $headers = preg_split('/[\s,]+/', $cust_header, -1, PREG_SPLIT_NO_EMPTY);
      if (!count($headers))
            $this->errors['tests'][$i]['header'] = $this->gettext('cannotbeempty');
      else {
        foreach ($headers as $hr)
          if (!preg_match('/^[a-z0-9-]+$/i', $hr))
                $this->errors['tests'][$i]['header'] = $this->gettext('forbiddenchars');
      }
         }
         if (empty($this->errors['tests'][$i]['header']))
      $cust_header = (is_array($headers) && count($headers) == 1) ? $headers[0] : $headers;
         if ($type == 'exists')
             {
      $this->form['tests'][$i]['test'] = 'exists';
          $this->form['tests'][$i]['arg'] = $cust_header;
@@ -434,9 +498,12 @@
  private function managesieve_send()
  {
    // Handle form action 
    if (isset($_GET['_framed']) || isset($_POST['_framed']))
      $this->rc->output->send('managesieve.managesieveedit');
    else {
    if (isset($_GET['_framed']) || isset($_POST['_framed'])) {
      if (isset($_GET['_newset']) || isset($_POST['_newset']))
        $this->rc->output->send('managesieve.setedit');
      else
        $this->rc->output->send('managesieve.filteredit');
    } else {
      $this->rc->output->set_pagetitle($this->gettext('filters'));
      $this->rc->output->send('managesieve.managesieve');
    }
@@ -463,7 +530,34 @@
    $this->rc->output->include_script('list.js');
  
    // add some labels to client
    $this->rc->output->add_label('managesieve.filterconfirmdelete');
    $this->rc->output->add_label('managesieve.filterdeleteconfirm');
    return $out;
  }
  // return the filters list as <SELECT>
  function filtersets_list($attrib)
  {
    // add id to message list table if not specified
    if (!strlen($attrib['id']))
      $attrib['id'] = 'rcmfiltersetslist';
    $list = $this->sieve->get_scripts();
    $active = $this->sieve->get_active();
    $select = new html_select(array('name' => '_set', 'id' => $attrib['id'], 'onchange' => 'rcmail.managesieve_set()'));
    asort($list, SORT_LOCALE_STRING);
    foreach($list as $set)
      $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set);
    $out = $select->show($this->sieve->current);
    // set client env
    $this->rc->output->add_gui_object('filtersetslist', $attrib['id']);
    $this->rc->output->add_label('managesieve.setdeleteconfirm');
    $this->rc->output->add_label('managesieve.active');
  
    return $out;
  }
@@ -477,9 +571,58 @@
    $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');
    $this->rc->output->abs_url($attrib['src']) : 'program/blank.gif');
    return html::tag('iframe', $attrib);
  }
  function filterset_form($attrib)
  {
    if (!$attrib['id'])
      $attrib['id'] = 'rcmfiltersetform';
    $out = '<form name="filtersetform" action="./" method="post">'."\n";
    $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' => '_newset', 'value' => 1));
    $out .= $hiddenfields->show();
    $name = get_input_value('_name', RCUBE_INPUT_GPC);
    $copy = get_input_value('_copy', RCUBE_INPUT_GPC);
    $table = new html_table(array('cols' => 2));
    // filter set name input
    $input_name = new html_inputfield(array('name' => '_name', 'id' => '_name', 'size' => 30,
   'class' => ($this->errors['name'] ? 'error' : '')));
    $table->add('title', sprintf('<label for="%s"><b>%s:</b></label>', '_name', Q($this->gettext('filtersetname'))));
    $table->add(null, $input_name->show($name));
    // filters set list
    $list = $this->sieve->get_scripts();
    $active = $this->sieve->get_active();
    $select = new html_select(array('name' => '_copy', 'id' => '_copy'));
    asort($list, SORT_LOCALE_STRING);
    $select->add($this->gettext('none'), '');
    foreach($list as $set)
      $select->add($set . ($set == $active ? ' ('.$this->gettext('active').')' : ''), $set);
    $table->add('title', '<label>'.$this->gettext('copyfromset').':</label>');
    $table->add(null, $select->show($copy));
    $out .= $table->show();
    $this->rc->output->add_gui_object('sieveform', 'filtersetform');
    return $out;
  }
@@ -599,9 +742,11 @@
    // TODO: list arguments
    if ((isset($rule['test']) && $rule['test'] == 'header') && in_array($rule['arg1'], $this->headers))
    if ((isset($rule['test']) && $rule['test'] == 'header')
   && !is_array($rule['arg1']) && in_array($rule['arg1'], $this->headers))
      $out .= $select_header->show($rule['arg1']);
    elseif ((isset($rule['test']) && $rule['test'] == 'exists') && in_array($rule['arg'], $this->headers))
    elseif ((isset($rule['test']) && $rule['test'] == 'exists')
   && !is_array($rule['arg']) && in_array($rule['arg'], $this->headers))
      $out .= $select_header->show($rule['arg']);
    elseif (isset($rule['test']) && $rule['test'] == 'size')
      $out .= $select_header->show('size');
@@ -612,10 +757,12 @@
    $out .= '</td><td class="rowtargets">';
    if ((isset($rule['test']) && $rule['test'] == 'header') && !in_array($rule['arg1'], $this->headers))
      $custom = $rule['arg1'];
    elseif ((isset($rule['test']) && $rule['test'] == 'exists') && !in_array($rule['arg'], $this->headers))
      $custom = $rule['arg'];
    if ((isset($rule['test']) && $rule['test'] == 'header')
   && (is_array($rule['arg1']) || !in_array($rule['arg1'], $this->headers)))
      $custom = is_array($rule['arg1']) ? implode(', ', $rule['arg1']) : $rule['arg1'];
    elseif ((isset($rule['test']) && $rule['test'] == 'exists')
   && (is_array($rule['arg']) || !in_array($rule['arg'], $this->headers)))
      $custom = is_array($rule['arg']) ? implode(', ', $rule['arg']) : $rule['arg'];
    
    $out .= '<div id="custom_header' .$id. '" style="display:' .(isset($custom) ? 'inline' : 'none'). '">
   <input type="text" name="_custom_header[]" '. $this->error_class($id, 'test', 'header')
@@ -752,6 +899,9 @@
    $a_folders = $this->rc->imap->list_mailboxes();
    $delimiter = $this->rc->imap->get_hierarchy_delimiter();
    // set mbox encoding
    $mbox_encoding = $this->rc->config->get('managesieve_mbox_encoding', 'UTF7-IMAP');
    if ($action['type'] == 'fileinto')
      $mailbox = $action['target'];
    else
@@ -759,12 +909,15 @@
    foreach ($a_folders as $folder)
    {
      $utf7folder = $folder;
      $utf7folder = $this->rc->imap->mod_mailbox($folder);
      $names = explode($delimiter, rcube_charset_convert($folder, 'UTF7-IMAP'));
      $name = $names[sizeof($names)-1];
    
      if ($replace_delimiter = $this->rc->config->get('managesieve_replace_delimiter'))
        $utf7folder = str_replace($delimiter, $replace_delimiter, $utf7folder);
      // convert to Sieve implementation encoding
      $utf7folder = $this->mbox_encode($utf7folder, $mbox_encoding);
    
      if ($folder_class = rcmail_folder_classname($name))
        $foldername = $this->gettext($folder_class);
@@ -820,6 +973,9 @@
  private function check_email($email)
  {
    if (function_exists('check_email'));
      return check_email($email);
    // Check for invalid characters
    if (preg_match('/[\x00-\x1F\x7F-\xFF]/', $email))
      return false;
@@ -857,6 +1013,10 @@
    return false;
  }
 
  private function mbox_encode($text, $encoding)
  {
    return rcube_charset_convert($text, 'UTF7-IMAP', $encoding);
  }
}
?>