Changeset 4844 in subversion


Ignore:
Timestamp:
Jun 9, 2011 3:43:22 PM (2 years ago)
Author:
thomasb
Message:

Implement LDAPv3 Virtual List View (VLV) for paged results listing

Location:
trunk/roundcubemail
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/roundcubemail/CHANGELOG

    r4840 r4844  
    22=========================== 
    33 
     4- Implement LDAPv3 Virtual List View (VLV) for paged results listing 
    45- Use 'address_template' config option when adding a new address block (#1487944) 
    56- Added addressbook advanced search 
  • trunk/roundcubemail/config/main.inc.php.dist

    r4823 r4844  
    535535  'filter'        => '(objectClass=inetOrgPerson)',      // used for basic listing (if not empty) and will be &'d with search queries. example: status=act 
    536536  'fuzzy_search'  => true,    // server allows wildcard search 
     537  'vlv'           => false,   // Enable Virtual List View to mor efficiently fetch paginated data (if server supports it) 
    537538  'sizelimit'     => '0',     // Enables you to limit the count of entries fetched. Setting this to 0 means no limit. 
    538539  'timelimit'     => '0',     // Sets the number of seconds how long is spend on the search. Setting this to 0 means no limit. 
  • trunk/roundcubemail/program/include/rcube_ldap.php

    r4834 r4844  
    5555    private $group_members = array(); 
    5656 
     57    private $vlv_active = false; 
     58    private $vlv_count = 0; 
     59 
    5760 
    5861    /** 
     
    418421        if ($this->ldap_result && $this->result->count > 0) 
    419422        { 
    420             if ($this->sort_col && $this->prop['scope'] !== 'base') 
     423            if ($this->sort_col && $this->prop['scope'] !== 'base' && !$this->vlv_active) 
    421424                ldap_sort($this->conn, $this->ldap_result, $this->sort_col); 
    422425 
    423             $start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first; 
     426            $start_row = $this->vlv_active ? 0 : $this->result->first; 
     427            $start_row = $subset < 0 ? $start_row + $this->page_size + $subset : $start_row; 
    424428            $last_row = $this->result->first + $this->page_size; 
    425429            $last_row = $subset != 0 ? $start_row + abs($subset) : $last_row; 
     
    548552        $count = 0; 
    549553        if ($this->conn && $this->ldap_result) { 
    550             $count = ldap_count_entries($this->conn, $this->ldap_result); 
     554            $count = $this->vlv_active ? $this->vlv_count : ldap_count_entries($this->conn, $this->ldap_result); 
    551555        } // end if 
    552556        elseif ($this->conn) { 
     
    556560                $this->filter = $this->prop['filter']; 
    557561            } // end if 
    558             $this->_exec_search(); 
     562            $this->_exec_search(true); 
    559563            if ($this->ldap_result) { 
    560564                $count = ldap_count_entries($this->conn, $this->ldap_result); 
     
    857861    * @access private 
    858862    */ 
    859     private function _exec_search() 
     863    private function _exec_search($all = false) 
    860864    { 
    861865        if ($this->ready) 
     
    865869 
    866870            $this->_debug("C: Search [".$filter."]"); 
     871 
     872            // when using VLV, we need to issue listing command first in order to get the full count 
     873            if (!$all && $function != 'ldap_read' && $this->prop['vlv']) { 
     874                $this->_exec_search(true); 
     875                $this->vlv_count = ldap_count_entries($this->conn, $this->ldap_result); 
     876                $this->vlv_active = $this->_vlv_set_controls(); 
     877            } 
    867878 
    868879            if ($this->ldap_result = @$function($this->conn, $this->base_dn, $filter, 
     
    879890 
    880891        return false; 
     892    } 
     893     
     894    /** 
     895     * Set server controls for Virtual List View (paginated listing) 
     896     */ 
     897    private function _vlv_set_controls() 
     898    { 
     899        $sort_ctrl = array('oid' => "1.2.840.113556.1.4.473",  'value' => $this->_sort_ber_encode(array($this->sort_col))); 
     900        $vlv_ctrl  = array('oid' => "2.16.840.1.113730.3.4.9", 'value' => $this->_vlv_ber_encode(($offset = ($this->list_page-1) * $this->page_size + 1), $this->page_size), 'iscritical' => true); 
     901 
     902        //$this->_debug("C: set controls sort=" . join(' ', unpack('H'.(strlen($sort_ctrl['value'])*2), $sort_ctrl['value'])) . " ({$this->sort_col});" 
     903        //    . " vlv=" . join(' ', (unpack('H'.(strlen($vlv_ctrl['value'])*2), $vlv_ctrl['value']))) . " ($offset)"); 
     904 
     905        if (!ldap_set_option($this->conn, LDAP_OPT_SERVER_CONTROLS, array($sort_ctrl, $vlv_ctrl))) { 
     906            $this->_debug("S: ".ldap_error($this->conn)); 
     907            $this->set_error(self::ERROR_SEARCH, 'vlvnotsupported'); 
     908            return false; 
     909        } 
     910 
     911        return true; 
    881912    } 
    882913 
     
    12291260        return $groups; 
    12301261    } 
     1262 
     1263 
     1264    /** 
     1265     * Generate BER encoded string for Virtual List View option 
     1266     * 
     1267     * @param integer List offset (first record) 
     1268     * @param integer Records per page 
     1269     * @return string BER encoded option value 
     1270     */ 
     1271    private function _vlv_ber_encode($offset, $rpp) 
     1272    { 
     1273        # this string is ber-encoded, php will prefix this value with: 
     1274        # 04 (octet string) and 10 (length of 16 bytes) 
     1275        # the code behind this string is broken down as follows: 
     1276        # 30 = ber sequence with a length of 0e (14) bytes following 
     1277        # 20 = type integer (in two's complement form) with 2 bytes following (beforeCount): 01 00 (ie 0) 
     1278        # 20 = type integer (in two's complement form) with 2 bytes following (afterCount):  01 18 (ie 25-1=24) 
     1279        # a0 = type context-specific/constructed with a length of 06 (6) bytes following 
     1280        # 20 = type integer with 2 bytes following (offset): 01 01 (ie 1) 
     1281        # 20 = type integer with 2 bytes following (contentCount):  01 00 
     1282        # the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the 
     1283        # encoding of integer values (note: these values are in 
     1284        # two-complement form so since offset will never be negative bit 8 of the 
     1285        # leftmost octet should never by set to 1): 
     1286        # 8.3.2: If the contents octets of an integer value encoding consist 
     1287        # of more than one octet, then the bits of the first octet (rightmost) and bit 8 
     1288        # of the second (to the left of first octet) octet: 
     1289        # a) shall not all be ones; and 
     1290        # b) shall not all be zero 
     1291 
     1292        # construct the string from right to left 
     1293        $str = "020100"; # contentCount 
     1294         
     1295        $ber_val = self::_ber_encode_int($offset);  // returns encoded integer value in hex format 
     1296 
     1297        // calculate octet length of $ber_val 
     1298        $str = self::_ber_addseq($ber_val, '02') . $str; 
     1299 
     1300        // now compute length over $str 
     1301        $str = self::_ber_addseq($str, 'a0'); 
     1302 
     1303        // now tack on records per page 
     1304        $str = sprintf("0201000201%02x", min(255, $rpp)-1) . $str; 
     1305 
     1306        // now tack on sequence identifier and length 
     1307        $str = self::_ber_addseq($str, '30'); 
     1308 
     1309        return pack('H'.strlen($str), $str); 
     1310    } 
     1311 
     1312 
     1313    /** 
     1314     * create ber encoding for sort control 
     1315     * 
     1316     * @pararm array List of cols to sort by 
     1317     * @return string BER encoded option value 
     1318     */ 
     1319    private function _sort_ber_encode($sortcols) 
     1320    { 
     1321        $str = ''; 
     1322        foreach (array_reverse((array)$sortcols) as $col) { 
     1323            $ber_val = self::_string2hex($col); 
     1324 
     1325            # 30 = ber sequence with a length of octet value 
     1326            # 04 = octet string with a length of the ascii value 
     1327            $oct = self::_ber_addseq($ber_val, '04'); 
     1328            $str = self::_ber_addseq($oct, '30') . $str; 
     1329        } 
     1330 
     1331        // now tack on sequence identifier and length 
     1332        $str = self::_ber_addseq($str, '30'); 
     1333 
     1334        return pack('H'.strlen($str), $str); 
     1335    } 
     1336 
     1337    /** 
     1338     * Add BER sequence with correct length and the given identifier 
     1339     */ 
     1340    private static function _ber_addseq($str, $identifier) 
     1341    { 
     1342        $len = sprintf("%x", strlen($str)/2); 
     1343        if (strlen($len) % 2 != 0) 
     1344            $len = '0'.$len; 
     1345 
     1346        return $identifier . $len . $str; 
     1347    } 
     1348 
     1349    /** 
     1350     * Returns BER encoded integer value in hex format 
     1351     */ 
     1352    private static function _ber_encode_int($offset) 
     1353    { 
     1354        $val = sprintf("%x", $offset); 
     1355        $prefix = ''; 
     1356         
     1357        // check if bit 8 of high byte is 1 
     1358        if (preg_match('/^[89abcdef]/', $val)) 
     1359            $prefix = '00'; 
     1360 
     1361        if (strlen($val)%2 != 0) 
     1362            $prefix .= '0'; 
     1363 
     1364        return $prefix . $val; 
     1365    } 
     1366 
     1367    /** 
     1368     * Returns ascii string encoded in hex 
     1369     */ 
     1370    private static function _string2hex($str) { 
     1371        $hex = ''; 
     1372        for ($i=0; $i < strlen($str); $i++) 
     1373            $hex .= dechex(ord($str[$i])); 
     1374        return $hex; 
     1375    } 
     1376 
    12311377} 
Note: See TracChangeset for help on using the changeset viewer.