From 3412e50b54e3daac8745234e21ab6e72be0ed165 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Wed, 04 Jun 2014 11:20:33 -0400
Subject: [PATCH] Fix attachment menu structure and aria-attributes

---
 program/lib/Roundcube/rcube_ldap_generic.php |  236 +++++++++++++++++++++++++++++------------------------------
 1 files changed, 116 insertions(+), 120 deletions(-)

diff --git a/program/lib/Roundcube/rcube_ldap_generic.php b/program/lib/Roundcube/rcube_ldap_generic.php
index e845727..f1048ef 100644
--- a/program/lib/Roundcube/rcube_ldap_generic.php
+++ b/program/lib/Roundcube/rcube_ldap_generic.php
@@ -31,7 +31,7 @@
       // or
       'hosts'           => array('directory.verisign.com'),
       'port'            => 389,
-      'use_tls'	        => true|false,
+      'use_tls'         => true|false,
       'ldap_version'    => 3,             // using LDAPv3
       'auth_method'     => '',            // SASL authentication method (for proxy auth), e.g. DIGEST-MD5
       'attributes'      => array('dn'),   // List of attributes to read from the server
@@ -76,10 +76,9 @@
     /**
     * Object constructor
     *
-    * @param array   $p       LDAP connection properties
-    * @param boolean $debug   Enables debug mode
+    * @param array $p LDAP connection properties
     */
-    function __construct($p, $debug = false)
+    function __construct($p)
     {
         $this->config = $p;
 
@@ -88,8 +87,6 @@
 
         if (!is_array($p['hosts']) && !empty($p['host']))
             $this->config['hosts'] = array($p['host']);
-
-        $this->debug = $debug;
     }
 
     /**
@@ -138,7 +135,6 @@
         $this->page_size = $size;
     }
 
-
     /**
     * Establish a connection to the LDAP server
     */
@@ -164,7 +160,7 @@
                 $this->config['hosts'] = array($this->config['hosts']);
 
             foreach ($this->config['hosts'] as $host) {
-                if ($this->connect($host)) {
+                if (!empty($host) && $this->connect($host)) {
                     return true;
                 }
             }
@@ -176,7 +172,7 @@
         $host     = rcube_utils::idn_to_ascii(rcube_utils::parse_host($host));
         $hostname = $host . ($this->config['port'] ? ':'.$this->config['port'] : '');
 
-        $this->_debug("C: Connect [$hostname] [{$this->config['name']}]");
+        $this->_debug("C: Connect to $hostname [{$this->config['name']}]");
 
         if ($lc = @ldap_connect($host, $this->config['port'])) {
             if ($this->config['use_tls'] === true)
@@ -209,7 +205,6 @@
 
         return true;
     }
-
 
     /**
      * Bind connection with (SASL-) user and password
@@ -245,7 +240,7 @@
             $method = 'DIGEST-MD5';
         }
 
-        $this->_debug("C: Bind [mech: $method, authc: $authc, authz: $authz] [pass: $pass]");
+        $this->_debug("C: SASL Bind [mech: $method, authc: $authc, authz: $authz, pass: **** [" . strlen($pass) . "]");
 
         if (ldap_sasl_bind($this->conn, NULL, $pass, $method, NULL, $authc, $authz)) {
             $this->_debug("S: OK");
@@ -262,7 +257,6 @@
         return false;
     }
 
-
     /**
      * Bind connection with DN and password
      *
@@ -277,7 +271,7 @@
             return false;
         }
 
-        $this->_debug("C: Bind [dn: $dn] [pass: $pass]");
+        $this->_debug("C: Bind $dn, pass: **** [" . strlen($pass) . "]");
 
         if (@ldap_bind($this->conn, $dn, $pass)) {
             $this->_debug("S: OK");
@@ -295,7 +289,6 @@
         return false;
     }
 
-
     /**
      * Close connection to LDAP server
      */
@@ -308,7 +301,6 @@
         }
     }
 
-
     /**
      * Return the last result set
      *
@@ -318,7 +310,6 @@
     {
         return $this->result;
     }
-
 
     /**
      * Get a specific LDAP entry, identified by its DN
@@ -331,7 +322,7 @@
         $rec = null;
 
         if ($this->conn && $dn) {
-            $this->_debug("C: Read [dn: $dn] [(objectclass=*)]");
+            $this->_debug("C: Read $dn [(objectclass=*)]");
 
             if ($ldap_result = @ldap_read($this->conn, $dn, '(objectclass=*)', $this->attributes)) {
                 $this->_debug("S: OK");
@@ -352,7 +343,6 @@
         return $rec;
     }
 
-
     /**
      * Execute the LDAP search based on the stored credentials
      *
@@ -369,65 +359,70 @@
      */
     public function search($base_dn, $filter = '', $scope = 'sub', $attrs = array('dn'), $prop = array(), $count_only = false)
     {
-        if ($this->conn) {
-            if (empty($filter))
-                $filter = $filter = '(objectclass=*)';
+        if (!$this->conn) {
+            return false;
+        }
 
-            $this->_debug("C: Search [$filter][dn: $base_dn]");
+        if (empty($filter)) {
+            $filter = '(objectclass=*)';
+        }
 
-            $function = self::scope2func($scope, $ns_function);
+        $this->_debug("C: Search $base_dn for $filter");
 
-            // find available VLV index for this query
-            if (!$count_only && ($vlv_sort = $this->_find_vlv($base_dn, $filter, $scope, $prop['sort']))) {
-                // when using VLV, we get the total count by...
-                // ...either reading numSubOrdinates attribute
-                if ($this->config['numsub_filter'] && ($result_count = @$ns_function($this->conn, $base_dn, $this->config['numsub_filter'], array('numSubOrdinates'), 0, 0, 0))) {
-                    $counts = ldap_get_entries($this->conn, $result_count);
-                    for ($vlv_count = $j = 0; $j < $counts['count']; $j++)
-                        $vlv_count += $counts[$j]['numsubordinates'][0];
-                    $this->_debug("D: total numsubordinates = " . $vlv_count);
-                }
-                // ...or by fetching all records dn and count them
-                else if (!function_exists('ldap_parse_virtuallist_control')) {
-                    $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $prop, true);
-                }
+        $function = self::scope2func($scope, $ns_function);
 
-                $this->vlv_active = $this->_vlv_set_controls($vlv_sort, $this->list_page, $this->page_size, $prop['search']);
-            }
-            else {
-                $this->vlv_active = false;
-            }
-
-            // only fetch dn for count (should keep the payload low)
-            if ($ldap_result = $function($this->conn, $base_dn, $filter,
-                $attrs, 0, (int)$this->config['sizelimit'], (int)$this->config['timelimit'])
+        // find available VLV index for this query
+        if (!$count_only && ($vlv_sort = $this->_find_vlv($base_dn, $filter, $scope, $prop['sort']))) {
+            // when using VLV, we get the total count by...
+            // ...either reading numSubOrdinates attribute
+            if (($sub_filter = $this->config['numsub_filter']) &&
+                ($result_count = @$ns_function($this->conn, $base_dn, $sub_filter, array('numSubOrdinates'), 0, 0, 0))
             ) {
-                // when running on a patched PHP we can use the extended functions to retrieve the total count from the LDAP search result
-                if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) {
-                    if (ldap_parse_result($this->conn, $ldap_result, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)) {
-                        ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $vlv_count, $vresult);
-                        $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$vlv_count");
-                    }
-                    else {
-                        $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn)));
-                    }
-                }
-                else if ($this->debug) {
-                    $this->_debug("S: ".ldap_count_entries($this->conn, $ldap_result)." record(s) found");
-                }
-
-                $this->result = new rcube_ldap_result($this->conn, $ldap_result, $base_dn, $filter, $vlv_count);
-
-                return $count_only ? $this->result->count() : $this->result;
+                $counts = ldap_get_entries($this->conn, $result_count);
+                for ($vlv_count = $j = 0; $j < $counts['count']; $j++)
+                    $vlv_count += $counts[$j]['numsubordinates'][0];
+                $this->_debug("D: total numsubordinates = " . $vlv_count);
             }
-            else {
-                $this->_debug("S: ".ldap_error($this->conn));
+            // ...or by fetching all records dn and count them
+            else if (!function_exists('ldap_parse_virtuallist_control')) {
+                $vlv_count = $this->search($base_dn, $filter, $scope, array('dn'), $prop, true);
             }
+
+            $this->vlv_active = $this->_vlv_set_controls($vlv_sort, $this->list_page, $this->page_size, $prop['search']);
+        }
+        else {
+            $this->vlv_active = false;
+        }
+
+        // only fetch dn for count (should keep the payload low)
+        if ($ldap_result = @$function($this->conn, $base_dn, $filter,
+            $attrs, 0, (int)$this->config['sizelimit'], (int)$this->config['timelimit'])
+        ) {
+            // when running on a patched PHP we can use the extended functions
+            // to retrieve the total count from the LDAP search result
+            if ($this->vlv_active && function_exists('ldap_parse_virtuallist_control')) {
+                if (ldap_parse_result($this->conn, $ldap_result, $errcode, $matcheddn, $errmsg, $referrals, $serverctrls)) {
+                    ldap_parse_virtuallist_control($this->conn, $serverctrls, $last_offset, $vlv_count, $vresult);
+                    $this->_debug("S: VLV result: last_offset=$last_offset; content_count=$vlv_count");
+                }
+                else {
+                    $this->_debug("S: ".($errmsg ? $errmsg : ldap_error($this->conn)));
+                }
+            }
+            else if ($this->debug) {
+                $this->_debug("S: ".ldap_count_entries($this->conn, $ldap_result)." record(s) found");
+            }
+
+            $this->result = new rcube_ldap_result($this->conn, $ldap_result, $base_dn, $filter, $vlv_count);
+
+            return $count_only ? $this->result->count() : $this->result;
+        }
+        else {
+            $this->_debug("S: ".ldap_error($this->conn));
         }
 
         return false;
     }
-
 
     /**
      * Modify an LDAP entry on the server
@@ -450,7 +445,7 @@
      */
     public function add($dn, $entry)
     {
-        $this->_debug("C: Add [dn: $dn]: ".print_r($entry, true));
+        $this->_debug("C: Add $dn: ".print_r($entry, true));
 
         $res = ldap_add($this->conn, $dn, $entry);
         if ($res === false) {
@@ -469,7 +464,7 @@
      */
     public function delete($dn)
     {
-        $this->_debug("C: Delete [dn: $dn]");
+        $this->_debug("C: Delete $dn");
 
         $res = ldap_delete($this->conn, $dn);
         if ($res === false) {
@@ -488,7 +483,7 @@
      */
     public function mod_replace($dn, $entry)
     {
-        $this->_debug("C: Replace [dn: $dn]: ".print_r($entry, true));
+        $this->_debug("C: Replace $dn: ".print_r($entry, true));
 
         if (!ldap_mod_replace($this->conn, $dn, $entry)) {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -506,7 +501,7 @@
      */
     public function mod_add($dn, $entry)
     {
-        $this->_debug("C: Add [dn: $dn]: ".print_r($entry, true));
+        $this->_debug("C: Add $dn: ".print_r($entry, true));
 
         if (!ldap_mod_add($this->conn, $dn, $entry)) {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -524,7 +519,7 @@
      */
     public function mod_del($dn, $entry)
     {
-        $this->_debug("C: Delete [dn: $dn]: ".print_r($entry, true));
+        $this->_debug("C: Delete $dn: ".print_r($entry, true));
 
         if (!ldap_mod_del($this->conn, $dn, $entry)) {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -542,7 +537,7 @@
      */
     public function rename($dn, $newrdn, $newparent = null, $deleteoldrdn = true)
     {
-        $this->_debug("C: Rename [dn: $dn] [dn: $newrdn]");
+        $this->_debug("C: Rename $dn to $newrdn");
 
         if (!ldap_rename($this->conn, $dn, $newrdn, $newparent, $deleteoldrdn)) {
             $this->_debug("S: ".ldap_error($this->conn));
@@ -562,7 +557,7 @@
     public function list_entries($dn, $filter, $attributes = array('dn'))
     {
         $list = array();
-        $this->_debug("C: List [dn: $dn] [{$filter}]");
+        $this->_debug("C: List $dn [{$filter}]");
 
         if ($result = ldap_list($this->conn, $dn, $filter, $attributes)) {
             $list = ldap_get_entries($this->conn, $result);
@@ -592,7 +587,7 @@
      */
     public function read_entries($dn, $filter, $attributes = null)
     {
-        $this->_debug("C: Read [dn: $dn] [{$filter}]");
+        $this->_debug("C: Read $dn [{$filter}]");
 
         if ($this->conn && $dn) {
             if (!$attributes)
@@ -610,7 +605,6 @@
 
         return false;
     }
-
 
     /**
      * Choose the right PHP function according to scope property
@@ -697,16 +691,22 @@
 
         return $entries;
     }
-    
+
     /**
      * Turn an LDAP entry into a regular PHP array with attributes as keys.
      *
      * @param array $entry Attributes array as retrieved from ldap_get_attributes() or ldap_get_entries()
+     *
      * @return array       Hash array with attributes as keys
      */
     public static function normalize_entry($entry)
     {
+        if (!isset($entry['count'])) {
+            return $entry;
+        }
+
         $rec = array();
+
         for ($i=0; $i < $entry['count']; $i++) {
             $attr = $entry[$i];
             if ($entry[$attr]['count'] == 1) {
@@ -737,7 +737,7 @@
         $sort_ctrl = array('oid' => "1.2.840.113556.1.4.473",  'value' => self::_sort_ber_encode((array)$sort));
         $vlv_ctrl  = array('oid' => "2.16.840.1.113730.3.4.9", 'value' => self::_vlv_ber_encode(($offset = ($list_page-1) * $page_size + 1), $page_size, $search), 'iscritical' => true);
 
-        $this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) . " ($sort[0]);"
+        $this->_debug("C: Set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) . " ($sort[0]);"
             . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset/$page_size; $search)");
 
         if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, array($sort_ctrl, $vlv_ctrl))) {
@@ -748,7 +748,6 @@
 
         return true;
     }
-
 
     /**
      * Returns unified attribute name (resolving aliases)
@@ -769,7 +768,6 @@
 
         return (isset($aliases[$name]) ? $aliases[$name] : $name) . $suffix;
     }
-
 
     /**
      * Quotes attribute value string
@@ -794,7 +792,6 @@
 
         return strtr($str, $replace);
     }
-
 
     /**
      * Prints debug info to the log
@@ -839,7 +836,7 @@
         $vlv_config = $this->_read_vlv_config();
 
         if ($vlv = $vlv_config[$base_dn]) {
-            $this->_debug("D: Found a VLV for base_dn: " . $base_dn);
+            $this->_debug("D: Found a VLV for $base_dn");
 
             if ($vlv['filter'] == strtolower($filter) || stripos($filter, '(&'.$vlv['filter'].'(') === 0) {
                 $this->_debug("D: Filter matches");
@@ -858,12 +855,11 @@
             }
         }
         else {
-            $this->_debug("D: No VLV for base dn " . $base_dn);
+            $this->_debug("D: No VLV for $base_dn");
         }
 
         return false;
     }
-
 
     /**
      * Return VLV indexes and searches including necessary configuration
@@ -883,7 +879,7 @@
         if (is_array($this->vlv_config)) {
             return $this->vlv_config;
         }
-        
+
         if ($this->cache && ($cached_config = $this->cache->get('vlvconfig'))) {
             $this->vlv_config = $cached_config;
             return $this->vlv_config;
@@ -926,50 +922,50 @@
         return $this->vlv_config;
     }
 
-
     /**
      * Generate BER encoded string for Virtual List View option
      *
      * @param integer List offset (first record)
      * @param integer Records per page
+     *
      * @return string BER encoded option value
      */
     private static function _vlv_ber_encode($offset, $rpp, $search = '')
     {
-        # this string is ber-encoded, php will prefix this value with:
-        # 04 (octet string) and 10 (length of 16 bytes)
-        # the code behind this string is broken down as follows:
-        # 30 = ber sequence with a length of 0e (14) bytes following
-        # 02 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0)
-        # 02 = type integer (in two's complement form) with 2 bytes following (afterCount):  01 18 (ie 25-1=24)
-        # a0 = type context-specific/constructed with a length of 06 (6) bytes following
-        # 02 = type integer with 2 bytes following (offset): 01 01 (ie 1)
-        # 02 = type integer with 2 bytes following (contentCount):  01 00
-        
-        # whith a search string present:
-        # 81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here)
-        # 81 indicates a user string is present where as a a0 indicates just a offset search
-        # 81 = type context-specific/constructed with a length of 06 (6) bytes following
-        
-        # the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the
-        # encoding of integer values (note: these values are in
-        # two-complement form so since offset will never be negative bit 8 of the
-        # leftmost octet should never by set to 1):
-        # 8.3.2: If the contents octets of an integer value encoding consist
-        # of more than one octet, then the bits of the first octet (rightmost) and bit 8
-        # of the second (to the left of first octet) octet:
-        # a) shall not all be ones; and
-        # b) shall not all be zero
-        
-        if ($search)
-        {
+        /*
+            this string is ber-encoded, php will prefix this value with:
+            04 (octet string) and 10 (length of 16 bytes)
+            the code behind this string is broken down as follows:
+            30 = ber sequence with a length of 0e (14) bytes following
+            02 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0)
+            02 = type integer (in two's complement form) with 2 bytes following (afterCount):  01 18 (ie 25-1=24)
+            a0 = type context-specific/constructed with a length of 06 (6) bytes following
+            02 = type integer with 2 bytes following (offset): 01 01 (ie 1)
+            02 = type integer with 2 bytes following (contentCount):  01 00
+
+            with a search string present:
+            81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here)
+            81 indicates a user string is present where as a a0 indicates just a offset search
+            81 = type context-specific/constructed with a length of 06 (6) bytes following
+
+            The following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the
+            encoding of integer values (note: these values are in
+            two-complement form so since offset will never be negative bit 8 of the
+            leftmost octet should never by set to 1):
+            8.3.2: If the contents octets of an integer value encoding consist
+            of more than one octet, then the bits of the first octet (rightmost)
+            and bit 8 of the second (to the left of first octet) octet:
+                a) shall not all be ones; and
+                b) shall not all be zero
+        */
+
+        if ($search) {
             $search = preg_replace('/[^-[:alpha:] ,.()0-9]+/', '', $search);
             $ber_val = self::_string2hex($search);
             $str = self::_ber_addseq($ber_val, '81');
         }
-        else
-        {
-            # construct the string from right to left
+        else {
+            // construct the string from right to left
             $str = "020100"; # contentCount
 
             $ber_val = self::_ber_encode_int($offset);  // returns encoded integer value in hex format
@@ -980,7 +976,7 @@
             // now compute length over $str
             $str = self::_ber_addseq($str, 'a0');
         }
-        
+
         // now tack on records per page
         $str = "020100" . self::_ber_addseq(self::_ber_encode_int($rpp-1), '02') . $str;
 
@@ -989,7 +985,6 @@
 
         return pack('H'.strlen($str), $str);
     }
-
 
     /**
      * create ber encoding for sort control
@@ -1003,8 +998,8 @@
         foreach (array_reverse((array)$sortcols) as $col) {
             $ber_val = self::_string2hex($col);
 
-            # 30 = ber sequence with a length of octet value
-            # 04 = octet string with a length of the ascii value
+            // 30 = ber sequence with a length of octet value
+            // 04 = octet string with a length of the ascii value
             $oct = self::_ber_addseq($ber_val, '04');
             $str = self::_ber_addseq($oct, '30') . $str;
         }
@@ -1051,8 +1046,9 @@
     private static function _string2hex($str)
     {
         $hex = '';
-        for ($i=0; $i < strlen($str); $i++)
+        for ($i=0; $i < strlen($str); $i++) {
             $hex .= dechex(ord($str[$i]));
+        }
         return $hex;
     }
 

--
Gitblit v1.9.1