From 6039aae3878aa5880415290cbc41af4bac4fdcb5 Mon Sep 17 00:00:00 2001
From: thomascube <thomas@roundcube.net>
Date: Sat, 29 Jan 2011 06:31:23 -0500
Subject: [PATCH] Add groups support for LDAP address books, contributed by Andreas Dick

---
 program/include/rcube_ldap.php     |  269 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 program/steps/addressbook/save.inc |    2 
 program/include/rcmail.php         |    2 
 config/main.inc.php.dist           |    9 +
 4 files changed, 277 insertions(+), 5 deletions(-)

diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index cf111db..7dfca7a 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -501,8 +501,13 @@
   'scope'         => 'sub',   // search mode: sub|base|list
   'filter'        => '',      // used for basic listing (if not empty) and will be &'d with search queries. example: status=act
   'fuzzy_search'  => true,    // server allows wildcard search
-  'sizelimit'     => '0',     // Enables you to limit the count of entries fetched. Setting this to 0 means no limit. 
-  'timelimit'     => '0',     // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit. 
+  'sizelimit'     => '0',     // Enables you to limit the count of entries fetched. Setting this to 0 means no limit.
+  'timelimit'     => '0',     // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit.
+  // definition for groups, set to false if no groups are supported
+  'groups'        => array(
+    'base_dn'     => 'ou=groups,ou=rcabook,dc=localhost',
+    'filter'      => '(objectClass=groupOfNames)',
+  ),
 );
 */
 
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index 7f76ba4..ab2c161 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -394,7 +394,7 @@
         $list[$id] = array(
           'id' => $id,
           'name' => $prop['name'],
-          'groups' => false,
+          'groups' => is_array($prop['groups']),
           'readonly' => !$prop['writable'],
           'autocomplete' => in_array('sql', $autocomplete)
         );
diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index 7ea22eb..4fe74e3 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -4,7 +4,7 @@
  | program/include/rcube_ldap.php                                        |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2006-2010, The Roundcube Dev Team                       |
+ | Copyright (C) 2006-2011, The Roundcube Dev Team                       |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -12,6 +12,7 @@
  |                                                                       |
  +-----------------------------------------------------------------------+
  | Author: Thomas Bruederli <roundcube@gmail.com>                        |
+ |         Andreas Dick <andudi (at) gmx (dot) ch>                       |
  +-----------------------------------------------------------------------+
 
  $Id$
@@ -40,10 +41,15 @@
   /** public properties */
   public $primary_key = 'ID';
   public $readonly = true;
+  public $groups = false;
   public $list_page = 1;
   public $page_size = 10;
+  public $group_id = 0;
   public $ready = false;
   public $coltypes = array();
+
+  private $group_cache = array();
+  private $group_members = array();
 
 
   /**
@@ -58,6 +64,10 @@
   {
     $this->prop = $p;
 
+    // check if groups are configured
+    if (is_array($p['groups']))
+      $this->groups = true;
+    
     // fieldmap property is given
     if (is_array($p['fieldmap'])) {
       foreach ($p['fieldmap'] as $rf => $lf)
@@ -345,6 +355,21 @@
       $entries = ldap_get_entries($this->conn, $this->ldap_result);
       for ($i = $start_row; $i < min($entries['count'], $last_row); $i++)
         $this->result->add($this->_ldap2result($entries[$i]));
+    }
+
+    // temp hack for filtering group members
+    if ($this->group_id)
+    {
+        $result = new rcube_result_set();
+        while ($record = $this->result->iterate())
+        {
+            if ($this->group_members[$record['ID']])
+            {
+                $result->add($record);
+                $result->count++;
+            }
+        }
+        $this->result = $result;
     }
 
     return $this->result;
@@ -799,5 +824,247 @@
     return strtr($str, $replace);
   }
 
+
+    /**
+     * Setter for the current group
+     * (empty, has to be re-implemented by extending class)
+     */
+    function set_group($group_id)
+    {
+        if ($group_id)
+        {
+            if (! $this->group_cache) $this->list_groups();
+            $cache = $this->group_cache[$group_id]['members'];
+
+            $members = array();
+            for ($i=1; $i<$cache["count"]; $i++)
+            {
+                $member_dn = base64_encode($cache[$i]);
+                $members[$member_dn] = 1;
+            }
+            $this->group_members = $members;
+            $this->group_id = $group_id;
+        }
+        else $this->group_id = 0;
+    }
+
+    /**
+     * List all active contact groups of this source
+     *
+     * @param string  Optional search string to match group name
+     * @return array  Indexed list of contact groups, each a hash array
+     */
+    function list_groups($search = null)
+    {
+        if (!$this->prop['groups'])
+          return array();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $filter = $this->prop['groups']['filter'];
+
+        $res = ldap_search($this->conn, $base_dn, $filter, array('cn','member'));
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return array();
+        }
+        $ldap_data = ldap_get_entries($this->conn, $res);
+
+        $groups = array();
+        $group_sortnames = array();
+        for ($i=0; $i<$ldap_data["count"]; $i++)
+        {
+            $group_name = $ldap_data[$i]['cn'][0];
+            $group_id = base64_encode($group_name);
+            $groups[$group_id]['ID'] = $group_id;
+            $groups[$group_id]['name'] = $group_name;
+            $groups[$group_id]['members'] = $ldap_data[$i]['member'];
+            $group_sortnames[] = strtolower($group_name);
+        }
+        array_multisort($group_sortnames, SORT_ASC, SORT_STRING, $groups);
+
+        $this->group_cache = $groups;
+        return $groups;
+    }
+
+    /**
+     * Create a contact group with the given name
+     *
+     * @param string The group name
+     * @return mixed False on error, array with record props in success
+     */
+    function create_group($group_name)
+    {
+        if (!$this->group_cache)
+            $this->list_groups();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $new_dn = "cn=$group_name,$base_dn";
+        $new_gid = base64_encode($group_name);
+
+        $new_entry = array(
+            'objectClass' => array('top', 'groupOfNames'),
+            'cn' => $group_name,
+            'member' => '',
+        );
+
+        $res = ldap_add($this->conn, $new_dn, $new_entry);
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return false;
+        }
+        return array('id' => $new_gid, 'name' => $group_name);
+    }
+
+    /**
+     * Delete the given group and all linked group members
+     *
+     * @param string Group identifier
+     * @return boolean True on success, false if no data was changed
+     */
+    function delete_group($group_id)
+    {
+        if (!$this->group_cache)
+            $this->list_groups();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $group_name = $this->group_cache[$group_id]['name'];
+
+        $del_dn = "cn=$group_name,$base_dn";
+        $res = ldap_delete($this->conn, $del_dn);
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Rename a specific contact group
+     *
+     * @param string Group identifier
+     * @param string New name to set for this group
+     * @return boolean New name on success, false if no data was changed
+     */
+    function rename_group($group_id, $new_name)
+    {
+        if (!$this->group_cache)
+            $this->list_groups();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $group_name = $this->group_cache[$group_id]['name'];
+        $old_dn = "cn=$group_name,$base_dn";
+        $new_rdn = "cn=$new_name";
+
+        $res = ldap_rename($this->conn, $old_dn, $new_rdn, NULL, TRUE);
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return false;
+        }
+        return $new_name;
+    }
+
+    /**
+     * Add the given contact records the a certain group
+     *
+     * @param string  Group identifier
+     * @param array   List of contact identifiers to be added
+     * @return int    Number of contacts added
+     */
+    function add_to_group($group_id, $contact_ids)
+    {
+        if (!$this->group_cache)
+            $this->list_groups();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $group_name = $this->group_cache[$group_id]['name'];
+        $group_dn = "cn=$group_name,$base_dn";
+
+        $new_attrs = array();
+        foreach (explode(",", $contact_ids) as $id)
+            $new_attrs['member'][] = base64_decode($id);
+
+        $res = ldap_mod_add($this->conn, $group_dn, $new_attrs);
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return 0;
+        }
+        return count($new_attrs['member']);
+    }
+
+    /**
+     * Remove the given contact records from a certain group
+     *
+     * @param string  Group identifier
+     * @param array   List of contact identifiers to be removed
+     * @return int    Number of deleted group members
+     */
+    function remove_from_group($group_id, $contact_ids)
+    {
+        if (!$this->group_cache)
+            $this->list_groups();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $group_name = $this->group_cache[$group_id]['name'];
+        $group_dn = "cn=$group_name,$base_dn";
+
+        $del_attrs = array();
+        foreach (explode(",", $contact_ids) as $id)
+            $del_attrs['member'][] = base64_decode($id);
+
+        $res = ldap_mod_del($this->conn, $group_dn, $del_attrs);
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return 0;
+        }
+        return count($del_attrs['member']);
+    }
+
+    /**
+     * Get group assignments of a specific contact record
+     *
+     * @param mixed Record identifier
+     *
+     * @return array List of assigned groups as ID=>Name pairs
+     * @since 0.5-beta
+     */
+    function get_record_groups($contact_id)
+    {
+        if (!$this->prop['groups'])
+            return array();
+
+        $base_dn = $this->prop['groups']['base_dn'];
+        $contact_dn = base64_decode($contact_id);
+        $filter = "(member=$contact_dn)";
+
+        $res = ldap_search($this->conn, $base_dn, $filter, array('cn'));
+        if ($res === false)
+        {
+            $this->_debug("S: ".ldap_error($this->conn));
+            $this->set_error(self::ERROR_SAVING, 'errorsaving');
+            return array();
+        }
+        $ldap_data = ldap_get_entries($this->conn, $res);
+
+        $groups = array();
+        for ($i=0; $i<$ldap_data["count"]; $i++)
+        {
+            $group_name = $ldap_data[$i]['cn'][0];
+            $group_id = base64_encode($group_name);
+            $groups[$group_id] = $group_id;
+        }
+        return $groups;
+    }
 }
 
diff --git a/program/steps/addressbook/save.inc b/program/steps/addressbook/save.inc
index 5d8b531..4eb24fe 100644
--- a/program/steps/addressbook/save.inc
+++ b/program/steps/addressbook/save.inc
@@ -242,7 +242,7 @@
 
   if ($insert_id) {
     // add new contact to the specified group
-    if ($CONTACTS->group_id) {
+    if ($CONTACTS->groups && $CONTACTS->group_id) {
       $plugin = $RCMAIL->plugins->exec_hook('group_addmembers', array('group_id' => $CONTACTS->group_id, 'ids' => $insert_id, 'source' => $source));
 
       if (!$plugin['abort']) {

--
Gitblit v1.9.1