Code

Replaced in_array calls for gosa-plugins
[gosa.git] / gosa-plugins / mail / admin / groups / mail / class_groupMail.inc
index bd8dbb3e9bce7f9bf38ffd0c67f241441cd41f0e..95acdd231d7b109120ea8721aae0585c3c2a826f 100644 (file)
@@ -2,52 +2,35 @@
 
 class mailgroup extends plugin
 {
-  var $uid                        = "";       // User id 
-  var $cn                         = "";       // cn
-  var $orig_cn                    = "";       // cn
+  /* Multiple edit */
+  var $gosaMailForwardingAddress_Some  = array();  // Used in multiple edit 
 
-  var $method                     = "mailMethod"; // Used Mail method 
-  var $mmethod                    = "";           // Contains the gosa.conf MAILMETHOD
+  /* Default values */
   var $mail                       = "";           // Default mail address 
-
   var $gosaMailAlternateAddress   = array();  // Set default Alternate Mail Adresses to empty array
   var $gosaMailForwardingAddress  = array();  // Forwarding also empty
-
-  var $gosaMailForwardingAddress_Some  = array();  // Used in multiple edit 
-
   var $gosaMailServer             = "";       // Selected mailserver 
   var $gosaMailQuota              = "";       // Defined Quota 
-  var $quotaUsage                 = 0;        // Currently used quota
-
   var $gosaVacationMessage        = "";       // Vocation message 
-
-  var $imapacl                    = array('anyone'    => 'p',     // Set acls for everyone
-      '%members%' => 'lrswp',  // %members% are all group-members
-      ''          => 'p');    // Every user added gets this right
-
-  var $kolabFolderType_SubType = "";
-  var $kolabFolderType_Type = "";
-
   var $gosaSpamSortLevel          = "";     
   var $gosaSpamMailbox            = "";
   var $gosaSharedFolderTarget     ;
-
-  var $forward_dialog             = FALSE;    
-
-  var $members                    = array();  // Group members
-
-  var $mailusers                  = array();
-  var $perms                      = array();
   var $gosaMailDeliveryMode       = "[L        ]";   // 
   var $gosaMailMaxSize            = "";       // 
-  
-  var $remove_folder_from_imap    = true;
+  var $FolderType                 = array("CAT" => '', "SUB_CAT" => '');
 
-  /* Helper */
-  var $indexed_acl= array();
-  var $indexed_user= array();
+  var $quotaUsage                 = -1;        // -1 Means undefined
 
-  var $view_logged = FALSE;
+  /* Internal */
+  var $AclTypes                   = array();
+  var $members                    = array();  // Group members
+  var $mailusers                  = array();  // Group member with mail account
+  var $folder_acls                = array();
+  var $MailMethod = NULL; 
+  var $mailAddressSelect             = FALSE;    
+  var $remove_folder_from_imap    = true;
+  var $view_logged                = FALSE;
+  var $mailDomainPart             = "";
 
   /* attribute list for save action */
   var $attributes= array( "mail",   "gosaMailServer", "gosaMailQuota", "gosaMailMaxSize",
@@ -56,138 +39,168 @@ class mailgroup extends plugin
       "acl","gosaSharedFolderTarget", "gosaVacationMessage");
 
   var $objectclasses= array("gosaMailAccount");
-  var $CopyPasteVars          = array("quotaUsage","imapacl");
+  var $multiple_support = FALSE; // Not tested yet
 
-  var $multiple_support = TRUE;
+  var $uid = "";
+  var $cn ="";
+  var $orig_cn = "";
+  var $show_effective_memeber = FALSE;
 
-  function mailgroup (&$config, $dn= NULL, $ui= NULL)
+  function __construct (&$config, $dn= NULL, $base_object= NULL)
   {
-    /* Initialise all available attributes ... if possible
-     */
     plugin::plugin($config, $dn);
-    $this->orig_cn = $this->cn;
 
-    /* Set mailMethod to the one defined in gosa.conf 
+    /* Get attributes from parent object
      */
-    if (isset($this->config->current['MAILMETHOD'])){
-      $this->mmethod= $this->config->current['MAILMETHOD'];
+    foreach(array("uid","cn") as $attr){
+      if(isset($this->parent->by_object['group']) && isset($this->parent->by_object['group']->$attr)){
+        $this->$attr = $this->parent->by_object['group']->$attr;
+      }elseif(isset($this->attrs[$attr])){
+        $this->$attr = $this->attrs[$attr][0];
+      }
     }
+    $this->orig_cn = $this->uid = $this->cn;
 
-    /* Check if selected mail method exists 
+    /* Intialize the used mailMethod
      */
-    $cls = get_correct_class_name("mailMethod$this->mmethod");
-    if ($cls && class_exists($cls)){
-      $this->method= $cls;
-    } else {
-      print_red(sprintf(_("There is no mail method '%s' specified in your gosa.conf available."), $this->mmethod));
-    }
-
-    /* Load Mailserver  
+    $tmp = new mailMethod($config,$this,"group");
+    $this->mailMethod           = $tmp->get_method();
+    $this->mailMethod->fixAttributesOnLoad();
+    $this->mailDomainParts      = $this->mailMethod->getMailDomains();
+    $this->AvailableFolderTypes = $this->mailMethod->getAvailableFolderTypes();
+    $this->MailBoxes = array();
+
+    /* Remember account status
      */
-    if(isset($this->attrs['gosaMailServer'][0])){
-      $this->gosaMailServer =  $this->attrs['gosaMailServer'][0];
+    $this->initially_was_account = $this->is_account;
+
+    /* While we are not not allowed to modify the mail address
+     *  and this is a new mail account, preset the user part of the 
+     *  mail address with the accounts cn.
+     */ 
+    if(!$this->mailMethod->isModifyableMail() && !$this->initially_was_account){
+      $this->mail = $base_object->cn;
     }
 
-    /* Convert cn to uid in case of existing entry
+    /* Load folder_acls with defaults.
+      anyone -- The default acl, will be written to ldap.
+      member -- The ACL used for the members.
+     */ 
+    $this->folder_acls = $this->mailMethod->getDefaultACLs();
+
+    /* Load acls
+       The most used acl will be used as member acl, this
+        shortens the listed acls.        
+       This may be merged/overwritten by the mail methods.
      */
-    if (isset($this->attrs['cn'][0])){
-      $this->uid= $this->attrs['cn'][0];
-    }
+    $ldap = $this->config->get_ldap_link();
+    if(isset($this->attrs['acl'])){
+      for($i = 0; $i < $this->attrs['acl']['count'] ; $i++){
 
-    /* Get folder type */
-    if(isset($this->config->current['MAILMETHOD'])&&preg_match("/olab/i",$this->config->current['MAILMETHOD'])){
-      if(isset($this->attrs['kolabFolderType'])){
-        $tmp = split("\.",$this->attrs['kolabFolderType'][0]);
-        $this->kolabFolderType_Type = $tmp[0];
-        $this->kolabFolderType_SubType = $tmp[1];
+        /* Be carefull here, since kolab22 uses spaces in the acls (herbert read anon/post)
+         */
+        $str = $this->attrs['acl'][$i];
+        list($name, $acl) = preg_split("/[ ]{1}/", $str, 2);
+        if($name == "anyone") $name = "__anyone__";
+        $this->folder_acls[$name] = $acl;
       }
     }
 
-    /* If this isn't a new mailgroup, read all required data from ldap
+    /* Initialize configured values
      */
-    if (($dn != "new")&&($dn !== NULL)){
+    if($this->is_account){
+      if($this->mailMethod->connect() && $this->mailMethod->account_exists()){
+
+        /* Read quota */
+        $this->gosaMailQuota = $this->mailMethod->getQuota($this->gosaMailQuota);
+        $this->quotaUsage = $this->mailMethod->getQuotaUsage($this->quotaUsage);
+        if($this->mailMethod->is_error()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot read quota settings: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
 
-      /* Load attributes which represent multiple entries  
+        /* Read mailboxes */
+        $this->MailBoxes = $this->mailMethod->getMailboxList($this->MailBoxes);
+        if($this->mailMethod->is_error()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot get list of mailboxes: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
+
+        /* Receive folder types */
+        $this->FolderType = $this->mailMethod->getFolderType($this->FolderType);
+        if($this->mailMethod->is_error()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot receive folder types: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
+
+        /* Receive permissions */  
+        $this->folder_acls = $this->mailMethod->getFolderACLs($this->folder_acls);
+        if($this->mailMethod->is_error()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot receive folder permissions: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
+
+      }elseif(!$this->mailMethod->is_connected()){
+        msg_dialog::display(_("Mail error"), sprintf(_("Mail method cannot connect: %s"),
+              $this->mailMethod->get_error()), ERROR_DIALOG);
+      }elseif(!$this->mailMethod->account_exists()){
+        msg_dialog::display(_("Mail error"), sprintf(_("Mailbox '%s' doesn't exists on mail server: %s"),
+              $this->mailMethod->get_account_id(),$this->gosaMailServer), ERROR_DIALOG);
+      }
+
+      /* If the doamin part is selectable, we have to split the mail address
        */
+      if(!(!$this->mailMethod->isModifyableMail() && $this->is_account)){
+        if($this->mailMethod->domainSelectionEnabled() || $this->mailMethod->mailEqualsCN()){
+          $this->mailDomainPart = preg_replace("/^[^@]*+@/","",$this->mail);
+          $this->mail = preg_replace("/@.*$/","\\1",$this->mail);
+          if(!in_array_strict($this->mailDomainPart,$this->mailDomainParts)){
+            $this->mailDomainParts[] = $this->mailDomainPart;
+          }
+        }
+      }
+
+      /* Load attributes containing arrays */
       foreach (array("gosaMailAlternateAddress", "gosaMailForwardingAddress") as $val){
-        $this->$val = array();
+        $this->$val= array();
         if (isset($this->attrs["$val"]["count"])){
           for ($i= 0; $i<$this->attrs["$val"]["count"]; $i++){
             array_push($this->$val, $this->attrs["$val"][$i]);
           }
         }
       }
+    }
 
-      /* Only do IMAP actions if gosaMailServer attribute is set 
-       */
-      if (isset ($this->attrs["gosaMailServer"][0])){
-
-        /* Create new instance of our defined mailclass
-         */
-        $method= new $this->method($this->config);
-        if ($method->connect($this->attrs["gosaMailServer"][0])){
-
-          /* If we do NOT use kolab or equal methods, 
-             read imap the acls from the mail method class.
-             They will be merged later with the ldap specified acls.
-           */
-          if(!preg_match("/olab/i",$this->mmethod) && !empty($this->mmethod)){
-            $this->imapacl=  $method->getSharedFolderPermissions($this->uid);
-
-            /* Need to filter what a member acl could be... */
-            $vote= array();
-            $peak= 0;
-            $leader= "";
-            foreach ($this->imapacl as $user => $acl){
-
-              if ($user != "anyone" ){
-                if (!isset($vote[$acl])){
-                  $vote[$acl]= 1;
-                } else {
-                  $vote[$acl]++;
-                }
-                if ($vote[$acl] > $peak){
-                  $leader= $acl;
-                  $peek= $vote[$acl];
-                }
-              }
-            }
-            /* Highest count wins as %members%, remove all members
-               with the same acl */
-            if(!empty($leader)){
-              $this->imapacl['%members%']= $leader;
-            }
-            foreach ($this->imapacl as $user => $acl){
-              if ($this->acl == $leader && in_array($user, $this->attrs['memberUid'])){
-                unset($this->imapacl[$user]);
-              }
-            }
-          }
-
-          /* Adapt attributes if needed */
-          $method->fixAttributesOnLoad($this);
-
-          /*  get Quota */
-          $quota= $method->getQuota($this->uid);
-
-          /* Update quota values */
-          if(is_array($quota)){
-            if ($quota['gosaMailQuota'] == 2147483647){
-              $this->quotaUsage= "";
-              $this->gosaMailQuota= "";
-            } else {
-              $this->quotaUsage= $quota['quotaUsage'];
-              $this->gosaMailQuota= $quota['gosaMailQuota'];
-            }
-          }else{
-            $this->quotaUsage     = "";
-            $this->gosaMailQuota  = "";
-          }
-          $method->disconnect();
-        }   // ENDE $method->connect($this->attrs["gosaMailServer"][0])){
-      }   // ENDE gosaMailServer
-    }   // ENDE dn != "new"
+    /* Disconnect mailMethod. Connect on demand later.
+     */
+    $this->mailMethod->disconnect();
+    $this->AclTypes = $this->mailMethod->getAclTypes();
 
+    /* Summarize most used ACLs as member acl 
+     */
+    if(count($this->folder_acls) > 2){
+      $acl_usage = array();
+      $most_acl = $this->folder_acls['__member__'];
+      $most_cnt = 0;
+      $member = $this->get_member();
+      foreach($this->folder_acls as $user => $acl){
+        if(preg_match("/^__/",$user)) continue;
+        if(!in_array_strict($user,$member['mail'])) continue; 
+        if(!isset($acl_usage[$acl])) $acl_usage[$acl]=0;
+        $acl_usage[$acl] ++;
+        if($acl_usage[$acl] > $most_cnt){
+          $most_cnt = $acl_usage[$acl];
+          $most_acl = $acl;
+        }
+      }
+      $this->folder_acls['__member__'] = $most_acl;  
+      foreach($this->folder_acls as $name => $acl){
+        if(preg_match("/^__/",$name)) continue;
+        if($acl == $most_acl && in_array_strict($name,$member['mail'])){
+          unset($this->folder_acls[$name]);
+        }
+      }
+    }
 
     /* Get global filter config */
     if (!session::is_set("gmailfilter")){
@@ -198,174 +211,88 @@ class mailgroup extends plugin
           "regex"           => "*");
       session::set("gmailfilter", $gmailfilter);
     }
+  }
 
-    /* Load permissions */
-    $tmp = array();
+  /*! \brief  Returns all group members once with 'dn' and once with 'mail'.
+              This function is used to summarize ACLs by member acls.
+      @return Array   Containing all members, with mail and dn
+   */ 
+  function get_member()
+  {
+    $member = array('all' => array(), 'mail' => array());
     $ldap = $this->config->get_ldap_link();
-
-    /* Read acls  from ldap and merge them with
-     *  acls read from mail method.
-     */
-    if (isset($this->attrs['acl'])){
-
-      for ($i= 0; $i<$this->attrs['acl']['count']; $i++){
-        list($user, $permission)= split(' ', $this->attrs['acl'][$i]);
-
-        /* Add to list */
-        $this->imapacl[$user]= $permission;
-
-        /* Get all user permissions sorted by acl, to detect the most used acl
-           This acl is then used for %members%
-         */
-        if ($user != "anyone" && $user != "%members%"){
-          $tmp[$permission][] = $user;
-        }
-
-        /* There is an entry in $this->imapacl like this this ... 
-           $this->attrs['imapacl']['anyone'] = "p";
-           $this->attrs['imapacl']['%members%'] = "lprs";
-           $this->attrs['imapacl'][''] = ""; <------ This is used to diplay an empty 
-           Field for special acls in our template.
-           If there is at least one special acl in out imapacl,
-           we don't need this entry anymore, because it is already displayed. 
-         */
-        if ($user != "anyone" && $user != "%members%"){
-          unset($this->imapacl['']);
+    $ldap->cd($this->config->current['BASE']);
+    if(isset($this->parent->by_object['group'])){
+      foreach($this->parent->by_object['group']->memberUid as $uid){
+        if(!isset($this->parent->by_object['group']->dnMapping[$uid])) continue;
+        $dn = $this->parent->by_object['group']->dnMapping[$uid];
+        $member['all'][$uid] = $uid;
+        if($ldap->object_match_filter($dn,"(&(objectClass=gosaMailAccount)(".$this->mailMethod->getUAttrib()."=*))")){
+          $ldap->cat($dn);
+          $attrs = $ldap->fetch();
+          $member['mail'][$uid] = $attrs[$this->mailMethod->getUAttrib()][0]; 
         }
       }
-    }
-
-    /**
-     * Detect group members which use the same acl
-     *  as used for %members% and remove them.
-     **/
-
-    /* In this section we detect which acl is the most used.
-       This will be used as %members% acl.
-     */
-    $tmp2 = array(); 
-    foreach($tmp as $acl => $user){
-      $tmp2[count($tmp[$acl])]=$acl;
-    }
-    /* Most used at last 
-     */
-    ksort($tmp2);      
-
-    /* Assign last (most used acl) to %members% acl 
-     */
-    $str = array_pop($tmp2);
-    if(!empty($str)) {
-      $this->imapacl['%members%']=$str;
-    }
-    if(!isset($this->imapacl['%members%'])){
-      $this->imapacl['%members%'] = "lrspw";
-    }
-
-
-    /* Open ldap connection 
-     */
-    $ldap = $this->config->get_ldap_link();
-    $ldap->cd($this->config->current['BASE']);
-
-    /* Remove those users, that use %members% acl && are member of this group. */
-    foreach($this->imapacl as $mail => $permission){
-      $ldap->search("(&(objectClass=person)(|(mail=".$mail.")(uid=".$mail.")))",array("uid"));
-      $atr = $ldap->fetch();
-      if((isset($this->attrs['memberUid'])) && (is_array($this->attrs['memberUid']))){
-        if((isset($atr['uid'][0]))&&(in_array($atr['uid'][0],$this->attrs['memberUid']))&&($permission == $this->imapacl['%members%'])){
-          unset($this->imapacl[$mail]);
+    }else{
+      if(!isset($this->attrs['memberUid'])) return($member);
+      $uattrib = $this->mailMethod->getUAttrib();
+      $users = get_list("(&(objectClass=person)(objectClass=gosaAccount)(uid=*))",
+              "users",$this->config->current['BASE'],
+              array("uid","objectClass",$uattrib),GL_SUBSEARCH | GL_NO_ACL_CHECK);
+      foreach($users as $user){
+        $member['all'][$user['uid'][0]] = $user['dn'];
+        if(isset($user[$uattrib]) 
+            && in_array_strict("gosaMailAccount",$user['objectClass']) 
+            && (in_array_strict($user['uid'][0], $this->attrs['memberUid']))){
+          $member['mail'][$user['uid'][0]] = $user[$uattrib][0];
         }
       }
     }
-
-    /**
-     * ENDE: Detect group member with same acl and replace them with %members%
-     **/
-    $this->indexed_user = array("%members%","anyone");
-
-    /* Append an empty entry, for special acl handling */
-    if(count($this->imapacl)==2){
-      $this->imapacl[''] ="lrsw";
-      $this->indexed_user[] = '';
-    }
-
-    /* Load Mailserver
-     */
-    if(isset($this->attrs['gosaMailServer'][0])){
-      $this->gosaMailServer =  $this->attrs['gosaMailServer'][0];
-    }
-    /* Fill translations */
-    $this->perms["lrsw"]= _("read");
-    $this->perms["lrswp"]= _("post");
-    $this->perms["p"]= _("external post");
-    $this->perms["lrswip"]= _("append");
-    $this->perms["lrswipcd"]= _("write");
-    $this->perms["lrswipcda"]= _("admin");
-    $this->perms[""]= _("none");
+    return($member);
   }
 
 
-
   function execute()
   {
     /* Call parent execute */
-    //plugin::execute();
-    $display = "";
+    plugin::execute();
 
     /* Log view */
     if($this->is_account && !$this->view_logged){
       $this->view_logged = TRUE;
       new log("view","groups/".get_class($this),$this->dn);
     }
+    /****************
+      Account status
+     ****************/
 
-    /* Load templating engine */
-    $smarty= get_smarty();
-
-    /* Assign acls */
-    $tmp = $this->plInfo();
-    foreach($tmp['plProvidedAcls'] as $name => $translation) {
-      $smarty->assign($name."ACL",$this->getacl($name));
-    }
-
-    if (session::get('js')==FALSE){
-      $smarty->assign("javascript", "false");
-    } else {
-      $smarty->assign("javascript", "true");
-    }
-
-    /* Handle actions should not be done, when 
-     *  editing multiple entries at once. e.g. account state
-     */ 
     if(!$this->multiple_support_active){
 
-      /* Do we need to flip is_account state? */
       if(isset($_POST['modify_state'])){
-        if($this->is_account && $this->acl_is_removeable()){
+        if($this->is_account && $this->acl_is_removeable() && $this->mailMethod->accountRemoveAble()){
           $this->is_account= FALSE;
-        }elseif(!$this->is_account && $this->acl_is_createable()){
+        }elseif(!$this->is_account && $this->acl_is_createable() && $this->mailMethod->accountCreateable()){
           $this->is_account= TRUE;
         }
       }
 
-      $display = "";
-
-      /* Do we represent a valid account? */
-      if (!$this->is_account && $this->parent === NULL){
-
-        $display.= "<img alt=\"\" src=\"images/stop.png\" align=middle>&nbsp;<b>".
-          _("This 'dn' has no valid mail extensions.")."</b>";
-        return ($display);
-      }
-
-      /* Show tab dialog headers */
-      $display= "";
-      if ($this->parent !== NULL){
-        if ($this->is_account){
-          $display.= $this->show_disable_header(_("Remove mail account"),
-              _("This account has mail features enabled. You can disable them by clicking below."));
-        } else {
-          $display.= $this->show_enable_header(_("Create mail account"),
-              _("This account has mail features disabled. You can enable them by clicking below."));
+      if ($this->is_account){
+        $reason = "";
+        if(!$this->mailMethod->accountRemoveable($reason)){
+          $display= $this->show_disable_header(msgPool::removeFeaturesButton(_("Mail")),$reason ,TRUE,TRUE);
+        }else{
+          $display= $this->show_disable_header(msgPool::removeFeaturesButton(_("Mail")),msgPool::
+              featuresEnabled(_("Mail")));
+        }
+      } else {
+        $reason = "";
+        if(!$this->mailMethod->accountCreateable($reason)){
+          $display= $this->show_disable_header(msgPool::addFeaturesButton(_("Mail")),$reason ,TRUE,TRUE);
+        }else{
+          $display= $this->show_disable_header(msgPool::addFeaturesButton(_("Mail")),msgPool::
+              featuresDisabled(_("Mail")));
 
           /* Show checkbox that allows us to remove imap entry too*/
           if($this->initially_was_account){
@@ -378,276 +305,165 @@ class mailgroup extends plugin
               title='"._("Remove shared folder from mail server database when entry gets removed in LDAP")."'>";
             $display .= _("Remove the shared folder and all its contents after saving this account");
           }
-          return ($display);
         }
+        return ($display);
       }
     }
 
-    /* Add ACL? */
-    if($this->acl_is_writeable("acl")){
-      foreach ($this->indexed_user as $nr => $user){
-        if (isset($_POST["add_$nr"])){
-          $this->imapacl[""]= "l";
-        }
-        if (isset($_POST["del_$nr"])){
-          unset ($this->imapacl[$user]);
-        }
+    
+    /****************
+      Preset mail attribute
+     ****************/
+    if(empty($this->mail) && $this->mailMethod->mailEqualsCN() && !$this->initially_was_account){
+      if($this->mailMethod->domainSelectionEnabled()){
+        $this->mail = &$this->parent->by_object['group']->cn;
       }
     }
 
-    /* Trigger forward add dialog? */
-    if($this->acl_is_writeable("gosaMailForwardingAddress")){
-      if (isset($_POST['add_local_forwarder'])){
-        $this->forward_dialog= TRUE;
-        $this->dialog= TRUE;
-      }
-    }
 
-    /* Cancel forward add dialog? */
-    if($this->acl_is_writeable("gosaMailForwardingAddress")){
-      if (isset($_POST['add_locals_cancel'])){
-        $this->forward_dialog= FALSE;
-        $this->dialog= FALSE;
-      }
-    }
+    /****************
+      Forward addresses
+     ****************/
 
-    /* Finished adding of locals? */
-    if ((isset($_POST['add_locals_finish'])) && ($this->acl_is_writeable("gosaMailForwardingAddress"))) {
-      if (count ($_POST['local_list']) && $this->acl_is_writeable("gosaMailForwardingAddress")){
+    if (isset($_POST['add_local_forwarder'])){
+      $this->mailAddressSelect= new mailAddressSelect($this->config, get_userinfo());
+      $this->dialog= TRUE;
+    }
 
-        /* Walk through list of forwarders, ignore own addresses */
-        foreach ($_POST['local_list'] as $val){
-          if (!in_array ($val, $this->gosaMailAlternateAddress) &&
-              $val != $this->mail){
+    if (isset($_POST['mailAddressSelect_cancel'])){
+      $this->mailAddressSelect= FALSE;
+      $this->dialog= FALSE;
+    }
 
+    if (isset($_POST['mailAddressSelect_save'])){
+      if($this->acl_is_writeable("gosaMailForwardingAddress")){
+        $list = $this->mailAddressSelect->save();
+        foreach ($list as $entry){
+          $val = $entry['mail'][0];
+          if (!in_array_strict($val, $this->gosaMailAlternateAddress) && $val != $this->mail){
             $this->addForwarder($val);
+            $this->is_modified= TRUE;
           }
         }
+        $this->mailAddressSelect= FALSE;
+        $this->dialog= FALSE;
+      } else {
+        msg_dialog::display(_("Error"), _("Please select an entry!"), ERROR_DIALOG);
       }
-      $this->forward_dialog= FALSE;
-      $this->dialog= FALSE;
     }
 
-    /* Add forward email addresses */
-    if ((isset($_POST['add_forwarder'])) && ($this->acl_is_writeable("gosaMailForwardingAddress"))){
-      if ($_POST['forward_address'] != ""){
+    if($this->mailAddressSelect instanceOf mailAddressSelect){
+      $used  = array();
+      $used['mail'] = array_values($this->gosaMailAlternateAddress);
+      $used['mail'] = array_merge($used['mail'], array_values($this->gosaMailForwardingAddress));
+      $used['mail'][] = $this->mail;
+
+      // Build up blocklist
+      session::set('filterBlacklist', $used);
+      return($this->mailAddressSelect->execute());
+    }
 
-        /* Valid email address specified? */
+    if (isset($_POST['add_forwarder'])){
+      if ($_POST['forward_address'] != ""){
         $address= $_POST['forward_address'];
+        $valid= FALSE;
         if (!tests::is_email($address)){
-
-          print_red (_("You're trying to add an invalid email address ".
-                "to the list of forwarders."));
-
+          if (!tests::is_email($address, TRUE)){
+            if ($this->is_template){
+              $valid= TRUE;
+            } else {
+              msg_dialog::display(_("Error"), msgPool::invalid(_("Mail address"),
+                    "","","your-address@your-domain.com"),ERROR_DIALOG);
+            }
+          }
         } elseif ($address == $this->mail
-            || in_array($address, $this->gosaMailAlternateAddress)) {
-
-          print_red (_("Adding your one of your own addresses to the forwarders makes no sense."));
-
+            || in_array_strict($address, $this->gosaMailAlternateAddress)) {
+          msg_dialog::display(_("Error"),_("Cannot add primary address to the list of forwarders!") , ERROR_DIALOG);
         } else {
-
-          /* Add it */
-          if ($this->acl_is_writeable("gosaMailForwardingAddress")){
+          $valid= TRUE;
+        }
+        if ($valid){
+          if($this->acl_is_writeable("gosaMailForwardingAddress")){
             $this->addForwarder ($address);
+            $this->is_modified= TRUE;
           }
-
         }
       }
     }
-
-    /* Delete forward email addresses */
-    if (isset($_POST['delete_forwarder']) && ($this->acl_is_writeable("gosaMailForwardingAddress"))){
-      if (count($_POST['forwarder_list'])&& $this->acl_is_writeable("gosaMailForwardingAddress")){
-
-        $this->delForwarder ($_POST['forwarder_list']);
-      }
+    if (isset($_POST['delete_forwarder'])){
+      $this->delForwarder ($_POST['forwarder_list']);
     }
 
-    /* Add alternate email addresses */
-    if (isset($_POST['add_alternate'])){
-      if ($_POST['alternate_address'] != "" && $this->acl_is_writeable("gosaMailAlternateAddress")){
-
-        if (!tests::is_email($_POST['alternate_address'])){
-          print_red (_("You're trying to add an invalid email address to the list of alternate addresses."));
+    /****************
+      Alternate addresses
+     ****************/
 
-        } elseif (($user= $this->addAlternate ($_POST['alternate_address'])) != ""){
-          $ui= get_userinfo();
-          if ($user != $ui->username){
-            print_red (_("The address you're trying to add is already used by user")." '$user'.");
+    if (isset($_POST['add_alternate'])){
+      $valid= FALSE;
+      if (!tests::is_email($_POST['alternate_address'])){
+        if ($this->is_template){
+          if (!(tests::is_email($_POST['alternate_address'], TRUE))){
+            msg_dialog::display(_("Error"),msgPool::invalid(_("Mail address"),
+                  "","","your-domain@your-domain.com"),     ERROR_DIALOG);
+          } else {
+            $valid= TRUE;
           }
+        } else {
+          msg_dialog::display(_("Error"),msgPool::invalid(_("Mail address"),
+                "","","your-domain@your-domain.com"),       ERROR_DIALOG);
         }
-      }
-    }
-
-    /* Delete alternate email addresses */
-    if($this->acl_is_writeable("gosaMailAlternateAddress")){
-      if (isset($_POST['delete_alternate']) && isset ($_POST['alternates_list'])){
-        if (count($_POST['alternates_list']) && $this->acl_is_writeable("gosaMailAlternateAddress")){
-          $this->delAlternate ($_POST['alternates_list']);
-        }
-      }
-    }
-
-    /* Show forward add dialog */
-    if ($this->forward_dialog){
-      $ldap= $this->config->get_ldap_link();
-
-      /* Save data */
-      $gmailfilter= session::get("gmailfilter");
-      foreach( array("depselect", "muser", "regex") as $type){
-        if (isset($_POST[$type])){
-          $gmailfilter[$type]= $_POST[$type];
-        }
-      }
-      if (isset($_GET['search'])){
-        $s= mb_substr($_GET['search'], 0, 1, "UTF8")."*";
-        if ($s == "**"){
-          $s= "*";
-        }
-        $gmailfilter['regex']= $s;
-      }
-      session::set("gmailfilter", $gmailfilter);
-
-      /* Get actual list */
-      $mailusers= array ();
-      if ($gmailfilter['regex'] != '*' && $gmailfilter['regex'] != ""){
-        $regex= $gmailfilter['regex'];
-        $filter= "(|(mail=$regex)(gosaMailAlternateAddress=$regex))";
       } else {
-        $filter= "";
-      }
-      if ($gmailfilter['muser'] != ""){
-        $user= $gmailfilter['muser'];
-        $filter= "$filter(|(uid=$user)(cn=$user)(givenName=$user)(sn=$user))";
-      }
-
-      /* Add already present people to the filter */
-      $exclude= "";
-      foreach ($this->gosaMailForwardingAddress as $mail){
-        $exclude.= "(mail=$mail)";
+        $valid= TRUE;
       }
-      if ($exclude != ""){
-        $filter.= "(!(|$exclude))";
-      }
-
-      $res= get_list("(&(objectClass=gosaMailAccount)$filter)", "users", $gmailfilter['depselect'],
-                     array("sn", "mail", "givenName"), GL_SUBSEARCH | GL_SIZELIMIT);
-      $ldap->cd($gmailfilter['depselect']);
-      $ldap->search ("(&(objectClass=gosaMailAccount)$filter)", array("sn", "mail", "givenName"));
-      error_reporting (0);
-      while ($attrs= $ldap->fetch()){
-        if(preg_match('/%/', $attrs['mail'][0])){
-          continue;
+      if ($valid && ($user= $this->addAlternate ($_POST['alternate_address'])) != ""){
+        $ui= get_userinfo();
+        $addon= "";
+        if ($user[0] == "!") {
+          $addon= sprintf(_("Address is already in use by group '%s'."), mb_substr($user, 1));
+        } else {
+          $addon= sprintf(_("Address is already in use by user '%s'."), $user);
         }
-        $name= $this->make_name($attrs);
-        $mailusers[$attrs['mail'][0]]= $name."&lt;".
-          $attrs['mail'][0]."&gt;";
+        msg_dialog::display(_("Error"), msgPool::duplicated(_("Mail address"))."<br><br><i>".
+            "$addon</i>", ERROR_DIALOG);
       }
-      error_reporting (E_ALL | E_STRICT);
-      natcasesort ($mailusers);
-      reset ($mailusers);
-
-      /* Show dialog */
-      $smarty->assign("search_image", get_template_path('images/search.png'));
-      $smarty->assign("usearch_image", get_template_path('images/search_user.png'));
-      $smarty->assign("tree_image", get_template_path('images/tree.png'));
-      $smarty->assign("infoimage", get_template_path('images/info.png'));
-      $smarty->assign("launchimage", get_template_path('images/small_filter.png'));
-      $smarty->assign("mailusers", $mailusers);
-      $smarty->assign("deplist", $this->config->idepartments);
-      $smarty->assign("apply", apply_filter());
-      $smarty->assign("alphabet", generate_alphabet());
-      $smarty->assign("hint", print_sizelimit_warning());
-      foreach( array("depselect", "muser", "regex") as $type){
-        $smarty->assign("$type", $gmailfilter[$type]);
-      }
-      $smarty->assign("hint", print_sizelimit_warning());
-      $display.= $smarty->fetch (get_template_path('mail_locals.tpl', TRUE, dirname(__FILE__)));
-      return ($display);
     }
-
-    /* Assemble normal permissions */
-    if (isset($this->imapacl['anyone'])){
-      $smarty->assign("default_permissions", $this->imapacl['anyone']);
-    }
-    $smarty->assign("member_permissions", "lrsp");
-    if (isset($this->imapacl['%members%'])){
-      $smarty->assign("member_permissions", $this->imapacl['%members%']);
+    if (isset($_POST['delete_alternate']) && isset($_POST['alternates_list'])){
+      $this->delAlternate ($_POST['alternates_list']);
     }
 
-    /* Assemble extra attributes */
-    $perm= $this->getacl( "permissions");
-    $tmp= "";
-    $nr= 0;
-    $count= count($this->imapacl);
-    $this->indexed_user= array();
-    $this->indexed_acl= array();
-    foreach($this->imapacl as $user => $acl){
 
-      /* Add additional acl settings */
-      if ($user != "anyone" && $user != "%members%"){
+    /****************
+      SMARTY- Assign smarty variables
+     ****************/
 
-        $Dis  = "";
-        if(!preg_match("/w/",$perm)){
-          $Dis = " disabled ";
-        }
+    /* Load templating engine */
+    $smarty= get_smarty();
+    $smarty->assign("initially_was_account", $this->initially_was_account);
+    $smarty->assign("isModifyableMail", $this->mailMethod->isModifyableMail());
+    $smarty->assign("isModifyableServer", $this->mailMethod->isModifyableServer());
+    $smarty->assign("mailEqualsCN", $this->mailMethod->mailEqualsCN());
+    $smarty->assign("folder_acls" , $this->postable_acls());
+    $smarty->assign("AclTypes" ,    $this->AclTypes);
+    $smarty->assign("Effective",    $this->get_effective_member_acls());
+    $smarty->assign("show_effective_memeber",    $this->show_effective_memeber);
   
-        /* Reset given Acls to ensure that nobody can read username and acls if not allwoed */
-        if(!preg_match("/r/",$perm)){
-          $user = "";
-          $nr   = "none";
-          $key  = "none";  
-        }
-
-        $tmp.= "<tr>  
-                 <td>
-                  <input name=\"user_$nr\" size=20 maxlength=60 value=\"$user\" ".$Dis.">
-                 </td>
-                 <td>
-                 <select size=\"1\" name=\"perm_$nr\" ".$Dis.">";
-
-        /* Add acl options for this additional acl setting */
-        if(preg_match("/r/",$perm)){
-          foreach ($this->perms as $key => $value){
-            if ($acl == $key){
-              $tmp.= "<option value=\"$key\" selected>$value</option>";
-            } else {
-              $tmp.= "<option value=\"$key\">$value</option>";
-            }
-          }
-        }
-        $tmp.= "</select>&nbsp;";
-
-
-        
-        if ($nr == $count - 1){
-          if($this->acl_is_writeable("acl")){
-            $tmp.= "<input type=submit value=\""._("Add")."\" ".
-              "name=\"add_$nr\" >";
-          }
-        }
-        if ($count > 3){
-          if($this->acl_is_writeable("acl")){
-            $tmp.= "<input type=submit value=\""._("Remove")."\" ".
-              "name=\"del_$nr\" ></td></tr>";        
-          }
-        }
-      }
-      $this->indexed_user[$nr]= $user;
-      $this->indexed_acl[$nr++]= $acl;
+    $smarty->assign("quotaEnabled", $this->mailMethod->quotaEnabled());
+    if($this->mailMethod->quotaEnabled()){
+      $smarty->assign("gosaMailQuota",$this->gosaMailQuota);
+      $smarty->assign("quotaUsage",   mailMethod::quota_to_image($this->quotaUsage,$this->gosaMailQuota)); 
     }
-    $smarty->assign("plusattributes", $tmp);
 
-    /* Show main page */
-    $mailserver= array();
-    foreach ($this->config->data['SERVERS']['IMAP'] as $key => $val){
-      $mailserver[]= $key;
-    }
-    $smarty->assign("mailServers", $mailserver);
-    foreach(array("gosaMailServer", "gosaMailQuota", "perms", "mail",
-          "gosaMailAlternateAddress", "gosaMailForwardingAddress") as $val){
-      $smarty->assign("$val", $this->$val);
-    }
+    $smarty->assign("MailDomains", $this->mailDomainParts);
+    $smarty->assign("MailDomain" , $this->mailDomainPart);
+    $smarty->assign("MailServers", $this->mailMethod->getMailServers());
+    $smarty->assign("allowSieveManagement", $this->mailMethod->allowSieveManagement());
+
+    $smarty->assign("domainSelectionEnabled", $this->mailMethod->domainSelectionEnabled());
+    $smarty->assign("folderTypesEnabled",$this->mailMethod->folderTypesEnabled());
+    $smarty->assign("AvailableFolderTypes", $this->AvailableFolderTypes);
+    $smarty->assign("FolderType", $this->FolderType);
     if (is_numeric($this->gosaMailQuota) && $this->gosaMailQuota != 0){
       if($this->acl_is_readable("gosaMailQuota")){
         $smarty->assign("quotausage", progressbar(round(($this->quotaUsage * 100)/ $this->gosaMailQuota),100,15,true));
@@ -660,31 +476,30 @@ class mailgroup extends plugin
       $smarty->assign("quotadefined", "false");
     }
 
-    if(isset($this->config->current['MAILMETHOD'])&&preg_match("/olab/i",$this->config->current['MAILMETHOD'])){
+    /* Assign acls */
+    $tmp = $this->plInfo();
+    foreach($tmp['plProvidedAcls'] as $name => $translation) {
+      $smarty->assign($name."ACL",$this->getacl($name));
+    }
+    foreach($this->attributes as $name){
+      $smarty->assign($name,$this->$name);
+    }
+
 
-      $smarty->assign("kolab", TRUE);
-      $smarty->assign("JS",session::get('js'));
-      $smarty->assign("kolabFolderType_Types",    array (   ''      => _('Unspecified'),  'mail' => _('Mails'),
-                                                            'task'  => _('Tasks') ,       'journal' => _('Journals'),
-                                                            'calendar' => _('Calendar'),       'contact' => _('Contacts'), 
-                                                            'note'  => _('Notes')));
-      if($this->kolabFolderType_Type == "mail"){
-        $smarty->assign("kolabFolderType_SubTypes", array(    
-              ''          => _('Unspecified'),  'inbox'     => _("Inbox")   , 
-              'drafts'    => _("Drafts"),       'sentitems' => _("Sent items"),
-              'junkemail' => _("Junk mail")));
-      }else{
-        $smarty->assign("kolabFolderType_SubTypes", array(  'default' => _("Default")));
-      }
-      $smarty->assign("kolabFolderType_Type",     $this->kolabFolderType_Type);
-      $smarty->assign("kolabFolderType_SubType",  $this->kolabFolderType_SubType);
+
+    $smarty->assign("mailServers", $this->mailMethod->getMailServers());
+    if (preg_match("/I/", $this->gosaMailDeliveryMode)) {
+      $smarty->assign("only_local", "checked");
     }else{
-      $smarty->assign("kolab", FALSE);
+      $smarty->assign("only_local", "");
     }
 
-    /* Multiple support handling */
+
+    /******
+      Multi edit support 
+     ******/
     foreach($this->attributes as $attr){
-      if(in_array($attr,$this->multi_boxes)){
+      if(in_array_strict($attr,$this->multi_boxes)){
         $smarty->assign("use_".$attr,TRUE);
       }else{
         $smarty->assign("use_".$attr,FALSE);
@@ -693,7 +508,7 @@ class mailgroup extends plugin
 
     /* Multiple support handling */
     foreach(array("kolabFolderType") as $attr){
-      if(in_array($attr,$this->multi_boxes)){
+      if(in_array_strict($attr,$this->multi_boxes)){
         $smarty->assign("use_".$attr,TRUE);
       }else{
         $smarty->assign("use_".$attr,FALSE);
@@ -702,8 +517,8 @@ class mailgroup extends plugin
 
     $smarty->assign("Forward_all",$this->gosaMailForwardingAddress);
     $smarty->assign("Forward_some",$this->gosaMailForwardingAddress_Some);
-
     $smarty->assign("multiple_support",$this->multiple_support_active);
+
     $display.= $smarty->fetch (get_template_path('mail.tpl', TRUE, dirname(__FILE__)));
     return ($display);
   }
@@ -715,47 +530,53 @@ class mailgroup extends plugin
     if(!$this->initially_was_account){
       return;
     }
-  
-    /* Added these ObjectClass and Attributes, because they were not 
-       removed correctly, only in case of kolab ... 
-     */
-    if(isset($this->config->current['MAILMETHOD'])&&preg_match("/olab/i",$this->config->current['MAILMETHOD'])){
-      $this->attributes[]="acl";
-      $this->objectclasses[] = "kolabSharedFolder";
+    /* If domain part was selectable, contruct mail address */
+    if($this->mailMethod->domainSelectionEnabled() || $this->mailMethod->mailEqualsCN()){
+      $this->mail = $this->mail."@".$this->mailDomainPart;
     }
-    /* include global link_info */
-    $ldap= $this->config->get_ldap_link();
 
-    /* Remove and write to LDAP */
+    /* Remove GOsa attributes */
     plugin::remove_from_parent();
 
     /* Zero arrays */
-    $this->attrs['gosaMailAlternateAddress']= array();
+    $this->attrs['gosaMailAlternateAddress'] = array();
     $this->attrs['gosaMailForwardingAddress']= array();
-    $this->attrs['gosaSharedFolderTarget']= array();
-
-    /* Connect to IMAP server for account deletion */
-    if ($this->initially_was_account){
-      $method= new $this->method($this->config);
-      $method->fixAttributesOnRemove($this);
-      if ($method->connect($this->gosaMailServer) && $this->remove_folder_from_imap){
-
-        /* Remove account from IMAP server */
-        $method->deleteMailbox($this->uid);
-        $method->disconnect();
-      }
-    }
-    /* Keep uid */
-    unset ($this->attrs['uid']);
 
+    $this->mailMethod->fixAttributesOnRemove();
+    $this->cleanup();
+    $ldap = $this->config->get_ldap_link();
     $ldap->cd($this->dn);
     $ldap->modify ($this->attrs); 
-    show_ldap_error($ldap->get_error(), sprintf(_("Removing of groups/mail with dn '%s' failed."),$this->dn));
-
+    if (!$ldap->success()){
+      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
+    }
 
     new log("remove","groups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
 
+
+    /* Let the mailMethod remove this mailbox, e.g. from imap and
+       update shared folder membership, ACL may need to be updated.
+     */
+    if (!$this->is_template && $this->remove_folder_from_imap){
+
+      if(!$this->mailMethod->connect()){
+        msg_dialog::display(_("Mail error"), sprintf(_("Mail method cannot connect: %s"),
+              $this->mailMethod->get_error()), ERROR_DIALOG);
+      }else{
+        if(!$this->mailMethod->deleteMailbox()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot remove mailbox: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
+        if(!$this->mailMethod->updateSharedFolder()){
+          msg_dialog::display(_("Mail error"), sprintf(_("Cannot update shared folder permissions: %s"),
+                $this->mailMethod->get_error()), ERROR_DIALOG);
+        }
+      }
+    }
+    $this->mailMethod->disconnect();
+
+
     /* Optionally execute a command after we're done */
     $this->handle_post_events("remove");
   }
@@ -764,16 +585,8 @@ class mailgroup extends plugin
   /* Save data to object */
   function save_object()
   {
-
-    /* Add special kolab attributes */    
-    if(isset($this->config->current['MAILMETHOD'])&&preg_match("/olab/i",$this->config->current['MAILMETHOD'])){
-      if(isset($_POST['kolabFolderType_Type'])){
-        $this->kolabFolderType_Type = get_post("kolabFolderType_Type");
-        $this->kolabFolderType_SubType = get_post("kolabFolderType_SubType");
-      }
-    }
-  
-    /* Check if user wants to remove the shared folder from imap too */
+    /* Check if user wants to remove the shared folder from imap too 
+     */
     if($this->initially_was_account && !$this->is_account){
       if(isset($_POST['remove_folder_from_imap'])){
         $this->remove_folder_from_imap = true;
@@ -781,26 +594,100 @@ class mailgroup extends plugin
         $this->remove_folder_from_imap = false;
       }
     }
+    if (isset($_POST['mailedit'])){
+
+      if(isset($_POST['show_effective_memeber'])){
+        $this->show_effective_memeber = !$this->show_effective_memeber;
+      }
+
+      $mail = $this->mail;
+      $server = $this->gosaMailServer;
+      plugin::save_object();
+
+      if(!$this->mailMethod->isModifyableServer() && $this->initially_was_account){
+        $this->gosaMailServer = $server;
+      }
+      if(!$this->mailMethod->isModifyableMail() && $this->initially_was_account){
+        $this->mail = $mail;
+      }else{
+
+        if($this->mailMethod->mailEqualsCN()){
+          $this->mail = &$this->parent->by_object['group']->cn;
+          if(isset($_POST['MailDomain'])){
+            $this->mailDomainPart = get_post('MailDomain');
+          }
+        }
+
+      /* Get posted mail domain part, if necessary
+       */
+      if($this->mailMethod->domainSelectionEnabled() && isset($_POST['MailDomain'])){
+        if(in_array_strict(get_post('MailDomain'), $this->mailDomainParts)){
+          $this->mailDomainPart = get_post('MailDomain');
+        }
+      }
+    }
+
+      /* Get folder type 
+       */
+      if($this->mailMethod->folderTypesEnabled()){
+        if(isset($_POST['FolderTypeCAT'])){
+        $this->FolderType['CAT']     = get_post('FolderTypeCAT');
+        }
+        if(isset($_POST['FolderTypeSUB_CAT'])){
+          $this->FolderType['SUB_CAT'] = get_post('FolderTypeSUB_CAT');
+        }
+      }
+
+      /* Handle posted ACL changes. 
+         Add/del member acls.
+       */
+      if(isset($_POST['mail_acls_posted'])){
+        $new_acls = array();
+        foreach(array("__anyone__","__member__") as $attr){
+          $pname = base64_encode($attr);
+          if(get_post('acl_value_'.$pname)){ 
+            $new_acls[$attr] = get_post('acl_value_'.$pname);
+          }else{
+            $new_acls[$attr] = $this->folder_acls[$attr];
+          }
+        }
+
+        foreach($this->folder_acls as $user => $acl){
+          $pname = base64_encode($user);
+          if($user == "__member__" || $user == "__anyone__") continue;
+          if(isset($_POST['remove_acl_user_'.$pname])){
+          }elseif(isset($_POST['acl_user_'.$pname])){
+            if($user != get_post('acl_user_'.$pname)){
+              $new_acls[get_post('acl_user_'.$pname)] = get_post('acl_value_'.$pname);
+            }else{
+              $new_acls[$user] = get_post('acl_value_'.$pname);
+            }
+          }else{
+            $new_acls[$user] = $acl;
+          }
+        }
+        if(isset($_POST['add_acl_user'])){
+          $new_acls[_('New')] = $this->folder_acls['__anyone__'];
+        }
+        $this->folder_acls = $new_acls;
+      }
+
+      /* Handle GOsa mail delivery flags.
+       */
 
     /* Assemble mail delivery mode
        The mode field in ldap consists of values between braces, this must
        be called when 'mail' is set, because checkboxes may not be set when
        we're in some other dialog.
 
-       Example for gosaMailDeliveryMode [LR        ]
-L: Local delivery
-R: Reject when exceeding mailsize limit
-S: Use spam filter
-V: Use vacation message
-C: Use custom sieve script
-I: Only insider delivery */
-    if (isset($_POST['mailedit'])){
-
-      plugin::save_object();
-
+       Example for gosaMailDeliveryMode [LR        ]
+       L - Local delivery
+       R - Reject when exceeding mailsize limit
+       S - Use spam filter
+       V - Use vacation message
+       C - Use custom sieve script
+       I - Only insider delivery */
       $tmp= preg_replace("/[^a-z]/i","",$this->gosaMailDeliveryMode);
-
-      /* Handle delivery flags */
       if($this->acl_is_writeable("gosaMailDeliveryModeL")){
         if(!preg_match("/L/",$tmp) && !isset($_POST['drop_own_mails'])){
           $tmp.="L";
@@ -831,25 +718,7 @@ I: Only insider delivery */
         $this->is_modified= TRUE;
       }
       $this->gosaMailDeliveryMode= $tmp;
-
-      /* Collect data and re-assign it to the imapacl array */
-      if ($this->acl_is_writeable("acl")){
-        $this->imapacl= array();
-        $this->imapacl['%members%']= $_POST['member_permissions'];
-        $this->imapacl['anyone']= $_POST['default_permissions'];
-        foreach ($this->indexed_user as $nr => $user){
-          if (!isset($_POST["user_$nr"])){
-            continue;
-          }
-          if ($_POST["user_$nr"] != $user ||
-              $_POST["perm_$nr"] != $this->indexed_acl[$nr]){
-            $this->is_modified= TRUE;
-          }
-          $this->imapacl[$_POST["user_$nr"]]= $_POST["perm_$nr"];
-        }
-      }
     }
-
   }
 
 
@@ -857,133 +726,60 @@ I: Only insider delivery */
   function save()
   {
     $ldap= $this->config->get_ldap_link();
-    $ldap->cd($this->config->current['BASE']);
-
-    /* Call parents save to prepare $this->attrs */
-    plugin::save();
 
-    /* Save arrays */
-    $this->attrs['gosaMailAlternateAddress']  = $this->gosaMailAlternateAddress;
-    $this->attrs['gosaMailForwardingAddress'] = $this->gosaMailForwardingAddress;
-    $this->attrs['gosaSharedFolderTarget']    = "share+".$this->uid;
-    $this->attrs['acl']= array();
-
-    /* Prepare Mail server attribute */
-    if(preg_match("/olab/i",$this->mmethod)){
-      if (empty($this->gosaMailServer)||is_array($this->gosaMailServer)){
-        if(isset($this->attrs['gosaMailServer'][0])){
-          $this->gosaMailServer = $this->attrs['gosaMailServer'][0];
-        }
-      }
-    }  
- /* Exchange '%member%' pseudo entry */
-    $memberacl= $this->imapacl['%members%'];
-    foreach ($this->members as $user){
-      if (!isset($this->imapacl[$user])){
-        $this->imapacl[$user]= $memberacl;
-      }
-    }
+    /* If domain part was selectable, contruct mail address */
+    if(!(!$this->mailMethod->isModifyableMail() && $this->initially_was_account)){
 
-    /* Prepare kolab attributes to be written  */
-    if(preg_match("/olab/i",$this->mmethod)){
-      if(!empty($this->kolabFolderType_Type)){
-        $this->attrs['kolabFolderType'] = $this->kolabFolderType_Type.".".$this->kolabFolderType_SubType;
-      }else{
-        $this->attrs['kolabFolderType'] = array();
+      if($this->mailMethod->domainSelectionEnabled() || $this->mailMethod->mailEqualsCN()){
+        $this->mail = $this->mail."@".$this->mailDomainPart;
       }
     }
 
-    /* Get naming attribute for mail accounts */
-    $tmp = new $this->method($this->config);
-    $uattrib = $tmp->uattrib;
-
-    /* Create ACL array
-       What is done here.
-
-       1.  Do not write acl entries for empty entries.
-       2.  Check if given user is a valid GOsa user.
-       - If he is one, check if he has a valid mail extension
-       -If this is true, add the user to the ACL entry.
-       -If this is NOT true, skip ACL entries for this user.
-       - He is not a GOsa Account, so write the ACL. (Manually entered ACL)
-       3. In case of "olab" mail method, remove the entry from the $this->imapacl array
-       because the kolab deamon will set the acls for us.
-
+    /* Enforce lowercase mail address and trim whitespaces
      */
-    foreach ($this->imapacl as $user => $acl){
+    $this->mail = trim(strtolower($this->mail));
+    
 
-      /* Skip empty entries */
-      if (empty($user) || $user == ""){
-        unset($this->imapacl[$user]);
-      }
+    /* Create acls 
+     */
+    $this->acl = array("anyone ".$this->folder_acls['__anyone__']);
+    $member = $this->get_member();
+    $new_folder_acls = array("anyone" => $this->folder_acls['__anyone__']);
+    foreach($member['mail'] as $uid => $mail){
 
-      /* Skip placeholder */
-      if (empty($user) || $user == "" || preg_match("/%members%/",$user)){
+      /* Do not save overridden acls */
+      if(isset($this->folder_acls[$mail])){
         continue;
       }
 
-      /* Check if your is a real GOsa user
-       * If user is a real GOsa user but do not have an email address - SKIP adding acls
-       * If user is a real GOsa user with an email address - add acls
-       */
-      $ldap->search("(&(objectClass=person)(|(uid=".$user.")(mail=".$user.")))",array("mail","uid"));
-      if($ldap->count()){
-
-        /* Has the user a valid mail account? */
-        $attrs = $ldap->fetch();
-        if(isset($attrs['mail'][0])){
-
-          $name = $attrs[$uattrib][0];
-          $this->attrs['acl'][]= $name." ".$acl;
-
-          /* Do not write imap acl directly i nkolab mode, let the kolab deamon do this. */
-          unset($this->imapacl[$user]);
-          if(!preg_match("/olab/i",$this->mmethod)){
-            $this->imapacl[$name] = $acl;
-          }
-
-        }else{
-
-          /* User is a valid GOsa account, but he has no mail extension. Skip ACLs */
-          unset($this->imapacl[$user]);
-        }
-      }else{
-
-        /* Seems to be a manually a added acl
-         * Write this acl.
-         */
-        $this->attrs['acl'][]= $user." ".$acl;
-
-        /* In case of kolab methods, let the deamon add the imap acls */
-        if(preg_match("/olab/i",$this->mmethod)){
-          unset($this->imapacl[$user]);
-        }
-      }
+      $this->acl[] = $mail." ".$this->folder_acls['__member__'];
+      $new_folder_acls[$mail]=$this->folder_acls['__member__'];
+    }
+    foreach($this->folder_acls as $user => $acls){
+      if(preg_match("/^__/",$user)) continue;
+      $this->acl[] = $user." ".$acls;
+      $new_folder_acls[$user]=$acls;
     }
+    $this->folder_acls = $new_folder_acls;
+    $this->acl = array_unique($this->acl);
+     
+    /* Call parents save to prepare $this->attrs */
+    plugin::save();
 
-    /* Handle mail method actions, set acls, quota ...*/
-    if ((!$this->is_template)&&(!empty($this->gosaMailServer))){
-      $method= new $this->method($this->config);
-      $method->fixAttributesOnStore($this);
-      if (($method->connect($this->gosaMailServer))){
-        $method->updateMailbox($this->uid);
-        $method->setQuota($this->uid, $this->gosaMailQuota);
+    /* Save arrays */
+    $this->attrs['gosaMailAlternateAddress'] = $this->gosaMailAlternateAddress;
+    $this->attrs['gosaMailForwardingAddress']= $this->gosaMailForwardingAddress;
 
-        /* Only write imap acls directly if we are not
-         *  using a kolab like mail method.
-         */
-        if(!preg_match("/olab/i",$this->mmethod)){
-          $method->setSharedFolderPermissions($this->uid, $this->imapacl);
-        }
-        $method->disconnect();
-      }
-    }
+    /* Map method attributes */
+    $this->mailMethod->fixAttributesOnStore();
 
     /* Save data to LDAP */
     $ldap->cd($this->dn);
     $this->cleanup();
     $ldap->modify ($this->attrs); 
-    show_ldap_error($ldap->get_error(), sprintf(_("Saving of groups/mail with dn '%s' failed."),$this->dn));
+    if (!$ldap->success()){
+      msg_dialog::display(_("LDAP error"), msgPool::ldaperror($ldap->get_error(), $this->dn, LDAP_MOD, get_class()));
+    }
     
     if($this->initially_was_account){
       new log("modify","groups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());
@@ -991,6 +787,32 @@ I: Only insider delivery */
       new log("create","groups/".get_class($this),$this->dn,array_keys($this->attrs),$ldap->get_error());  
     }
     
+    /* Do imap/sieve actions,
+     */
+    $this->mailMethod->connect();
+    if(!$this->mailMethod->is_connected()){
+      msg_dialog::display(_("Mail error"), sprintf(_("Mail method cannot connect: %s"),
+            $this->mailMethod->get_error()), ERROR_DIALOG);
+    }else{
+      if(!$this->mailMethod->updateMailbox()){
+        msg_dialog::display(_("Mail error"), sprintf(_("Cannot update mailbox: %s"),
+              $this->mailMethod->get_error()), ERROR_DIALOG);
+      }
+      if(!$this->mailMethod->setQuota($this->gosaMailQuota)){
+        msg_dialog::display(_("Mail error"), sprintf(_("Cannot write quota settings: %s"),
+              $this->mailMethod->get_error()), ERROR_DIALOG);
+      }
+      /* Save Folder Types, if available 
+       */
+      if($this->mailMethod->folderTypesEnabled()){
+        $this->mailMethod->setFolderType($this->FolderType);
+      }
+      if(!$this->mailMethod->setFolderACLs($this->folder_acls)){
+        msg_dialog::display(_("Mail error"), sprintf(_("Cannot update shared folder permissions: %s"),
+              $this->mailMethod->get_error()), ERROR_DIALOG);
+      }
+    }
+    $this->mailMethod->disconnect();
 
     /* Optionally execute a command after we're done */
     if ($this->initially_was_account == $this->is_account){
@@ -1007,42 +829,62 @@ I: Only insider delivery */
   /* Check formular input */
   function check()
   {
+    if(!$this->is_account) return array();
     $ldap= $this->config->get_ldap_link();
 
+
     /* Call common method to give check the hook */
     $message= plugin::check();
 
-    if(!$this->is_account) return array();
-    
-    //$message[] = $str;      
-
-    /* must: mail */
-    if ($this->mail == ""){
-      $message[]= _("The required field 'Primary address' is not set.");
+    /* Ensure that this group isn't renamed if the mailMethod enforces cn mailAttributes 
+     */
+    if($this->mailMethod->mailEqualsCN() && $this->initially_was_account){
+      if($this->cn != $this->orig_cn){
+        $message[] = sprintf(_("The group 'cn' has changed. It can't be changed due to the fact that mail method '%s' relies on it!")
+            ,get_class($this->mailMethod));
+      }
     }
-    if (!tests::is_email($this->mail)){
-      $message[]= _("Please enter a valid email addres in 'Primary address' field.");
+
+    if(empty($this->gosaMailServer)){
+      $message[]= msgPool::noserver(_("Mail"));
     }
-    $ldap->cd($this->config->current['BASE']);
-    $ldap->search ("(&(!(objectClass=gosaUserTemplate))(objectClass=gosaMailAccount)(|(mail=".$this->mail.")(gosaMailAlternateAddress=".
-        $this->mail."))(!(uid=".$this->orig_cn."))(!(cn=".$this->orig_cn.")))");
-    if ($ldap->count() != 0){
-      $message[]= _("The primary address you've entered is already in use.");
+
+    /* Mail address checks */
+    $mail = $this->mail;
+    if(!(!$this->mailMethod->isModifyableMail() && $this->initially_was_account)){
+      if($this->mailMethod->domainSelectionEnabled() || $this->mailMethod->mailEqualsCN()){
+        $mail.= "@".$this->mailDomainPart;
+      }
+      if (empty($mail)){
+        $message[]= msgPool::required(_("Primary address"));
+      }elseif (!tests::is_email($mail)){
+        $message[]= msgPool::invalid(_("Mail address"),"","","your-address@your-domain.com");
+      }
     }
-  
+    
     /* Check quota */
     if ($this->gosaMailQuota != '' && $this->acl_is_writeable("gosaMailQuota")){
       if (!is_numeric($this->gosaMailQuota)) {
-        $message[]= _("Value in 'Quota size' is not valid.");
+        $message[]= msgPool::invalid(_("Quota size"),$this->gosaMailQuota,"/[0-9]/");
       } else {
         $this->gosaMailQuota= (int) $this->gosaMailQuota;
       }
     }
 
+    /* Check if this mail address is already in use */
+    $ldap->cd($this->config->current['BASE']);
+    $filter = "(&(!(objectClass=gosaUserTemplate))(!(cn=".$this->cn."))".
+           "(objectClass=gosaMailAccount)".
+           "(|(mail=".$mail.")(alias=".$mail.")(gosaMailAlternateAddress=".$mail.")))";
+    $ldap->search($filter,array("cn"));
+    if ($ldap->count() != 0){
+           $message[]= msgPool::duplicated(_("Mail address"));
+    }
+
     /* Check rejectsize for integer */
     if ($this->gosaMailMaxSize != '' && $this->acl_is_writeable("gosaMailQuota")){
       if (!is_numeric($this->gosaMailMaxSize)){
-        $message[]= _("Please specify a vaild mail size for mails to be rejected.");
+        $message[]= msgPool::invalid(_("Mail max size"));
       } else {
         $this->gosaMailMaxSize= (int) $this->gosaMailMaxSize;
       }
@@ -1053,23 +895,24 @@ I: Only insider delivery */
       $message[]= _("You need to set the maximum mail size in order to reject anything.");
     }
 
-    if(ord($this->imapacl['anyone'][0])==194){
-      $message[] = _("Please choose valid permission settings. Default permission can't be emtpy.");
-    }
-
     if(empty($this->gosaMailServer)){
-      $message[] = _("Please select a valid mail server.");
+      $message[] = msgPool::required(_("Mail server"));
     }
 
     return ($message);
   }
 
   /* Adapt from template, using 'dn' */
-  function adapt_from_template($dn)
+  function adapt_from_template($dn, $skip= array())
   {
-    plugin::adapt_from_template($dn);
+    plugin::adapt_from_template($dn, $skip);
 
     foreach (array("gosaMailAlternateAddress", "gosaMailForwardingAddress") as $val){
+      if (in_array_strict($val, $skip)){
+        continue;
+      }
+
       $this->$val= array();
       if (isset($this->attrs["$val"]["count"])){
         for ($i= 0; $i<$this->attrs["$val"]["count"]; $i++){
@@ -1085,74 +928,6 @@ I: Only insider delivery */
     }
   }
 
-  /* Add entry to forwarder list */
-  function addForwarder($address)
-  {
-    $this->gosaMailForwardingAddress[]= $address;
-    $this->gosaMailForwardingAddress= array_unique($this->gosaMailForwardingAddress);
-
-    /* Update multiple edit values too */
-    if($this->multiple_support_active){
-      $this->gosaMailForwardingAddress_Some= 
-        array_remove_entries (array($address),$this->gosaMailForwardingAddress_Some);
-    }
-
-    sort ($this->gosaMailForwardingAddress);
-    reset ($this->gosaMailForwardingAddress);
-    $this->is_modified= TRUE;
-  }
-
-  /* Remove list of addresses from forwarder list */
-  function delForwarder($addresses)
-  {
-    $this->gosaMailForwardingAddress= array_remove_entries ($addresses,
-        $this->gosaMailForwardingAddress);
-
-    /* Update multiple edit values too */
-    if($this->multiple_support_active){
-      $this->gosaMailForwardingAddress_Some = array_remove_entries ($addresses,
-          $this->gosaMailForwardingAddress_Some);
-    }
-    $this->is_modified= TRUE;
-  }
-
-
-
-  function addAlternate($address)
-  {
-    $ldap= $this->config->get_ldap_link();
-
-    $address= strtolower($address);
-
-    /* Is this address already assigned in LDAP? */
-    $ldap->cd ($this->config->current['BASE']);
-    $ldap->search ("(&(objectClass=gosaMailAccount)(|(mail=$address)".
-        "(gosaMailAlternateAddress=$address)))");
-
-    if ($ldap->count() > 0){
-      $attrs= $ldap->fetch ();
-      return ($attrs["uid"][0]);
-    }
-
-    /* Add to list of alternates */
-    if (!in_array($address, $this->gosaMailAlternateAddress)){
-      $this->gosaMailAlternateAddress[]= $address;
-    }
-
-    sort ($this->gosaMailAlternateAddress);
-    reset ($this->gosaMailAlternateAddress);
-    $this->is_modified= TRUE;
-
-    return ("");
-  }
-
-
-  function delAlternate($addresses)
-  {
-    $this->gosaMailAlternateAddress= array_remove_entries ($addresses,
-        $this->gosaMailAlternateAddress);
-    $this->is_modified= TRUE;
-  }
 
 
   function make_name($attrs)
@@ -1220,15 +995,17 @@ I: Only insider delivery */
           "plDescription" => _("Group mail"),
           "plSelfModify"  => FALSE,
           "plDepends"     => array(),
-          "plPriority"    => 0,
+          "plPriority"    => 10,
           "plSection"     => array("administration"),
           "plCategory"    => array("groups"), 
           "plProvidedAcls"=> array(
             "mail"                      => _("Mail address"),
-            "gosaMailAlternateAddress"  => _("Alternate addresses"),
-            "gosaMailForwardingAddress" => _("Forwarding addresses"),
             "gosaMailQuota"             => _("Quota size"),
             "gosaMailServer"            => _("Mail server"),
+            "kolabFolderType"           => _("Folder type")." ("._("Kolab").")",
+            "gosaMailAlternateAddress"  => _("Alternate addresses"),
+            "gosaMailForwardingAddress" => _("Forwarding addresses"),
+            "gosaMailDeliveryModeI"     => _("Only local"),
             "acl"                       => _("Permissions"))
           ));
   }
@@ -1263,7 +1040,7 @@ I: Only insider delivery */
     $this->gosaMailForwardingAddress_Some = array();
     if(isset($all['gosaMailForwardingAddress'])){
       for($i = 0 ; $i < $all['gosaMailForwardingAddress']['count'] ; $i++){
-        if(!in_array($all['gosaMailForwardingAddress'][$i],$this->gosaMailForwardingAddress)){
+        if(!in_array_strict($all['gosaMailForwardingAddress'][$i],$this->gosaMailForwardingAddress)){
           $this->gosaMailForwardingAddress_Some[] = $all['gosaMailForwardingAddress'][$i];
         }
       }
@@ -1282,10 +1059,10 @@ I: Only insider delivery */
       }
 
       /* Add special kolab attributes */
-      if(isset($this->config->current['MAILMETHOD'])&&preg_match("/olab/i",$this->config->current['MAILMETHOD'])){
-        if(isset($_POST['kolabFolderType_Type'])){
-          $this->kolabFolderType_Type = get_post("kolabFolderType_Type");
-          $this->kolabFolderType_SubType = get_post("kolabFolderType_SubType");
+      if(preg_match("/olab/i",$this->config->get_cfg_value("mailmethod"))){
+        if(isset($_POST['kolabFolderTypeType']) && $this->acl_is_writeable("kolabFolderType")){
+          $this->kolabFolderTypeType = get_post("kolabFolderTypeType");
+          $this->kolabFolderTypeSubType = get_post("kolabFolderTypeSubType");
         }
       }
 
@@ -1315,11 +1092,11 @@ I: Only insider delivery */
     $ret = plugin::get_multi_edit_values();
     $ret['Forward_some'] = $this->gosaMailForwardingAddress_Some;    
     $ret['Forward_all'] = $this->gosaMailForwardingAddress;    
-    if(in_array('kolabFolderType',$this->multi_boxes)){
-      $ret['kolabFolderType_Type'] = $this->kolabFolderType_Type;
-      $ret['kolabFolderType_SubType'] = $this->kolabFolderType_SubType;
+    if(in_array_strict('kolabFolderType',$this->multi_boxes)){
+      $ret['kolabFolderTypeType'] = $this->kolabFolderTypeType;
+      $ret['kolabFolderTypeSubType'] = $this->kolabFolderTypeSubType;
     }
-    if(in_array("acl",$this->multi_boxes)){
+    if(in_array_strict("acl",$this->multi_boxes)){
       $ret['imapacl'] = $this->imapacl;
     }
     return($ret);
@@ -1329,7 +1106,7 @@ I: Only insider delivery */
   {
     $forward = array();
     foreach($attrs['Forward_some'] as $addr){
-      if(in_array($addr,$this->gosaMailForwardingAddress)){
+      if(in_array_strict($addr,$this->gosaMailForwardingAddress)){
         $forward[] = $addr;
       }
     }
@@ -1339,6 +1116,128 @@ I: Only insider delivery */
     plugin::set_multi_edit_values($attrs);
     $this->gosaMailForwardingAddress = $forward;
   }
+
+
+  /*! \brief  Add given mail address to the list of forwarders.
+   */
+  function addForwarder($address)
+  {
+    if(empty($address)) return;
+    $this->gosaMailForwardingAddress[]= $address;
+    $this->gosaMailForwardingAddress= array_unique($this->gosaMailForwardingAddress);
+
+    /* Update multiple edit values too */
+    if($this->multiple_support_active){
+      $this->gosaMailForwardingAddress_Some= 
+        array_remove_entries (array($address),$this->gosaMailForwardingAddress_Some);
+    }
+
+    sort ($this->gosaMailForwardingAddress);
+    reset ($this->gosaMailForwardingAddress);
+    $this->is_modified= TRUE;
+  }
+
+
+  /*! \brief  Removes the given mail address from the forwarders 
+   */
+  function delForwarder($addresses)
+  {
+    if(empty($addresses)) return;
+    $this->gosaMailForwardingAddress= array_remove_entries ($addresses,
+        $this->gosaMailForwardingAddress);
+
+    /* Update multiple edit values too */
+    if($this->multiple_support_active){
+      $this->gosaMailForwardingAddress_Some = array_remove_entries ($addresses,
+          $this->gosaMailForwardingAddress_Some);
+    }
+    $this->is_modified= TRUE;
+  }
+
+
+  /*! \brief  Add given mail address to the list of alternate adresses ,
+    .          check if this mal address is used, skip adding in this case
+   */
+  function addAlternate($address)
+  {
+    if(empty($address)) continue;
+    $ldap= $this->config->get_ldap_link();
+
+    $address= strtolower($address);
+
+    /* Is this address already assigned in LDAP? */
+    $ldap->cd ($this->config->current['BASE']);
+    $ldap->search ("(&(objectClass=gosaMailAccount)(|(mail=$address)".
+        "(gosaMailAlternateAddress=$address)))", array("cn", "uid"));
+
+    if ($ldap->count() > 0){
+      $attrs= $ldap->fetch ();
+      if (!isset($attrs["uid"])) {
+        return ("!".$attrs["cn"][0]);
+      }
+      return ($attrs["uid"][0]);
+    }
+
+    /* Add to list of alternates */
+    if (!in_array_strict($address, $this->gosaMailAlternateAddress)){
+      $this->gosaMailAlternateAddress[]= $address;
+    }
+
+    sort ($this->gosaMailAlternateAddress);
+    reset ($this->gosaMailAlternateAddress);
+    $this->is_modified= TRUE;
+
+    return ("");
+  }
+
+
+  /*! \brief  Removes the given mail address from the alternate addresses  
+   */
+  function delAlternate($addresses)
+  {
+    if(!count($addresses)) return;
+    $this->gosaMailAlternateAddress= array_remove_entries ($addresses,
+        $this->gosaMailAlternateAddress);
+    $this->is_modified= TRUE;
+  }
+
+
+  function postable_acls()
+  {
+    $ret = array();
+    foreach($this->folder_acls as $name => $acl){
+      $ret[$name] = array("name" => $name,"acl" => $acl,"post_name" => base64_encode($name));
+    }
+    return($ret);
+  }
+
+
+  function get_effective_member_acls()
+  {
+    $tmp = array();
+    $member = $this->get_member();
+    foreach($member['mail'] as $uid => $mail){
+
+      /* Do not save overridden acls */
+      if(isset($this->folder_acls[$mail])){
+        continue;
+      }
+
+      
+      $tmp[$mail]  = $this->folder_acls['__member__'];
+    }
+    return($tmp);
+  }
+
+
+  function allow_remove()
+  {
+    $resason = "";
+    if(!$this->mailMethod->allow_remove($reason)){
+      return($reason);
+    }
+    return("");
+  }
 }
 
   // vim:tabstop=2:expandtab:shiftwidth=2:filetype=php:syntax:ruler: