Changeset 4f9c833 in github


Ignore:
Timestamp:
May 7, 2008 6:16:00 PM (5 years ago)
Author:
svncommit <devs@…>
Branches:
master, HEAD, courier-fix, dev-browser-capabilities, pdo, release-0.6, release-0.7, release-0.8
Children:
1a659d7
Parents:
1854c45
Message:

/tmp/out

Files:
9 edited

Legend:

Unmodified
Added
Removed
  • CHANGELOG

    r32ac953 r4f9c833  
    11CHANGELOG RoundCube Webmail 
    22--------------------------- 
     3 
     42008/05/07 (davidke/richs) 
     5---------- 
     6- Completed LDAP address book support so it can now write to an LDAP server.  
     7- Expanded LDAP configuration options to support LDAP server writes. 
     8- Modified config/main.inc.php.dist: 
     9  New Option: $rcmail_config['use_SQL_address_book'] 
     10  Changed Option:  $rcmail_config['ldap_public']['Verisign'] 
    311 
    4122008/05/05 (alec) 
  • config/main.inc.php.dist

    r197601e r4f9c833  
    214214$rcmail_config['session_domain'] = ''; 
    215215 
    216 // in order to enable public ldap search, create a config array 
    217 // like the Verisign example below. if you would like to test,  
    218 // simply uncomment the Verisign example. 
     216// This indicates whether or not to use the SQL address book. 
     217// If set to false then it will look at using the first writable LDAP 
     218// address book as the primary address book and it will not display the 
     219// SQL address book in the 'Address Book' view. 
     220$rcmail_config['use_SQL_address_book'] = true; 
     221 
     222// In order to enable public ldap search, configure an array like the Verisign 
     223// example further below. if you would like to test, simply uncomment the example. 
     224// 
     225// If you are going to use LDAP for individual address books, you will need to  
     226// set 'user_specific' to true and use the variables to generate the appropriate DNs to access it. 
     227// 
     228// The recommended directory structure for LDAP is to store all the address book entries 
     229// under the users main entry, e.g.: 
     230// 
     231//  o=root 
     232//   ou=people 
     233//    uid=user@domain 
     234//      mail=contact@contactdomain 
     235//       
     236// So the base_dn would be uid=%fu,ou=people,o=root 
     237// The bind_dn would be the same as based_dn or some super user login. 
    219238/**  
    220239 * example config for Verisign directory 
     
    224243 *  'hosts'         => array('directory.verisign.com'), 
    225244 *  'port'          => 389, 
     245 *  'user_specific' => false,   // If true the base_dn, bind_dn and bind_pass default to the user's IMAP login. 
     246 *  // %fu - The full username provided, assumes the username is an email 
     247 *  //       address, uses the username_domain value if not an email address. 
     248 *  // %u  - The username prior to the '@'. 
     249 *  // %d  - The domain name after the '@'. 
    226250 *  'base_dn'       => '', 
    227251 *  'bind_dn'       => '', 
    228252 *  'bind_pass'     => '', 
     253 *  'writable'      => false,   // Indicates if we can write to the LDAP directory or not. 
     254 *  // If writable is true then these fields need to be populated: 
     255 *  // LDAP_Object_Classes, required_fields, LDAP_rdn 
     256 *  'LDAP_Object_Classes' => array("top", "inetOrgPerson"), // To create a new contact these are the object classes to specify (or any other classes you wish to use). 
     257 *  'required_fields'     => array("cn", "sn", "mail"),     // The required fields needed to build a new contact as required by the object classes (can include additional fields not required by the object classes). 
     258 *  'LDAP_rdn'      => 'mail', // The RDN field that is used for new entries, this field needs to be one of the search_fields, the base of base_dn is appended to the RDN to insert into the LDAP directory. 
    229259 *  'ldap_version'  => 3,       // using LDAPv3 
    230260 *  'search_fields' => array('mail', 'cn'),  // fields to search in 
     
    233263 *  'surname_field' => 'sn',    // this field represents the contact's last name 
    234264 *  'firstname_field' => 'gn',  // this field represents the contact's first name 
     265 *  'sort'          => 'cn',    // The field to sort the listing by. 
    235266 *  'scope'         => 'sub',   // search mode: sub|base|list 
    236267 *  'filter'        => '',      // used for basic listing (if not empty) and will be &'d with search queries. example: status=act 
  • program/include/main.inc

    r1854c45 r4f9c833  
    603603      $zebra_class = $c%2 ? 'even' : 'odd'; 
    604604 
    605       $table .= sprintf('<tr id="rcmrow%d" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]); 
     605      $table .= sprintf('<tr id="rcmrow%s" class="contact '.$zebra_class.'">'."\n", $row_data[$id_col]); 
    606606 
    607607      // format each col 
  • program/include/rcube_ldap.php

    r47124c22 r4f9c833  
    5757      if (preg_match('/^(.+)_field$/', $prop, $matches)) 
    5858        $this->fieldmap[$matches[1]] = $value; 
    59      
     59 
     60    $this->sort_col = $p["sort"]; 
     61 
    6062    $this->connect(); 
    6163  } 
     
    103105    { 
    104106      $this->ready = true; 
    105       if (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass'])) 
     107 
     108      if ($this->prop["user_specific"]) { 
     109        // User specific access, generate the proper values to use. 
     110        global $CONFIG, $RCMAIL; 
     111        if (empty($this->prop['bind_pass'])) { 
     112          // No password set, use the users. 
     113          $this->prop['bind_pass'] = $RCMAIL->decrypt_passwd($_SESSION["password"]); 
     114        } // end if 
     115 
     116        // Get the pieces needed for variable replacement. 
     117        // See if the logged in username has an "@" in it. 
     118        if (is_bool(strstr($_SESSION["username"], "@"))) { 
     119          // It does not, use the global default. 
     120          $fu = $_SESSION["username"]."@".$CONFIG["username_domain"]; 
     121          $u = $_SESSION["username"]; 
     122          $d = $CONFIG["username_domain"]; 
     123        } // end if 
     124        else { 
     125          // It does. 
     126          $fu = $_SESSION["username"]; 
     127          // Get the pieces needed for username and domain. 
     128          list($u, $d) = explode("@", $_SESSION["username"]); 
     129        } # end else 
     130 
     131        // Replace the bind_dn variables. 
     132        $bind_dn = str_replace(array("%fu", "%u", "%d"), 
     133                               array($fu, $u, $d), 
     134                               $this->prop['bind_dn']); 
     135        $this->prop['bind_dn'] = $bind_dn; 
     136        // Replace the base_dn variables. 
     137        $base_dn = str_replace(array("%fu", "%u", "%d"), 
     138                               array($fu, $u, $d), 
     139                               $this->prop['base_dn']); 
     140        $this->prop['base_dn'] = $base_dn; 
     141 
     142        $this->ready = $this->bind($this->prop['bind_dn'], $this->prop['bind_pass']); 
     143      } // end if 
     144      elseif (!empty($this->prop['bind_dn']) && !empty($this->prop['bind_pass'])) 
    106145        $this->ready = $this->bind($this->prop['bind_dn'], $this->prop['bind_pass']); 
    107146    } 
    108147    else 
    109148      raise_error(array('type' => 'ldap', 'message' => "Could not connect to any LDAP server, tried $host:{$this->prop[port]} last"), true); 
     149 
     150    // See if the directory is writeable. 
     151    if ($this->prop['writable']) { 
     152      $this->readonly = false; 
     153    } // end if 
     154 
    110155  } 
    111156 
     
    212257   * 
    213258   * @param  array  List of cols to show 
    214    * @param  int    Only return this number of records (not implemented) 
     259   * @param  int    Only return this number of records 
    215260   * @return array  Indexed list of contact records, each a hash array 
    216261   */ 
     
    236281      if ($this->sort_col && $this->prop['scope'] !== "base") 
    237282        @ldap_sort($this->conn, $this->ldap_result, $this->sort_col); 
    238          
     283 
     284      $start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first; 
     285      $last_row = $this->result->first + $this->page_size; 
     286      $last_row = $subset != 0 ? $start_row + abs($subset) : $last_row; 
     287 
    239288      $entries = ldap_get_entries($this->conn, $this->ldap_result); 
    240       for ($i = $this->result->first; $i < min($entries['count'], $this->result->first + $this->page_size); $i++) 
     289      for ($i = $start_row; $i < min($entries['count'], $last_row); $i++) 
    241290        $this->result->add($this->_ldap2result($entries[$i])); 
    242291    } 
     
    314363  { 
    315364    $count = 0; 
    316     if ($this->conn && $this->ldap_result) 
     365    if ($this->conn && $this->ldap_result) { 
    317366      $count = ldap_count_entries($this->conn, $this->ldap_result); 
     367    } // end if 
     368    elseif ($this->conn) { 
     369      // We have a connection but no result set, attempt to get one. 
     370      if (empty($this->filter)) { 
     371        // The filter is not set, set it. 
     372        $this->filter = $this->prop['filter']; 
     373      } // end if 
     374      $this->_exec_search(); 
     375      if ($this->ldap_result) { 
     376        $count = ldap_count_entries($this->conn, $this->ldap_result); 
     377      } // end if 
     378    } // end else 
    318379 
    319380    return new rcube_result_set($count, ($this->list_page-1) * $this->page_size); 
     
    349410      if ($entry && ($rec = ldap_get_attributes($this->conn, $entry))) 
    350411      { 
     412        // Add in the dn for the entry. 
     413        $rec["dn"] = base64_decode($dn); 
    351414        $res = $this->_ldap2result($rec); 
    352415        $this->result = new rcube_result_set(1); 
     
    363426   * 
    364427   * @param array    Hash array with save data 
    365    * @return boolean The create record ID on success, False on error 
     428   * @return encoded record ID on success, False on error 
    366429   */ 
    367430  function insert($save_cols) 
    368431  { 
    369     // TODO 
    370     return false; 
     432    // Map out the column names to their LDAP ones to build the new entry. 
     433    $newentry = array(); 
     434    $newentry["objectClass"] = $this->prop["LDAP_Object_Classes"]; 
     435    foreach ($save_cols as $col => $val) { 
     436      $fld = ""; 
     437      $fld = $this->_map_field($col); 
     438      if ($fld != "") { 
     439        // The field does exist, add it to the entry. 
     440        $newentry[$fld] = $val; 
     441      } // end if 
     442    } // end foreach 
     443 
     444    // Verify that the required fields are set. 
     445    // We know that the email address is required as a default of rcube, so 
     446    // we will default its value into any unfilled required fields. 
     447    foreach ($this->prop["required_fields"] as $fld) { 
     448      if (!isset($newentry[$fld])) { 
     449        $newentry[$fld] = $newentry[$this->_map_field("email")]; 
     450      } // end if 
     451    } // end foreach 
     452 
     453    // Build the new entries DN. 
     454    $dn = $this->prop["LDAP_rdn"]."=".$newentry[$this->prop["LDAP_rdn"]].",".$this->prop['base_dn']; 
     455    $res = @ldap_add($this->conn, $dn, $newentry); 
     456    if ($res === FALSE) { 
     457      return false; 
     458    } // end if 
     459 
     460    return base64_encode($dn); 
    371461  } 
    372462   
     
    381471  function update($id, $save_cols) 
    382472  { 
    383     // TODO     
    384     return false; 
     473    $record = $this->get_record($id, true); 
     474    $result = $this->get_result(); 
     475    $record = $result->first(); 
     476 
     477    $newdata = array(); 
     478    $replacedata = array(); 
     479    $deletedata = array(); 
     480    foreach ($save_cols as $col => $val) { 
     481      $fld = ""; 
     482      $fld = $this->_map_field($col); 
     483      if ($fld != "") { 
     484        // The field does exist compare it to the ldap record. 
     485        if ($record[$col] != $val) { 
     486          // Changed, but find out how. 
     487          if (!isset($record[$col])) { 
     488            // Field was not set prior, need to add it. 
     489            $newdata[$fld] = $val; 
     490          } // end if 
     491          elseif ($val == "") { 
     492            // Field supplied is empty, verify that it is not required. 
     493            if (!in_array($fld, $this->prop["required_fields"])) { 
     494              // It is not, safe to clear. 
     495              $deletedata[$fld] = $record[$col]; 
     496            } // end if 
     497          } // end elseif 
     498          else { 
     499            // The data was modified, save it out. 
     500            $replacedata[$fld] = $val; 
     501          } // end else 
     502        } // end if 
     503      } // end if 
     504    } // end foreach 
     505 
     506    // Update the entry as required. 
     507    $dn = base64_decode($id); 
     508    if (!empty($deletedata)) { 
     509      // Delete the fields. 
     510      $res = @ldap_mod_del($this->conn, $dn, $deletedata); 
     511      if ($res === FALSE) { 
     512        return false; 
     513      } // end if 
     514    } // end if 
     515 
     516    if (!empty($replacedata)) { 
     517      // Replace the fields. 
     518      $res = @ldap_mod_replace($this->conn, $dn, $replacedata); 
     519      if ($res === FALSE) { 
     520        return false; 
     521      } // end if 
     522    } // end if 
     523 
     524    if (!empty($newdata)) { 
     525      // Add the fields. 
     526      $res = @ldap_mod_add($this->conn, $dn, $newdata); 
     527      if ($res === FALSE) { 
     528        return false; 
     529      } // end if 
     530    } // end if 
     531 
     532    return true; 
    385533  } 
    386534   
     
    394542  function delete($ids) 
    395543  { 
    396     // TODO 
    397     return false; 
     544    if (!is_array($ids)) { 
     545      // Not an array, break apart the encoded DNs. 
     546      $dns = explode(",", $ids); 
     547    } // end if 
     548 
     549    foreach ($dns as $id) { 
     550      $dn = base64_decode($id); 
     551      // Delete the record. 
     552      $res = @ldap_delete($this->conn, $dn); 
     553      if ($res === FALSE) { 
     554        return false; 
     555      } // end if 
     556    } // end foreach 
     557 
     558    return true; 
    398559  } 
    399560 
  • program/js/app.js

    rac23884 r4f9c833  
    24772477 
    24782478    // send request to server 
    2479     this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_from='+(this.env.action ? this.env.action : '')+qs); 
     2479    this.http_post('delete', '_cid='+urlencode(a_cids.join(','))+'&_source='+urlencode(this.env.source)+'&_from='+(this.env.action ? this.env.action : '')+qs); 
    24802480    return true; 
    24812481    }; 
  • program/steps/addressbook/delete.inc

    r8d07583 r4f9c833  
    2020*/ 
    2121 
    22 if (($cid = get_input_value('_cid', RCUBE_INPUT_POST)) && preg_match('/^[0-9]+(,[0-9]+)*$/', $cid)) 
     22if (($cid = get_input_value('_cid', RCUBE_INPUT_POST)) && 
     23    (preg_match('/^[0-9]+(,[0-9]+)*$/', $cid) || 
     24     preg_match('/^[a-zA-Z0-9=]+(,[a-zA-Z0-9=]+)*$/', $cid)) 
     25   ) 
    2326  { 
    2427  $deleted = $CONTACTS->delete($cid); 
  • program/steps/addressbook/edit.inc

    r197601e r4f9c833  
    9292    $hiddenfields = new html_hiddenfield(array('name' => '_task', 'value' => $RCMAIL->task)); 
    9393    $hiddenfields->add(array('name' => '_action', 'value' => 'save', 'source' => get_input_value('_source', RCUBE_INPUT_GPC))); 
     94    $hiddenfields->add(array('name' => '_source', 'value' => get_input_value('_source', RCUBE_INPUT_GPC))); 
    9495     
    9596    if (($result = $CONTACTS->get_result()) && ($record = $result->first())) 
  • program/steps/addressbook/func.inc

    r47124c22 r4f9c833  
    2323if (($source = get_input_value('_source', RCUBE_INPUT_GPC)) && isset($CONFIG['ldap_public'][$source])) 
    2424  $CONTACTS = new rcube_ldap($CONFIG['ldap_public'][$source]); 
    25 else 
    26   $CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']); 
     25else { 
     26    if (!$CONFIG["use_SQL_address_book"]) { 
     27    // Get the first LDAP address book. 
     28    $source = key((array)$CONFIG['ldap_public']); 
     29    $prop = current((array)$CONFIG['ldap_public']); 
     30    $CONTACTS = new rcube_ldap($prop); 
     31  } // end if 
     32  else { 
     33    $CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']); 
     34  } // end else 
     35} // end else 
    2736 
    2837$CONTACTS->set_pagesize($CONFIG['pagesize']); 
     
    4352 
    4453// add list of address sources to client env 
    45 $js_list = array("0" => array('id' => 0, 'readonly' => false)); 
     54$js_list = array(); 
     55if ($CONFIG["use_SQL_address_book"]) { 
     56  // We are using the DB address book, add it. 
     57  $js_list = array("0" => array('id' => 0, 'readonly' => false)); 
     58} // end if 
    4659foreach ((array)$CONFIG['ldap_public'] as $id => $prop) 
    47   $js_list[$id] = array('id' => $id, 'readonly' => !$prop['writeable']); 
     60  $js_list[$id] = array('id' => $id, 'readonly' => !$prop['writable']); 
    4861$OUTPUT->set_env('address_sources', $js_list); 
    4962 
     
    6780  // allow the following attributes to be added to the <ul> tag 
    6881  $out = '<ul' . create_attrib_string($attrib, array('style', 'class', 'id')) . ">\n"; 
    69   $out .= sprintf($line_templ, 
    70     'rcmli'.$local_id, 
    71     !$current ? 'selected' : '', 
    72     Q(rcmail_url('list', array('_source' => 0))), 
    73     JS_OBJECT_NAME, 
    74     $local_id, 
    75     JS_OBJECT_NAME, 
    76     $local_id, 
    77     JS_OBJECT_NAME, 
    78     $local_id, 
    79     JS_OBJECT_NAME, 
    80     $local_id, 
    81     rcube_label('personaladrbook')); 
     82  if ($CONFIG["use_SQL_address_book"]) { 
     83    $out .= sprintf($line_templ, 
     84      'rcmli'.$local_id, 
     85      !$current ? 'selected' : '', 
     86      Q(rcmail_url('list', array('_source' => 0))), 
     87      JS_OBJECT_NAME, 
     88      $local_id, 
     89      JS_OBJECT_NAME, 
     90      $local_id, 
     91      JS_OBJECT_NAME, 
     92      $local_id, 
     93      JS_OBJECT_NAME, 
     94      $local_id, 
     95      rcube_label('personaladrbook')); 
     96  } // end if 
     97  else { 
     98    // DB address book not used, see if a source is set, if not use the 
     99    // first LDAP directory. 
     100    if (!$current) { 
     101      $current = key((array)$CONFIG['ldap_public']); 
     102    } // end if 
     103  } // end else 
    82104   
    83105  foreach ((array)$CONFIG['ldap_public'] as $id => $prop) 
  • program/steps/mail/addcontact.inc

    r47124c22 r4f9c833  
    2424if (!empty($_POST['_address'])) 
    2525{ 
    26   $CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']); 
     26  $CONTACTS = array(); 
     27  if (!$CONFIG["use_SQL_address_book"]) { 
     28    // Use the first writable LDAP address book. 
     29    foreach ($CONFIG["ldap_public"] as $id => $prop) { 
     30      if ($prop["writable"]) { 
     31        $CONTACTS = new rcube_ldap($prop); 
     32        break; 
     33      } // end if 
     34    } // end foreach 
     35  } // end if 
     36  else { 
     37    $CONTACTS = new rcube_contacts($DB, $_SESSION['user_id']); 
     38  } // end else 
    2739  $contact_arr = $IMAP->decode_address_list(get_input_value('_address', RCUBE_INPUT_POST, true), 1, false); 
    2840   
Note: See TracChangeset for help on using the changeset viewer.