2005-11-04 00:47:52 +01:00
< ? php
2006-06-13 06:30:16 +02:00
/**
* Addressbook - LDAP Backend
*
* @ link http :// www . egroupware . org
* @ author Cornelius Weiss < egw - AT - von - und - zu - weiss . de >
* @ author Lars Kneschke < l . kneschke - AT - metaways . de >
* @ author Ralf Becker < RalfBecker - AT - outdoor - training . de >
* @ package addressbook
* @ license http :// opensource . org / licenses / gpl - license . php GPL - GNU General Public License
* @ version $Id $
*/
define ( 'ADDRESSBOOK_ALL' , 0 );
define ( 'ADDRESSBOOK_ACCOUNTS' , 1 );
define ( 'ADDRESSBOOK_PERSONAL' , 2 );
define ( 'ADDRESSBOOK_GROUP' , 3 );
/**
* LDAP Backend for contacts , compatible with vars and parameters of eTemplate ' s so_sql .
* Maybe one day this becomes a generalized ldap storage object :- )
2008-05-07 15:08:58 +02:00
*
* All values used to construct filters need to run through ldap :: quote (),
2006-06-13 06:30:16 +02:00
* to be save against LDAP query injection !!!
*/
2008-05-10 14:02:49 +02:00
class addressbook_ldap
2006-06-13 06:30:16 +02:00
{
var $data ;
2008-05-07 15:08:58 +02:00
2006-07-08 02:36:23 +02:00
/**
* internal name of the id , gets mapped to uid
*
* @ var string
*/
var $contacts_id = 'id' ;
2006-06-13 06:30:16 +02:00
/**
* @ var string $accountName holds the accountname of the current user
*/
var $accountName ;
2006-04-26 08:00:31 +02:00
2005-11-04 00:47:52 +01:00
/**
2006-06-13 06:30:16 +02:00
* @ var object $ldapServerInfo holds the information about the current used ldap server
*/
var $ldapServerInfo ;
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* @ var int $ldapLimit how many rows to fetch from ldap server
*/
2008-05-07 15:08:58 +02:00
var $ldapLimit = 2000 ;
2006-06-13 06:30:16 +02:00
/**
* @ var string $personalContactsDN holds the base DN for the personal addressbooks
*/
var $personalContactsDN ;
2006-04-26 08:00:31 +02:00
2006-06-13 06:30:16 +02:00
/**
* @ var string $sharedContactsDN holds the base DN for the shared addressbooks
*/
var $sharedContactsDN ;
2008-05-07 15:08:58 +02:00
2013-05-22 19:22:20 +02:00
/**
* @ var string $accountContactsDN holds the base DN for accounts addressbook
*/
var $accountContactsDN ;
/**
* Filter used for accounts addressbook
* @ var string
*/
var $accountsFilter = '(objectclass=posixaccount)' ;
/**
* @ var string $allContactsDN holds the base DN of all addressbook
*/
var $allContactsDN ;
2013-06-01 19:55:33 +02:00
/**
* Attribute used for DN
*
* @ var string
*/
var $dn_attribute = 'uid' ;
2006-06-13 06:30:16 +02:00
/**
* @ var int $total holds the total count of found rows
*/
var $total ;
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* Charset used by eGW
2008-05-07 15:08:58 +02:00
*
2006-06-13 06:30:16 +02:00
* @ var string
*/
var $charset ;
2008-05-07 15:08:58 +02:00
2013-05-22 19:22:20 +02:00
/**
* LDAP searches only a limited set of attributes for performance reasons ,
* you NEED an index for that columns , ToDo : make it configurable
* minimum : $this -> columns_to_search = array ( 'n_family' , 'n_given' , 'org_name' , 'email' );
*/
var $search_attributes = array (
'n_family' , 'n_middle' , 'n_given' , 'org_name' , 'org_unit' ,
'adr_one_location' , 'adr_two_location' , 'note' ,
'email' , 'mozillasecondemail' , 'uidnumber' ,
);
2006-06-13 06:30:16 +02:00
/**
* maps between diverse ldap schema and the eGW internal names
2008-05-07 15:08:58 +02:00
*
2007-05-03 11:22:49 +02:00
* The ldap attribute names have to be lowercase !!!
2006-06-13 06:30:16 +02:00
*
* @ var array
*/
var $schema2egw = array (
2006-06-14 18:53:14 +02:00
'posixaccount' => array (
'account_id' => 'uidnumber' ,
2013-06-18 12:45:00 +02:00
// 'account_lid' => 'uid',
'id' => 'uid' ,
2013-05-22 19:22:20 +02:00
'shadowexpire' ,
2006-06-14 18:53:14 +02:00
),
2006-06-13 06:30:16 +02:00
'inetorgperson' => array (
2006-04-26 08:00:31 +02:00
'n_fn' => 'cn' ,
'n_given' => 'givenname' ,
'n_family' => 'sn' ,
'sound' => 'audio' ,
'note' => 'description' ,
'url' => 'labeleduri' ,
'org_name' => 'o' ,
'org_unit' => 'ou' ,
'title' => 'title' ,
2006-06-13 06:30:16 +02:00
'adr_one_street' => 'street' ,
'adr_one_locality' => 'l' ,
'adr_one_region' => 'st' ,
2006-04-26 08:00:31 +02:00
'adr_one_postalcode' => 'postalcode' ,
'tel_work' => 'telephonenumber' ,
'tel_home' => 'homephone' ,
'tel_fax' => 'facsimiletelephonenumber' ,
2006-06-13 06:30:16 +02:00
'tel_cell' => 'mobile' ,
'tel_pager' => 'pager' ,
2006-04-26 08:00:31 +02:00
'email' => 'mail' ,
'room' => 'roomnumber' ,
2006-06-13 06:30:16 +02:00
'jpegphoto' => 'jpegphoto' ,
'n_fileas' => 'displayname' ,
'label' => 'postaladdress' ,
2007-05-03 11:22:49 +02:00
'pubkey' => 'usersmimecertificate' ,
2012-02-29 10:58:15 +01:00
'uid' => 'entryuuid' ,
2006-06-13 06:30:16 +02:00
),
2006-04-26 08:00:31 +02:00
#displayName
#mozillaCustom1
#mozillaCustom2
#mozillaCustom3
#mozillaCustom4
#mozillaHomeUrl
#mozillaNickname
#mozillaUseHtmlMail
#nsAIMid
#postOfficeBox
2006-06-13 06:30:16 +02:00
'mozillaabpersonalpha' => array (
2006-06-23 02:06:27 +02:00
'adr_one_street2' => 'mozillaworkstreet2' ,
2006-07-07 00:59:28 +02:00
'adr_one_countryname' => 'c' , // 2 letter country code
2011-06-02 22:01:25 +02:00
'adr_one_countrycode' => 'c' , // 2 letter country code
2006-04-26 08:00:31 +02:00
'adr_two_street' => 'mozillahomestreet' ,
'adr_two_street2' => 'mozillahomestreet2' ,
'adr_two_locality' => 'mozillahomelocalityname' ,
'adr_two_region' => 'mozillahomestate' ,
'adr_two_postalcode' => 'mozillahomepostalcode' ,
'adr_two_countryname' => 'mozillahomecountryname' ,
2011-06-02 22:01:25 +02:00
'adr_two_countrycode' => 'mozillahomecountryname' ,
2006-04-26 08:00:31 +02:00
'email_home' => 'mozillasecondemail' ,
2006-06-13 06:30:16 +02:00
'url_home' => 'mozillahomeurl' ,
),
// similar to the newer mozillaAbPerson, but uses mozillaPostalAddress2 instead of mozillaStreet2
'mozillaorgperson' => array (
'adr_one_street2' => 'mozillapostaladdress2' ,
2011-09-13 12:36:03 +02:00
'adr_one_countrycode' => 'c' , // 2 letter country code
2006-07-07 00:59:28 +02:00
'adr_one_countryname' => 'co' , // human readable country name, must be after 'c' to take precedence on read!
2006-06-13 06:30:16 +02:00
'adr_two_street' => 'mozillahomestreet' ,
'adr_two_street2' => 'mozillahomepostaladdress2' ,
'adr_two_locality' => 'mozillahomelocalityname' ,
'adr_two_region' => 'mozillahomestate' ,
'adr_two_postalcode' => 'mozillahomepostalcode' ,
'adr_two_countryname' => 'mozillahomecountryname' ,
'email_home' => 'mozillasecondemail' ,
'url_home' => 'mozillahomeurl' ,
),
2006-04-26 08:00:31 +02:00
# managerName
# otherPostalAddress
# mailer
# anniversary
# spouseName
2008-05-07 15:08:58 +02:00
# companyPhone
2006-04-26 08:00:31 +02:00
# otherFacsimileTelephoneNumber
# radio
# telex
# tty
# categories(deprecated)
2006-06-13 06:30:16 +02:00
'evolutionperson' => array (
2006-04-26 08:00:31 +02:00
'bday' => 'birthdate' ,
'note' => 'note' ,
'tel_car' => 'carphone' ,
2006-06-13 06:30:16 +02:00
'tel_prefer' => 'primaryphone' ,
'cat_id' => 'category' , // special handling in _egw2evolutionperson method
2006-04-26 08:00:31 +02:00
'role' => 'businessrole' ,
2006-06-13 06:30:16 +02:00
'tel_assistent' => 'assistantphone' ,
2006-04-26 08:00:31 +02:00
'assistent' => 'assistantname' ,
'n_fileas' => 'fileas' ,
2006-06-13 06:30:16 +02:00
'tel_fax_home' => 'homefacsimiletelephonenumber' ,
'freebusy_uri' => 'freeBusyuri' ,
'calendar_uri' => 'calendaruri' ,
'tel_other' => 'otherphone' ,
2007-05-03 15:57:21 +02:00
'tel_cell_private' => 'callbackphone' , // not the best choice, but better then nothing
2006-06-13 06:30:16 +02:00
),
2008-05-07 15:08:58 +02:00
// additional schema can be added here, including special functions
2006-04-26 08:00:31 +02:00
/**
2006-06-13 06:30:16 +02:00
* still unsupported fields in LDAP :
* --------------------------------
* tz
* geo
2006-04-26 08:00:31 +02:00
*/
2006-06-13 06:30:16 +02:00
);
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* additional schema required by one of the above schema
*
* @ var array
*/
var $required_subs = array (
'inetorgperson' => array ( 'person' ),
);
2005-11-04 00:47:52 +01:00
/**
2006-06-13 06:30:16 +02:00
* array with the names of all ldap attributes of the above schema2egw array
2005-11-04 00:47:52 +01:00
*
2006-06-13 06:30:16 +02:00
* @ var array
*/
var $all_attributes = array ();
2008-05-07 15:08:58 +02:00
2012-11-19 09:23:09 +01:00
/**
* LDAP configuration
*
* @ var array values for keys " ldap_contact_context " , " ldap_host " , " ldap_context "
*/
private $ldap_config ;
2013-06-01 19:55:33 +02:00
/**
* LDAP connection
*
* @ var resource
*/
var $ds ;
2006-06-13 06:30:16 +02:00
/**
* constructor of the class
2012-11-19 09:23:09 +01:00
*
* @ param array $ldap_config = null default use from $GLOBALS [ 'egw_info' ][ 'server' ]
* @ param resource $ds = null ldap connection to use
2006-06-13 06:30:16 +02:00
*/
2012-11-19 09:23:09 +01:00
function __construct ( array $ldap_config = null , $ds = null )
2005-11-04 00:47:52 +01:00
{
2006-06-13 06:30:16 +02:00
//$this->db_data_cols = $this->stock_contact_fields + $this->non_contact_fields;
$this -> accountName = $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_lid' ];
2008-05-07 15:08:58 +02:00
2012-11-19 09:23:09 +01:00
if ( $ldap_config )
{
$this -> ldap_config = $ldap_config ;
}
else
{
$this -> ldap_config =& $GLOBALS [ 'egw_info' ][ 'server' ];
}
2013-05-23 18:52:42 +02:00
$this -> accountContactsDN = $this -> ldap_config [ 'ldap_context' ];
2013-05-22 19:22:20 +02:00
$this -> allContactsDN = $this -> ldap_config [ 'ldap_contact_context' ];
2013-06-01 19:55:33 +02:00
$this -> personalContactsDN = 'ou=personal,ou=contacts,' . $this -> allContactsDN ;
$this -> sharedContactsDN = 'ou=shared,ou=contacts,' . $this -> allContactsDN ;
2008-05-07 15:08:58 +02:00
2012-11-19 09:23:09 +01:00
if ( $ds )
{
$this -> ds = $ds ;
}
else
{
$this -> connect ();
}
$this -> ldapServerInfo = $GLOBALS [ 'egw' ] -> ldap -> getLDAPServerInfo ( $this -> ldap_config [ 'ldap_contact_host' ]);
2008-05-07 15:08:58 +02:00
2008-03-02 14:11:01 +01:00
foreach ( $this -> schema2egw as $schema => $attributes )
{
$this -> all_attributes = array_merge ( $this -> all_attributes , array_values ( $attributes ));
}
$this -> all_attributes = array_values ( array_unique ( $this -> all_attributes ));
2008-05-07 15:08:58 +02:00
2011-04-05 14:51:00 +02:00
$this -> charset = translation :: charset ();
2008-03-02 14:11:01 +01:00
}
2008-05-07 15:08:58 +02:00
2008-03-02 14:11:01 +01:00
/**
* __wakeup function gets called by php while unserializing the object to reconnect with the ldap server
*/
function __wakeup ()
{
$this -> connect ();
}
/**
* connect to LDAP server
2013-05-22 19:22:20 +02:00
*
* @ param boolean $admin = false true ( re - ) connect with admin not user credentials , eg . to modify accounts
2008-03-02 14:11:01 +01:00
*/
2013-05-22 19:22:20 +02:00
function connect ( $admin = false )
2008-03-02 14:11:01 +01:00
{
2013-05-22 19:22:20 +02:00
if ( $admin )
{
$this -> ds = $GLOBALS [ 'egw' ] -> ldap -> ldapConnect ();
}
2006-06-13 06:30:16 +02:00
// if ldap is NOT the contact repository, we only do accounts and need to use the account-data
2013-05-22 19:22:20 +02:00
elseif ( substr ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'contact_repository' ], - 4 ) != 'ldap' ) // not (ldap or sql-ldap)
2006-06-13 06:30:16 +02:00
{
2012-11-19 09:23:09 +01:00
$this -> ldap_config [ 'ldap_contact_host' ] = $this -> ldap_config [ 'ldap_host' ];
2013-06-01 19:55:33 +02:00
$this -> allContactsDN = $this -> ldap_config [ 'ldap_context' ];
2006-08-03 10:21:42 +02:00
$this -> ds = $GLOBALS [ 'egw' ] -> ldap -> ldapConnect ();
}
else
{
$this -> ds = $GLOBALS [ 'egw' ] -> ldap -> ldapConnect (
2012-11-19 09:23:09 +01:00
$this -> ldap_config [ 'ldap_contact_host' ],
2006-08-03 10:21:42 +02:00
$GLOBALS [ 'egw_info' ][ 'user' ][ 'account_dn' ],
$GLOBALS [ 'egw_info' ][ 'user' ][ 'passwd' ]
2008-03-02 14:13:23 +01:00
);
}
2006-06-13 06:30:16 +02:00
}
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* Returns the supported fields of this LDAP server ( based on the objectclasses it supports )
*
* @ return array with eGW contact field names
*/
function supported_fields ()
{
$fields = array (
'id' , 'tid' , 'owner' ,
'n_middle' , 'n_prefix' , 'n_suffix' , // stored in the cn
'created' , 'modified' , // automatic timestamps
'creator' , 'modifier' , // automatic for non accounts
'private' , // true for personal addressbooks, false otherwise
);
foreach ( $this -> schema2egw as $objectclass => $mapping )
{
if ( $this -> ldapServerInfo -> supportsObjectClass ( $objectclass ))
{
$fields = array_merge ( $fields , array_keys ( $mapping ));
2006-04-26 08:00:31 +02:00
}
}
2006-06-13 06:30:16 +02:00
return array_values ( array_unique ( $fields ));
}
2013-06-01 19:55:33 +02:00
/**
* Return LDAP filter for contact id
*
* @ param string $id
* @ return string
*/
protected function id_filter ( $id )
{
return '(|(entryUUID=' . ldap :: quote ( $id ) . ')(uid=' . ldap :: quote ( $id ) . '))' ;
}
/**
* Return LDAP filter for ( multiple ) contact ids
*
* @ param array | string $ids
* @ return string
*/
protected function ids_filter ( $ids )
{
if ( ! is_array ( $ids ) || count ( $ids ) == 1 )
{
return $this -> id_filter ( is_array ( $ids ) ? array_shift ( $ids ) : $ids );
}
$filter = array ();
foreach ( $ids as $id )
{
$filter [] = $this -> id_filter ( $id );
}
return '(|' . implode ( '' , $filter ) . ')' ;
}
2006-06-13 06:30:16 +02:00
/**
* reads contact data
*
2006-07-08 02:36:23 +02:00
* @ param string / array $contact_id contact_id or array with values for id or account_id
2006-06-13 06:30:16 +02:00
* @ return array / boolean data if row could be retrived else False
*/
function read ( $contact_id )
{
2012-02-29 10:58:15 +01:00
if ( is_array ( $contact_id ) && isset ( $contact_id [ 'account_id' ]) ||
! is_array ( $contact_id ) && substr ( $contact_id , 0 , 8 ) == 'account:' )
2006-07-08 02:36:23 +02:00
{
$filter = 'uidNumber=' . ( int )( is_array ( $contact_id ) ? $contact_id [ 'account_id' ] : substr ( $contact_id , 8 ));
}
else
{
2013-06-01 19:55:33 +02:00
if ( is_array ( $contact_id )) $contact_id = isset ( $contact_id [ 'id' ]) ? $contact_id [ 'id' ] : $contact_id [ 'uid' ];
$filter = $this -> id_filter ( $contact_id );
2006-07-08 02:36:23 +02:00
}
2013-06-01 19:55:33 +02:00
$rows = $this -> _searchLDAP ( $this -> allContactsDN ,
2006-07-08 02:36:23 +02:00
$filter , $this -> all_attributes , ADDRESSBOOK_ALL );
2006-06-13 06:30:16 +02:00
return $rows ? $rows [ 0 ] : false ;
2005-11-04 00:47:52 +01:00
}
2013-06-01 19:55:33 +02:00
/**
* Remove attributes we are not allowed to update
*
* @ param array $attributes
*/
function sanitize_update ( array & $ldapContact )
{
// never allow to change the uidNumber (account_id) on update, as it could be misused by eg. xmlrpc or syncml
unset ( $ldapContact [ 'uidnumber' ]);
unset ( $ldapContact [ 'entryuuid' ]); // not allowed to modify that, no need either
unset ( $ldapContact [ 'objectClass' ]);
}
2005-11-04 00:47:52 +01:00
/**
* saves the content of data to the db
*
* @ param array $keys if given $keys are copied to data before saveing => allows a save as
* @ return int 0 on success and errno != 0 else
*/
function save ( $keys = null )
{
2013-06-18 12:45:00 +02:00
//error_log(__METHOD__."(".array2string($keys).") this->data=".array2string($this->data));
2006-06-17 20:50:07 +02:00
if ( is_array ( $keys ))
{
$this -> data = is_array ( $this -> data ) ? array_merge ( $this -> data , $keys ) : $keys ;
}
2006-04-26 08:00:31 +02:00
$contactUID = '' ;
2008-05-07 15:08:58 +02:00
2006-06-17 20:50:07 +02:00
$data =& $this -> data ;
2006-04-26 08:00:31 +02:00
$isUpdate = false ;
$newObjectClasses = array ();
$ldapContact = array ();
2008-05-07 15:08:58 +02:00
2006-04-26 08:00:31 +02:00
// generate addressbook dn
2008-05-07 15:08:58 +02:00
if (( int ) $data [ 'owner' ])
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
// group address book
2006-06-13 23:53:00 +02:00
if ( ! ( $cn = strtolower ( $GLOBALS [ 'egw' ] -> accounts -> id2name (( int ) $data [ 'owner' ]))))
2006-06-13 06:30:16 +02:00
{
2006-09-13 06:49:53 +02:00
error_log ( 'Unknown owner' );
2006-06-13 06:30:16 +02:00
return true ;
2006-04-26 08:00:31 +02:00
}
2006-06-13 06:30:16 +02:00
$baseDN = 'cn=' . ldap :: quote ( $cn ) . ',' . ( $data [ 'owner' ] < 0 ? $this -> sharedContactsDN : $this -> personalContactsDN );
2008-05-07 15:08:58 +02:00
}
2006-07-08 02:36:23 +02:00
// only an admin or the user itself is allowed to change the data of an account
elseif ( $data [ 'account_id' ] && ( $GLOBALS [ 'egw_info' ][ 'user' ][ 'apps' ][ 'admin' ] ||
$data [ 'account_id' ] == $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]))
2006-06-13 06:30:16 +02:00
{
// account
2013-05-22 19:22:20 +02:00
$baseDN = $this -> accountContactsDN ;
2006-06-13 06:30:16 +02:00
$cn = false ;
// we need an admin connection
2013-06-01 19:55:33 +02:00
$this -> connect ( true );
2006-07-08 02:36:23 +02:00
// for sql-ldap we need to account_lid/uid as id, NOT the contact_id in id!
if ( $GLOBALS [ 'egw_info' ][ 'server' ][ 'contact_repository' ] == 'sql-ldap' )
{
$data [ 'id' ] = $GLOBALS [ 'egw' ] -> accounts -> id2name ( $data [ 'account_id' ]);
}
2006-06-13 06:30:16 +02:00
}
else
{
2006-09-13 06:49:53 +02:00
error_log ( " Permission denied, to write: data[owner]= $data[owner] , data[account_id]= $data[account_id] , account_id= " . $GLOBALS [ 'egw_info' ][ 'user' ][ 'account_id' ]);
return lang ( 'Permission denied !!!' ); // only admin or the user itself is allowd to write accounts!
2005-11-04 00:47:52 +01:00
}
2006-06-13 23:53:00 +02:00
// check if $baseDN exists. If not create it
if (( $err = $this -> _check_create_dn ( $baseDN )))
2006-06-13 06:30:16 +02:00
{
2006-06-13 23:53:00 +02:00
return $err ;
2006-04-26 08:00:31 +02:00
}
2006-06-13 06:30:16 +02:00
// check the existing objectclasses of an entry, none = array() for new ones
$oldObjectclasses = array ();
2013-06-01 19:55:33 +02:00
$attributes = array ( 'dn' , 'cn' , 'objectClass' , $this -> dn_attribute , 'mail' );
2006-06-13 23:53:00 +02:00
$contactUID = $this -> data [ $this -> contacts_id ];
2013-06-01 19:55:33 +02:00
if ( ! empty ( $contactUID ) &&
( $result = ldap_search ( $this -> ds , $base = $this -> allContactsDN , $filter = $this -> id_filter ( $contactUID ), $attributes )) &&
2006-06-13 23:53:00 +02:00
( $oldContactInfo = ldap_get_entries ( $this -> ds , $result )) && $oldContactInfo [ 'count' ])
2006-06-13 06:30:16 +02:00
{
2007-06-03 13:07:50 +02:00
unset ( $oldContactInfo [ 0 ][ 'objectclass' ][ 'count' ]);
2006-06-13 06:30:16 +02:00
foreach ( $oldContactInfo [ 0 ][ 'objectclass' ] as $objectclass )
{
$oldObjectclasses [] = strtolower ( $objectclass );
}
2006-04-26 08:00:31 +02:00
$isUpdate = true ;
}
2013-06-01 19:55:33 +02:00
if ( empty ( $contactUID ))
2006-06-13 06:30:16 +02:00
{
2013-06-01 19:55:33 +02:00
$ldapContact [ $this -> contacts_id ] = $this -> data [ $this -> contacts_id ] = $contactUID = md5 ( $GLOBALS [ 'egw' ] -> common -> randomstring ( 15 ));
2005-11-04 00:47:52 +01:00
}
2013-06-01 19:55:33 +02:00
//error_log(__METHOD__."() contactUID='$contactUID', isUpdate=".array2string($isUpdate).", oldContactInfo=".array2string($oldContactInfo));
2006-06-13 06:30:16 +02:00
// add for all supported objectclasses the objectclass and it's attributes
foreach ( $this -> schema2egw as $objectclass => $mapping )
{
2013-06-18 12:45:00 +02:00
if ( ! $this -> ldapServerInfo -> supportsObjectClass ( $objectclass )) continue ;
2008-05-07 15:08:58 +02:00
if ( ! in_array ( $objectclass , $oldObjectclasses ))
2006-06-13 06:30:16 +02:00
{
2006-06-13 23:53:00 +02:00
$ldapContact [ 'objectClass' ][] = $objectclass ;
2006-04-26 08:00:31 +02:00
}
2006-06-13 06:30:16 +02:00
if ( isset ( $this -> required_subs [ $objectclass ]))
{
foreach ( $this -> required_subs [ $objectclass ] as $sub )
{
2008-05-07 15:08:58 +02:00
if ( ! in_array ( $sub , $oldObjectclasses ))
2006-06-13 06:30:16 +02:00
{
2006-06-13 23:53:00 +02:00
$ldapContact [ 'objectClass' ][] = $sub ;
2006-04-26 08:00:31 +02:00
}
}
}
2008-05-07 15:08:58 +02:00
foreach ( $mapping as $egwFieldName => $ldapFieldName )
2006-06-13 06:30:16 +02:00
{
2013-06-18 12:45:00 +02:00
if ( is_int ( $egwFieldName )) continue ;
2008-05-07 15:08:58 +02:00
if ( ! empty ( $data [ $egwFieldName ]))
2006-06-13 06:30:16 +02:00
{
// dont convert the (binary) jpegPhoto!
2006-07-07 00:59:28 +02:00
$ldapContact [ $ldapFieldName ] = $ldapFieldName == 'jpegphoto' ? $data [ $egwFieldName ] :
2011-04-05 14:51:00 +02:00
translation :: convert ( trim ( $data [ $egwFieldName ]), $this -> charset , 'utf-8' );
2008-05-07 15:08:58 +02:00
}
elseif ( $isUpdate && isset ( $data [ $egwFieldName ]))
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$ldapContact [ $ldapFieldName ] = array ();
}
2013-06-18 12:45:00 +02:00
//error_log(__METHOD__."() ".__LINE__." objectclass=$objectclass, data['$egwFieldName']=".array2string($data[$egwFieldName])." --> ldapContact['$ldapFieldName']=".array2string($ldapContact[$ldapFieldName]));
2006-04-26 08:00:31 +02:00
}
2006-06-13 06:30:16 +02:00
// handling of special attributes, like cat_id in evolutionPerson
$egw2objectclass = '_egw2' . $objectclass ;
if ( method_exists ( $this , $egw2objectclass ))
{
$this -> $egw2objectclass ( $ldapContact , $data , $isUpdate );
2006-04-26 08:00:31 +02:00
}
}
2013-06-01 19:55:33 +02:00
if ( $isUpdate )
2006-06-13 06:30:16 +02:00
{
2006-09-16 11:59:29 +02:00
// make sure multiple email-addresses in the mail attribute "survive"
if ( isset ( $ldapContact [ 'mail' ]) && $oldContactInfo [ 0 ][ 'mail' ][ 'count' ] > 1 )
{
$mail = $oldContactInfo [ 0 ][ 'mail' ];
unset ( $mail [ 'count' ]);
$mail [ 0 ] = $ldapContact [ 'mail' ];
$ldapContact [ 'mail' ] = array_values ( array_unique ( $mail ));
}
2006-04-26 08:00:31 +02:00
// update entry
$dn = $oldContactInfo [ 0 ][ 'dn' ];
$needRecreation = false ;
// add missing objectclasses
2013-06-18 12:45:00 +02:00
if ( $ldapContact [ 'objectClass' ] && ( $missing = array_diff ( $ldapContact [ 'objectClass' ], $oldObjectclasses )))
2006-06-13 06:30:16 +02:00
{
2008-05-07 15:08:58 +02:00
if ( !@ ldap_mod_add ( $this -> ds , $dn , array ( 'objectClass' => $ldapContact [ 'objectClass' ])))
2006-06-13 06:30:16 +02:00
{
2008-05-07 15:08:58 +02:00
if ( in_array ( ldap_errno ( $this -> ds ), array ( 69 , 20 )))
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
// need to modify structural objectclass
$needRecreation = true ;
2013-06-18 12:45:00 +02:00
//error_log(__METHOD__."() ".__LINE__." could not add objectclasses ".array2string($missing)." --> need to recreate contact");
2008-05-07 15:08:58 +02:00
}
else
2006-06-13 06:30:16 +02:00
{
2006-06-13 23:53:00 +02:00
//echo "<p>ldap_mod_add($this->ds,'$dn',array(objectClass =>".print_r($ldapContact['objectClass'],true)."))</p>\n";
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . ' update of ' . $dn . ' failed errorcode: ' . ldap_errno ( $this -> ds ) . ' (' . ldap_error ( $this -> ds ) . ')' );
2006-06-13 23:53:00 +02:00
return $this -> _error ( __LINE__ );
2006-04-26 08:00:31 +02:00
}
}
}
2008-05-07 15:08:58 +02:00
2006-04-26 08:00:31 +02:00
// check if we need to rename the DN or need to recreate the contact
2013-06-01 19:55:33 +02:00
$newRDN = $this -> dn_attribute . '=' . ldap :: quote ( $ldapContact [ $this -> dn_attribute ]);
2006-04-26 08:00:31 +02:00
$newDN = $newRDN . ',' . $baseDN ;
2013-06-01 19:55:33 +02:00
if ( $needRecreation )
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$result = ldap_read ( $this -> ds , $dn , 'objectclass=*' );
$oldContact = ldap_get_entries ( $this -> ds , $result );
2013-06-18 12:45:00 +02:00
$oldContact = ldap :: result2array ( $oldContact [ 0 ]);
unset ( $oldContact [ 'dn' ]);
$newContact = $oldContact ;
2013-06-01 19:55:33 +02:00
$newContact [ $this -> dn_attribute ] = $ldapContact [ $this -> dn_attribute ];
2006-04-26 08:00:31 +02:00
2008-05-07 15:08:58 +02:00
if ( is_array ( $ldapContact [ 'objectClass' ]) && count ( $ldapContact [ 'objectClass' ]) > 0 )
2006-06-13 06:30:16 +02:00
{
2008-05-19 07:34:11 +02:00
$newContact [ 'objectclass' ] = array_unique ( array_map ( 'strtolower' , // objectclasses my have different case
array_merge ( $newContact [ 'objectclass' ], $ldapContact [ 'objectClass' ])));
2006-04-26 08:00:31 +02:00
}
2008-05-07 15:08:58 +02:00
if ( ! ldap_delete ( $this -> ds , $dn ))
2006-06-13 06:30:16 +02:00
{
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . ' delete of old ' . $dn . ' failed errorcode: ' . ldap_errno ( $this -> ds ) . ' (' . ldap_error ( $this -> ds ) . ')' );
2006-06-17 20:50:07 +02:00
return $this -> _error ( __LINE__ );
2006-06-13 23:53:00 +02:00
}
2008-05-07 15:08:58 +02:00
if ( !@ ldap_add ( $this -> ds , $newDN , $newContact ))
2006-06-13 06:30:16 +02:00
{
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . ' re-create contact as ' . $newDN . ' failed errorcode: ' . ldap_errno ( $this -> ds ) . ' (' . ldap_error ( $this -> ds ) . ') newContact=' . array2string ( $newContact ));
// if adding with new objectclass or dn fails, re-add deleted contact
@ ldap_add ( $this -> ds , $dn , $oldContact );
2006-06-13 23:53:00 +02:00
return $this -> _error ( __LINE__ );
2006-04-26 08:00:31 +02:00
}
$dn = $newDN ;
}
2013-06-01 19:55:33 +02:00
// try renaming entry if content of dn-attribute changed
if ( strtolower ( $dn ) != strtolower ( $newDN ) || $ldapContact [ $this -> dn_attribute ] != $oldContactInfo [ $this -> dn_attribute ])
{
if ( @ ldap_rename ( $this -> ds , $dn , $newRDN , null , true ))
{
$dn = $newDN ;
}
else
{
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . " ldap_rename of $dn to $newRDN failed! " . ldap_error ( $this -> ds ));
2013-06-01 19:55:33 +02:00
}
}
unset ( $ldapContact [ $this -> dn_attribute ]);
$this -> sanitize_update ( $ldapContact );
2006-04-26 08:00:31 +02:00
2008-05-07 15:08:58 +02:00
if ( !@ ldap_modify ( $this -> ds , $dn , $ldapContact ))
2006-06-13 06:30:16 +02:00
{
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . ' update of ' . $dn . ' failed errorcode: ' . ldap_errno ( $this -> ds ) . ' (' . ldap_error ( $this -> ds ) . ') ldapContact=' . array2string ( $ldapContact ));
2006-06-13 23:53:00 +02:00
return $this -> _error ( __LINE__ );
2006-04-26 08:00:31 +02:00
}
2006-06-13 23:53:00 +02:00
}
else
2006-06-13 06:30:16 +02:00
{
2013-06-01 19:55:33 +02:00
$dn = $this -> dn_attribute . '=' . ldap :: quote ( $ldapContact [ $this -> dn_attribute ]) . ',' . $baseDN ;
2012-04-29 17:41:38 +02:00
unset ( $ldapContact [ 'entryuuid' ]); // trying to write it, gives an error
2008-05-07 15:08:58 +02:00
2006-06-13 23:53:00 +02:00
if ( !@ ldap_add ( $this -> ds , $dn , $ldapContact ))
2006-06-13 06:30:16 +02:00
{
2013-06-18 12:45:00 +02:00
error_log ( __METHOD__ . '() ' . __LINE__ . ' add of ' . $dn . ' failed errorcode: ' . ldap_errno ( $this -> ds ) . ' (' . ldap_error ( $this -> ds ) . ') ldapContact=' . array2string ( $ldapContact ));
2006-06-13 23:53:00 +02:00
return $this -> _error ( __LINE__ );
2006-04-26 08:00:31 +02:00
}
}
2006-06-13 06:30:16 +02:00
return 0 ; // Ok, no error
2005-11-04 00:47:52 +01:00
}
/**
* deletes row representing keys in internal data or the supplied $keys if != null
*
* @ param array $keys if given array with col => value pairs to characterise the rows to delete
* @ return int affected rows , should be 1 if ok , 0 if an error
*/
function delete ( $keys = null )
{
// single entry
if ( $keys [ $this -> contacts_id ]) $keys = array ( 0 => $keys );
2008-05-07 15:08:58 +02:00
if ( ! is_array ( $keys ))
2006-06-13 06:30:16 +02:00
{
$keys = array ( $keys );
2006-04-26 08:00:31 +02:00
}
2005-11-04 00:47:52 +01:00
$ret = 0 ;
2006-04-26 08:00:31 +02:00
$attributes = array ( 'dn' );
2005-11-04 00:47:52 +01:00
foreach ( $keys as $entry )
{
2008-10-08 17:28:37 +02:00
$entry = ldap :: quote ( is_array ( $entry ) ? $entry [ 'id' ] : $entry );
2013-06-01 19:55:33 +02:00
if ( $result = ldap_search ( $this -> ds , $this -> allContactsDN ,
2008-05-07 15:08:58 +02:00
" (|(entryUUID= $entry )(uid= $entry )) " , $attributes ))
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$contactInfo = ldap_get_entries ( $this -> ds , $result );
2008-05-07 15:08:58 +02:00
if ( @ ldap_delete ( $this -> ds , $contactInfo [ 0 ][ 'dn' ]))
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$ret ++ ;
}
2005-11-04 00:47:52 +01:00
}
}
return $ret ;
}
/**
* searches db for rows matching searchcriteria
*
* '*' and '?' are replaced with sql - wildcards '%' and '_'
*
* @ param array / string $criteria array of key and data cols , OR a SQL query ( content for WHERE ), fully quoted ( ! )
* @ param boolean / string $only_keys = true True returns only keys , False returns all cols . comma seperated list of keys to return
* @ param string $order_by = '' fieldnames + { ASC | DESC } separated by colons ',' , can also contain a GROUP BY ( if it contains ORDER BY )
* @ param string / array $extra_cols = '' string or array of strings to be added to the SELECT , eg . " count(*) as num "
* @ param string $wildcard = '' appended befor and after each criteria
* @ param boolean $empty = false False = empty criteria are ignored in query , True = empty have to be empty in row
* @ param string $op = 'AND' defaults to 'AND' , can be set to 'OR' too , then criteria 's are OR' ed together
* @ param mixed $start = false if != false , return only maxmatch rows begining with start , or array ( $start , $num )
* @ param array $filter = null if set ( != null ) col - data pairs , to be and - ed ( ! ) into the query without wildcards
2008-05-07 15:08:58 +02:00
* @ param string $join = '' sql to do a join , added as is after the table - name , eg . " , table2 WHERE x=y " or
2005-11-04 00:47:52 +01:00
* " LEFT JOIN table2 ON (x=y) " , Note : there ' s no quoting done on $join !
* @ param boolean $need_full_no_count = false If true an unlimited query is run to determine the total number of rows , default false
* @ return array of matching rows ( the row is an array of the cols ) or False
*/
function & search ( $criteria , $only_keys = True , $order_by = '' , $extra_cols = '' , $wildcard = '' , $empty = False , $op = 'AND' , $start = false , $filter = null , $join = '' , $need_full_no_count = false )
{
2013-06-20 09:46:34 +02:00
error_log ( __METHOD__ . " ( " . array2string ( $criteria ) . " , " . array2string ( $only_keys ) . " , ' $order_by ', " . array2string ( $extra_cols ) . " , ' $wildcard ', ' $empty ', ' $op ', " . array2string ( $start ) . " , " . array2string ( $filter ) . " ) " );
2012-02-29 14:36:24 +01:00
if ( is_array ( $filter [ 'owner' ]))
{
if ( count ( $filter [ 'owner' ]) == 1 )
{
$filter [ 'owner' ] = array_shift ( $filter [ 'owner' ]);
}
else
{
// multiple addressbooks happens currently only via CardDAV or eSync
// currently we query all contacts and remove not matching ones (not the most efficient way to do it)
$owner_filter = $filter [ 'owner' ];
unset ( $filter [ 'owner' ]);
}
}
2008-05-07 15:08:58 +02:00
if (( int ) $filter [ 'owner' ])
2006-06-13 06:30:16 +02:00
{
if ( ! ( $accountName = $GLOBALS [ 'egw' ] -> accounts -> id2name ( $filter [ 'owner' ]))) return false ;
$searchDN = 'cn=' . ldap :: quote ( strtolower ( $accountName )) . ',' ;
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
if ( $filter [ 'owner' ] < 0 )
{
$searchDN .= $this -> sharedContactsDN ;
2006-04-26 08:00:31 +02:00
$addressbookType = ADDRESSBOOK_GROUP ;
}
2006-06-13 06:30:16 +02:00
else
{
$searchDN .= $this -> personalContactsDN ;
$addressbookType = ADDRESSBOOK_PERSONAL ;
}
2008-05-07 15:08:58 +02:00
}
2006-06-13 06:30:16 +02:00
elseif ( ! isset ( $filter [ 'owner' ]))
{
2013-05-22 19:22:20 +02:00
$searchDN = $this -> allContactsDN ;
2006-04-26 08:00:31 +02:00
$addressbookType = ADDRESSBOOK_ALL ;
}
2006-06-13 06:30:16 +02:00
else
{
2013-05-22 19:22:20 +02:00
$searchDN = $this -> accountContactsDN ;
2006-06-13 06:30:16 +02:00
$addressbookType = ADDRESSBOOK_ACCOUNTS ;
}
2006-04-26 08:00:31 +02:00
// create the search filter
2008-05-07 15:08:58 +02:00
switch ( $addressbookType )
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
case ADDRESSBOOK_ACCOUNTS :
2013-05-22 19:22:20 +02:00
$objectFilter = $this -> accountsFilter ;
2006-04-26 08:00:31 +02:00
break ;
default :
$objectFilter = '(objectclass=inetorgperson)' ;
break ;
}
2013-01-25 14:21:31 +01:00
// exclude expired accounts
//$shadowExpireNow = floor((time()+date('Z'))/86400);
//$objectFilter .= "(|(!(shadowExpire=*))(shadowExpire>=$shadowExpireNow))";
2013-06-20 09:46:34 +02:00
// shadowExpire>= does NOT work, as shadow schema only specifies integerMatch and not integerOrderingMatch :-(
2006-06-13 06:30:16 +02:00
2008-05-07 15:08:58 +02:00
$searchFilter = '' ;
if ( is_array ( $criteria ) && count ( $criteria ) > 0 )
2006-06-13 06:30:16 +02:00
{
$wildcard = $wildcard === '%' ? '*' : '' ;
2006-04-26 08:00:31 +02:00
$searchFilter = '' ;
2008-05-07 15:08:58 +02:00
foreach ( $criteria as $egwSearchKey => $searchValue )
2006-06-13 06:30:16 +02:00
{
2013-06-01 19:55:33 +02:00
if ( in_array ( $egwSearchKey , array ( 'id' , 'contact_id' )))
{
$searchFilter .= $this -> ids_filter ( $searchValue );
continue ;
}
2006-06-13 06:30:16 +02:00
foreach ( $this -> schema2egw as $mapping )
{
2008-05-07 15:08:58 +02:00
if (( $ldapSearchKey = $mapping [ $egwSearchKey ]))
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
$searchString = translation :: convert ( $searchValue , $this -> charset , 'utf-8' );
2006-06-13 06:30:16 +02:00
$searchFilter .= '(' . $ldapSearchKey . '=' . $wildcard . ldap :: quote ( $searchString ) . $wildcard . ')' ;
break ;
}
2006-04-26 08:00:31 +02:00
}
}
2008-05-07 15:08:58 +02:00
if ( $op == 'AND' )
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$searchFilter = " (& $searchFilter ) " ;
2008-05-07 15:08:58 +02:00
}
else
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$searchFilter = " (| $searchFilter ) " ;
}
}
2006-06-13 06:30:16 +02:00
$colFilter = $this -> _colFilter ( $filter );
$ldapFilter = " (& $objectFilter $searchFilter $colFilter ) " ;
if ( ! ( $rows = $this -> _searchLDAP ( $searchDN , $ldapFilter , $this -> all_attributes , $addressbookType )))
{
return $rows ;
}
2012-02-29 14:36:24 +01:00
// only return certain owners --> unset not matching ones
if ( $owner_filter )
{
foreach ( $rows as $k => $row )
{
if ( ! in_array ( $row [ 'owner' ], $owner_filter ))
{
unset ( $rows [ $k ]);
-- $this -> total ;
}
}
}
2006-06-13 06:30:16 +02:00
if ( $order_by )
{
$order = array ();
$sort = 'ASC' ;
foreach ( explode ( ',' , $order_by ) as $o )
{
2012-02-29 10:58:15 +01:00
if ( substr ( $o , 0 , 8 ) == 'contact_' ) $o = substr ( $o , 8 );
2006-06-13 06:30:16 +02:00
if ( substr ( $o , - 4 ) == ' ASC' )
{
$sort = 'ASC' ;
$order [] = substr ( $o , 0 , - 4 );
}
elseif ( substr ( $o , - 5 ) == ' DESC' )
{
$sort = 'DESC' ;
$order [] = substr ( $o , 0 , - 5 );
}
elseif ( $o )
{
$order [] = $o ;
}
}
$rows = ExecMethod2 ( 'phpgwapi.arrayfunctions.arfsort' , $rows , $order , $sort );
}
// if requested ($start !== false) return only limited resultset
if ( is_array ( $start ))
{
list ( $start , $offset ) = $start ;
}
2012-09-13 19:59:10 +02:00
if ( is_numeric ( $start ) && is_numeric ( $offset ) && $offset >= 0 )
2006-06-13 06:30:16 +02:00
{
return array_slice ( $rows , $start , $offset );
}
elseif ( is_numeric ( $start ))
{
return array_slice ( $rows , $start , $GLOBALS [ 'egw_info' ][ 'user' ][ 'preferences' ][ 'common' ][ 'maxmatchs' ]);
}
2006-04-26 08:00:31 +02:00
return $rows ;
}
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* Process so_sql like filters ( at the moment only a subset used by the addressbook UI
*
* @ param array $filter col - name => value pairs or just sql strings
* @ return string ldap filter
*/
function _colFilter ( $filter )
{
if ( ! is_array ( $filter )) return '' ;
$filters = '' ;
foreach ( $filter as $key => $value )
{
2007-05-21 08:54:55 +02:00
if ( $key != 'cat_id' && $key != 'account_id' && ! $value ) continue ;
2006-06-13 06:30:16 +02:00
switch (( string ) $key )
{
case 'owner' : // already handled
case 'tid' : // ignored
break ;
2008-05-07 15:08:58 +02:00
2007-05-21 08:54:55 +02:00
case 'account_id' :
if ( is_null ( $value ))
{
$filters .= '(!(uidNumber=*))' ;
}
elseif ( $value )
{
$filters .= '(uidNumber=' . ldap :: quote ( $value ) . ')' ;
2008-05-07 15:08:58 +02:00
2007-05-21 08:54:55 +02:00
}
break ;
2006-06-13 06:30:16 +02:00
case 'cat_id' :
2006-10-02 09:49:00 +02:00
if ( is_null ( $value ))
{
$filters .= '(!(category=*))' ;
}
elseif (( int ) $value )
2006-06-13 06:30:16 +02:00
{
2006-10-02 09:49:00 +02:00
if ( ! is_object ( $GLOBALS [ 'egw' ] -> categories ))
{
$GLOBALS [ 'egw' ] -> categories = CreateObject ( 'phpgwapi.categories' );
}
$cats = $GLOBALS [ 'egw' ] -> categories -> return_all_children (( int ) $value );
if ( count ( $cats ) > 1 ) $filters .= '(|' ;
foreach ( $cats as $cat )
{
2011-04-05 14:51:00 +02:00
$catName = translation :: convert (
2006-10-02 09:49:00 +02:00
$GLOBALS [ 'egw' ] -> categories -> id2name ( $cat ), $this -> charset , 'utf-8' );
$filters .= '(category=' . ldap :: quote ( $catName ) . ')' ;
}
if ( count ( $cats ) > 1 ) $filters .= ')' ;
2006-06-13 06:30:16 +02:00
}
break ;
2008-05-07 15:08:58 +02:00
2013-06-01 19:55:33 +02:00
case 'id' :
case 'contact_id' :
$filter .= $this -> ids_filter ( $value );
break ;
2006-06-13 06:30:16 +02:00
default :
if ( ! is_int ( $key ))
{
foreach ( $this -> schema2egw as $mapping )
{
if ( isset ( $mapping [ $key ]))
{
// todo: value = "!''"
$filters .= '(' . $mapping [ $key ] . '=' . ( $value === " !'' " ? '*' :
2011-04-05 14:51:00 +02:00
ldap :: quote ( translation :: convert ( $value , $this -> charset , 'utf-8' ))) . ')' ;
2006-06-13 06:30:16 +02:00
break ;
}
}
}
// filter for letter-search
2011-04-10 17:31:06 +02:00
elseif ( preg_match ( " /^([^ ]+) " . preg_quote ( $GLOBALS [ 'egw' ] -> db -> capabilities [ egw_db :: CAPABILITY_CASE_INSENSITIV_LIKE ]) . " '(.*)%' $ / " , $value , $matches ))
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
list (, $name , $value ) = $matches ;
if ( strpos ( $name , '.' ) !== false ) list (, $name ) = explode ( '.' , $name );
2006-06-13 06:30:16 +02:00
foreach ( $this -> schema2egw as $mapping )
{
2011-04-05 14:51:00 +02:00
if ( isset ( $mapping [ $name ]))
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
$filters .= '(' . $mapping [ $name ] . '=' . ldap :: quote (
translation :: convert ( $value , $this -> charset , 'utf-8' )) . '*)' ;
2006-06-13 06:30:16 +02:00
break ;
}
}
}
break ;
}
}
return $filters ;
}
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* Perform the actual ldap - search , retrieve and convert all entries
2008-05-07 15:08:58 +02:00
*
2006-06-13 06:30:16 +02:00
* Used be read and search
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-06-13 06:30:16 +02:00
* @ param string $_ldapContext
* @ param string $_filter
* @ param array $_attributes
* @ param int $_addressbooktype
* @ return array / boolean with eGW contacts or false on error
*/
2008-05-07 15:08:58 +02:00
function _searchLDAP ( $_ldapContext , $_filter , $_attributes , $_addressbooktype )
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$this -> total = 0 ;
2008-05-07 15:08:58 +02:00
2006-04-26 08:00:31 +02:00
$_attributes [] = 'entryUUID' ;
$_attributes [] = 'objectClass' ;
$_attributes [] = 'createTimestamp' ;
$_attributes [] = 'modifyTimestamp' ;
2006-06-13 06:30:16 +02:00
$_attributes [] = 'creatorsName' ;
$_attributes [] = 'modifiersName' ;
2006-04-26 08:00:31 +02:00
2013-06-20 09:46:34 +02:00
//error_log(__METHOD__."('$_ldapContext', '$_filter', ".array2string($_attributes).", $_addressbooktype)");
2013-05-22 19:22:20 +02:00
2013-06-20 09:46:34 +02:00
if ( $_addressbooktype == ADDRESSBOOK_ALL || $_ldapContext == $this -> allContactsDN )
2006-06-13 06:30:16 +02:00
{
2006-04-26 08:00:31 +02:00
$result = ldap_search ( $this -> ds , $_ldapContext , $_filter , $_attributes , 0 , $this -> ldapLimit );
2008-05-07 15:08:58 +02:00
}
else
2006-06-13 06:30:16 +02:00
{
2006-06-18 07:04:01 +02:00
$result = @ ldap_list ( $this -> ds , $_ldapContext , $_filter , $_attributes , 0 , $this -> ldapLimit );
2006-04-26 08:00:31 +02:00
}
2013-05-22 19:22:20 +02:00
if ( ! $result || ! $entries = ldap_get_entries ( $this -> ds , $result )) return array ();
2013-06-20 09:46:34 +02:00
//error_log(__METHOD__."('$_ldapContext', '$_filter', ".array2string($_attributes).", $_addressbooktype) result of $entries[count]");
2013-01-25 14:21:31 +01:00
2006-06-13 06:30:16 +02:00
$this -> total = $entries [ 'count' ];
2013-05-22 19:22:20 +02:00
foreach ( $entries as $i => $entry )
2006-06-13 06:30:16 +02:00
{
if ( ! is_int ( $i )) continue ; // eg. count
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
$contact = array (
'id' => $entry [ 'uid' ][ 0 ] ? $entry [ 'uid' ][ 0 ] : $entry [ 'entryuuid' ][ 0 ],
'tid' => 'n' , // the type id for the addressbook
);
2008-05-07 15:08:58 +02:00
foreach ( $entry [ 'objectclass' ] as $ii => $objectclass )
2006-06-13 06:30:16 +02:00
{
$objectclass = strtolower ( $objectclass );
if ( ! is_int ( $ii ) || ! isset ( $this -> schema2egw [ $objectclass ]))
{
continue ; // eg. count or unsupported objectclass
}
2008-05-07 15:08:58 +02:00
foreach ( $this -> schema2egw [ $objectclass ] as $egwFieldName => $ldapFieldName )
2006-06-13 06:30:16 +02:00
{
2013-05-22 19:22:20 +02:00
if ( ! empty ( $entry [ $ldapFieldName ][ 0 ]) && ! is_int ( $egwFieldName ) && ! isset ( $contact [ $egwFieldName ]))
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
$contact [ $egwFieldName ] = translation :: convert ( $entry [ $ldapFieldName ][ 0 ], 'utf-8' );
2006-04-26 08:00:31 +02:00
}
}
2006-06-13 06:30:16 +02:00
$objectclass2egw = '_' . $objectclass . '2egw' ;
if ( method_exists ( $this , $objectclass2egw ))
{
2013-05-22 19:22:20 +02:00
if (( $ret = $this -> $objectclass2egw ( $contact , $entry )) === false )
{
-- $this -> total ;
continue 2 ;
}
2006-04-26 08:00:31 +02:00
}
2006-06-13 06:30:16 +02:00
}
// read binary jpegphoto only for one result == call by read
if ( $this -> total == 1 && isset ( $entry [ 'jpegphoto' ][ 0 ]))
{
$bin = ldap_get_values_len ( $this -> ds , ldap_first_entry ( $this -> ds , $result ), 'jpegphoto' );
$contact [ 'jpegphoto' ] = $bin [ 0 ];
}
2007-05-22 17:55:58 +02:00
if ( preg_match ( '/cn=([^,]+),' . preg_quote ( $this -> personalContactsDN , '/' ) . '$/i' , $entry [ 'dn' ], $matches ))
2006-06-13 06:30:16 +02:00
{
// personal addressbook
$contact [ 'owner' ] = $GLOBALS [ 'egw' ] -> accounts -> name2id ( $matches [ 1 ], 'account_lid' , 'u' );
2008-05-07 15:08:58 +02:00
$contact [ 'private' ] = 0 ;
2006-06-13 06:30:16 +02:00
}
2007-05-22 17:55:58 +02:00
elseif ( preg_match ( '/cn=([^,]+),' . preg_quote ( $this -> sharedContactsDN , '/' ) . '$/i' , $entry [ 'dn' ], $matches ))
2006-06-13 06:30:16 +02:00
{
// group addressbook
$contact [ 'owner' ] = $GLOBALS [ 'egw' ] -> accounts -> name2id ( $matches [ 1 ], 'account_lid' , 'g' );
2006-07-08 02:36:23 +02:00
$contact [ 'private' ] = 0 ;
2006-06-13 06:30:16 +02:00
}
else
{
// accounts
2008-05-07 15:08:58 +02:00
$contact [ 'owner' ] = 0 ;
2006-07-08 02:36:23 +02:00
$contact [ 'private' ] = 0 ;
2006-06-13 06:30:16 +02:00
}
2008-02-08 12:14:47 +01:00
#########################################
## this piece of code could never have been working, as the call to $GLOBALS['egw']->accounts->name2id is wrong
#########################################
#foreach(array(
# 'creatorsname' => 'creator',
# 'modifiersname' => 'modifier',
#) as $ldapFieldName => $egwFieldName)
#{
# if (!empty($entry[$ldapFieldName][0]) && preg_match('/^cn=([^,]+),/',$entry[$ldapFieldName][0],$matches))
# {
# $contact[$egwFieldName] = $GLOBALS['egw']->accounts->name2id($matches[1],'u');
# }
#}
2006-06-13 06:30:16 +02:00
foreach ( array (
'createtimestamp' => 'created' ,
'modifytimestamp' => 'modified' ,
) as $ldapFieldName => $egwFieldName )
{
2008-05-07 15:08:58 +02:00
if ( ! empty ( $entry [ $ldapFieldName ][ 0 ]))
2006-06-13 06:30:16 +02:00
{
$contact [ $egwFieldName ] = $this -> _ldap2ts ( $entry [ $ldapFieldName ][ 0 ]);
2006-04-26 08:00:31 +02:00
}
}
2006-06-13 06:30:16 +02:00
$contacts [] = $contact ;
}
return $contacts ;
}
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
/**
* Creates a timestamp from the date returned by the ldap server
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-06-13 06:30:16 +02:00
* @ param string $date YYYYmmddHHiiss
* @ return int
*/
function _ldap2ts ( $date )
{
2008-05-07 15:08:58 +02:00
return gmmktime ( substr ( $date , 8 , 2 ), substr ( $date , 10 , 2 ), substr ( $date , 12 , 2 ),
2006-06-13 06:30:16 +02:00
substr ( $date , 4 , 2 ), substr ( $date , 6 , 2 ), substr ( $date , 0 , 4 ));
}
2008-05-07 15:08:58 +02:00
2006-06-13 23:53:00 +02:00
/**
* check if $baseDN exists . If not create it
*
2013-06-01 19:55:33 +02:00
* @ param string $baseDN cn = xxx , ou = yyy , ou = contacts , $this -> allContactsDN
2006-09-13 06:49:53 +02:00
* @ return boolean / string false on success or string with error - message
2006-06-13 23:53:00 +02:00
*/
function _check_create_dn ( $baseDN )
{
// check if $baseDN exists. If not create new one
if ( @ ldap_read ( $this -> ds , $baseDN , 'objectclass=*' ))
{
return false ;
}
2013-05-22 19:22:20 +02:00
//error_log(__METHOD__."('$baseDN') !ldap_read({$this->ds}, '$baseDN', 'objectclass=*') ldap_errno()=".ldap_errno($this->ds).', ldap_error()='.ldap_error($this->ds).get_class($this));
2008-05-07 15:08:58 +02:00
if ( ldap_errno ( $this -> ds ) != 32 || substr ( $baseDN , 0 , 3 ) != 'cn=' )
2006-06-13 23:53:00 +02:00
{
2013-05-22 19:22:20 +02:00
error_log ( __METHOD__ . " (' $baseDN ') baseDN does NOT exist and we cant/wont create it! ldap_errno()= " . ldap_errno ( $this -> ds ) . ', ldap_error()=' . ldap_error ( $this -> ds ));
2006-06-13 23:53:00 +02:00
return $this -> _error ( __LINE__ ); // baseDN does NOT exist and we cant/wont create it
}
// create a admin connection to add the needed DN
2009-06-08 18:21:14 +02:00
$adminLDAP = new ldap ;
2006-06-13 23:53:00 +02:00
$adminDS = $adminLDAP -> ldapConnect ();
2008-05-07 15:08:58 +02:00
2006-06-13 23:53:00 +02:00
list (, $ou ) = explode ( ',' , $baseDN );
foreach ( array (
2013-06-01 19:55:33 +02:00
'ou=contacts,' . $this -> allContactsDN ,
$ou . ',ou=contacts,' . $this -> allContactsDN ,
2006-06-13 23:53:00 +02:00
$baseDN ,
) as $dn )
{
if ( !@ ldap_read ( $this -> ds , $dn , 'objectclass=*' ) && ldap_errno ( $this -> ds ) == 32 )
{
// entry does not exist, lets try to create it
list ( $top ) = explode ( ',' , $dn );
list ( $var , $val ) = explode ( '=' , $top );
$data = array (
'objectClass' => $var == 'cn' ? 'organizationalRole' : 'organizationalUnit' ,
$var => $val ,
);
if ( !@ ldap_add ( $adminDS , $dn , $data ))
{
//echo "<p>ldap_add($adminDS,'$dn',".print_r($data,true).")</p>\n";
2012-04-29 17:41:38 +02:00
$err = lang ( " Can't create dn %1 " , $dn ) . ': ' . $this -> _error ( __LINE__ , $adminDS );
2006-06-13 23:53:00 +02:00
$adminLDAP -> ldapDisconnect ();
return $err ;
}
}
}
$adminLDAP -> ldapDisconnect ();
2008-05-07 15:08:58 +02:00
2006-06-13 23:53:00 +02:00
return false ;
}
2008-05-07 15:08:58 +02:00
2006-06-13 23:53:00 +02:00
/**
* error message for failed ldap operation
*
* @ param int $line
* @ return string
*/
function _error ( $line , $ds = null )
{
return ldap_error ( $ds ? $ds : $this -> ds ) . ': so_ldap: ' . $line ;
}
2006-06-13 06:30:16 +02:00
/**
* Special handling for mapping of eGW contact - data to the evolutionPerson objectclass
2008-05-07 15:08:58 +02:00
*
2006-06-13 06:30:16 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-06-13 06:30:16 +02:00
* @ param array & $ldapContact already copied fields according to the mapping
* @ param array $data eGW contact data
* @ param boolean $isUpdate
*/
function _egw2evolutionperson ( & $ldapContact , $data , $isUpdate )
{
2008-05-07 15:08:58 +02:00
if ( ! empty ( $data [ 'cat_id' ]))
2006-06-13 06:30:16 +02:00
{
2006-07-28 18:44:41 +02:00
$ldapContact [ 'category' ] = array ();
2008-05-07 15:08:58 +02:00
foreach ( is_array ( $data [ 'cat_id' ]) ? $data [ 'cat_id' ] : explode ( ',' , $data [ 'cat_id' ]) as $cat )
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
$ldapContact [ 'category' ][] = translation :: convert (
2006-08-12 18:56:57 +02:00
ExecMethod ( 'phpgwapi.categories.id2name' , $cat ), $this -> charset , 'utf-8' );
2006-06-13 06:30:16 +02:00
}
}
foreach ( array (
2006-08-22 10:14:15 +02:00
'postaladdress' => $data [ 'adr_one_street' ] . '$' . $data [ 'adr_one_locality' ] . ', ' . $data [ 'adr_one_region' ] . '$' . $data [ 'adr_one_postalcode' ] . '$$' . $data [ 'adr_one_countryname' ],
'homepostaladdress' => $data [ 'adr_two_street' ] . '$' . $data [ 'adr_two_locality' ] . ', ' . $data [ 'adr_two_region' ] . '$' . $data [ 'adr_two_postalcode' ] . '$$' . $data [ 'adr_two_countryname' ],
2006-06-13 06:30:16 +02:00
) as $attr => $value )
{
2008-05-07 15:08:58 +02:00
if ( $value != '$, $$$' )
2006-06-13 06:30:16 +02:00
{
2011-04-05 14:51:00 +02:00
$ldapContact [ $attr ] = translation :: convert ( $value , $this -> charset , 'utf-8' );
2008-05-07 15:08:58 +02:00
}
elseif ( $isUpdate )
2006-06-13 06:30:16 +02:00
{
$ldapContact [ $attr ] = array ();
}
}
2006-07-07 00:59:28 +02:00
// save the phone number of the primary contact and not the eGW internal field-name
if ( $data [ 'tel_prefer' ] && $data [ $data [ 'tel_prefer' ]])
{
$ldapContact [ 'primaryphone' ] = $data [ $data [ 'tel_prefer' ]];
}
elseif ( $isUpdate )
{
$ldapContact [ 'primaryphone' ] = array ();
}
2006-06-13 06:30:16 +02:00
}
/**
* Special handling for mapping data of the evolutionPerson objectclass to eGW contact
2008-05-07 15:08:58 +02:00
*
2006-06-13 06:30:16 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-06-13 06:30:16 +02:00
* @ param array & $contact already copied fields according to the mapping
* @ param array $data eGW contact data
*/
function _evolutionperson2egw ( & $contact , $data )
{
2006-07-31 20:10:43 +02:00
if ( $data [ 'category' ] && is_array ( $data [ 'category' ]))
2006-06-13 06:30:16 +02:00
{
2006-07-31 20:10:43 +02:00
$contact [ 'cat_id' ] = array ();
2008-05-07 15:08:58 +02:00
foreach ( $data [ 'category' ] as $iii => $cat )
2006-06-13 06:30:16 +02:00
{
if ( ! is_int ( $iii )) continue ;
2008-05-07 15:08:58 +02:00
2006-06-13 06:30:16 +02:00
$contact [ 'cat_id' ][] = ExecMethod ( 'phpgwapi.categories.name2id' , $cat );
}
if ( $contact [ 'cat_id' ]) $contact [ 'cat_id' ] = implode ( ',' , $contact [ 'cat_id' ]);
2006-04-26 08:00:31 +02:00
}
2006-07-07 00:59:28 +02:00
if ( $data [ 'primaryphone' ])
{
unset ( $contact [ 'tel_prefer' ]); // to not find itself
$contact [ 'tel_prefer' ] = array_search ( $data [ 'primaryphone' ][ 0 ], $contact );
}
2005-11-04 00:47:52 +01:00
}
2006-06-13 06:30:16 +02:00
/**
* Special handling for mapping data of the inetOrgPerson objectclass to eGW contact
2008-05-07 15:08:58 +02:00
*
2006-06-13 06:30:16 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-06-13 06:30:16 +02:00
* @ param array & $contact already copied fields according to the mapping
* @ param array $data eGW contact data
*/
function _inetorgperson2egw ( & $contact , $data )
{
if ( empty ( $data [ 'givenname' ][ 0 ]))
{
$parts = explode ( $data [ 'sn' ][ 0 ], $data [ 'cn' ][ 0 ]);
$contact [ 'n_prefix' ] = trim ( $parts [ 0 ]);
$contact [ 'n_suffix' ] = trim ( $parts [ 1 ]);
2008-05-07 15:08:58 +02:00
}
else
2006-06-13 06:30:16 +02:00
{
2007-05-22 17:55:58 +02:00
$parts = preg_split ( '/' . preg_quote ( $data [ 'givenname' ][ 0 ], '/' ) . '.*' . preg_quote ( $data [ 'sn' ][ 0 ], '/' ) . '/' , $data [ 'cn' ][ 0 ]);
2006-06-13 06:30:16 +02:00
$contact [ 'n_prefix' ] = trim ( $parts [ 0 ]);
$contact [ 'n_suffix' ] = trim ( $parts [ 1 ]);
2008-05-07 15:08:58 +02:00
if ( preg_match ( '/' . preg_quote ( $data [ 'givenname' ][ 0 ], '/' ) . ' (.*) ' . preg_quote ( $data [ 'sn' ][ 0 ], '/' ) . '/' , $data [ 'cn' ][ 0 ], $matches ))
2006-06-13 06:30:16 +02:00
{
$contact [ 'n_middle' ] = $matches [ 1 ];
}
}
}
2008-05-07 15:08:58 +02:00
2013-05-22 19:22:20 +02:00
/**
* Special handling for mapping data of posixAccount objectclass to eGW contact
*
* Please note : all regular fields are already copied !
*
* @ internal
* @ param array & $contact already copied fields according to the mapping
* @ param array $data eGW contact data
*/
function _posixaccount2egw ( & $contact , $data )
{
static $shadowExpireNow ;
if ( ! isset ( $shadowExpireNow )) $shadowExpireNow = floor (( time () - date ( 'Z' )) / 86400 );
// exclude expired or deactivated accounts
if ( isset ( $data [ 'shadowexpire' ]) && $data [ 'shadowexpire' ][ 0 ] <= $shadowExpireNow )
{
return false ;
}
}
2006-07-07 00:59:28 +02:00
/**
* Special handling for mapping data of the mozillaAbPersonAlpha objectclass to eGW contact
2008-05-07 15:08:58 +02:00
*
2006-07-07 00:59:28 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-07-07 00:59:28 +02:00
* @ param array & $contact already copied fields according to the mapping
* @ param array $data eGW contact data
*/
function _mozillaabpersonalpha2egw ( & $contact , $data )
{
if ( $data [ 'c' ])
{
$contact [ 'adr_one_countryname' ] = ExecMethod ( 'phpgwapi.country.get_full_name' , $data [ 'c' ][ 0 ]);
}
}
/**
* Special handling for mapping of eGW contact - data to the mozillaAbPersonAlpha objectclass
2008-05-07 15:08:58 +02:00
*
2006-07-07 00:59:28 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-07-07 00:59:28 +02:00
* @ param array & $ldapContact already copied fields according to the mapping
* @ param array $data eGW contact data
* @ param boolean $isUpdate
*/
function _egw2mozillaabpersonalpha ( & $ldapContact , $data , $isUpdate )
{
2011-06-02 22:01:25 +02:00
if ( $data [ 'adr_one_countrycode' ])
{
$ldapContact [ 'c' ] = $data [ 'adr_one_countrycode' ];
}
elseif ( $data [ 'adr_one_countryname' ])
2006-07-07 00:59:28 +02:00
{
$ldapContact [ 'c' ] = ExecMethod ( 'phpgwapi.country.country_code' , $data [ 'adr_one_countryname' ]);
2011-06-02 22:01:25 +02:00
if ( $ldapContact [ 'c' ] && strlen ( $ldapContact [ 'c' ]) > 2 ) // Bad countryname when "custom" selected!
{
$ldapContact [ 'c' ] = array (); // should return error...
}
2006-07-07 00:59:28 +02:00
}
elseif ( $isUpdate )
{
$ldapContact [ 'c' ] = array ();
2008-05-07 15:08:58 +02:00
}
2006-07-07 00:59:28 +02:00
}
2008-05-07 15:08:58 +02:00
2006-07-07 00:59:28 +02:00
/**
* Special handling for mapping data of the mozillaOrgPerson objectclass to eGW contact
2008-05-07 15:08:58 +02:00
*
2006-07-07 00:59:28 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-07-07 00:59:28 +02:00
* @ param array & $contact already copied fields according to the mapping
* @ param array $data eGW contact data
*/
function _mozillaorgperson2egw ( & $contact , $data )
{
2011-09-13 12:36:03 +02:00
// no special handling necessary, as it supports two distinct attributes: c, cn
2006-07-07 00:59:28 +02:00
}
/**
* Special handling for mapping of eGW contact - data to the mozillaOrgPerson objectclass
2008-05-07 15:08:58 +02:00
*
2006-07-07 00:59:28 +02:00
* Please note : all regular fields are already copied !
*
2008-05-07 15:08:58 +02:00
* @ internal
2006-07-07 00:59:28 +02:00
* @ param array & $ldapContact already copied fields according to the mapping
* @ param array $data eGW contact data
* @ param boolean $isUpdate
*/
function _egw2mozillaorgperson ( & $ldapContact , $data , $isUpdate )
{
2011-09-13 12:36:03 +02:00
if ( $data [ 'adr_one_countrycode' ])
{
$ldapContact [ 'c' ] = $data [ 'adr_one_countrycode' ];
if ( $isUpdate ) $ldapContact [ 'co' ] = array ();
}
elseif ( $data [ 'adr_one_countryname' ])
2006-07-07 00:59:28 +02:00
{
$ldapContact [ 'c' ] = ExecMethod ( 'phpgwapi.country.country_code' , $data [ 'adr_one_countryname' ]);
2011-09-13 12:36:03 +02:00
if ( $ldapContact [ 'c' ] && strlen ( $ldapContact [ 'c' ]) > 2 ) // Bad countryname when "custom" selected!
{
$ldapContact [ 'c' ] = array (); // should return error...
}
2006-07-07 00:59:28 +02:00
}
elseif ( $isUpdate )
{
2011-09-13 12:36:03 +02:00
$ldapContact [ 'c' ] = $ldapContact [ 'co' ] = array ();
2008-05-07 15:08:58 +02:00
}
2011-09-13 12:36:03 +02:00
//error_log(__METHOD__."() adr_one_countrycode='{$data['adr_one_countrycode']}', adr_one_countryname='{$data['adr_one_countryname']}' --> c=".array2string($ldapContact['c']).', co='.array2string($ldapContact['co']));
2006-07-07 00:59:28 +02:00
}
2006-12-11 08:35:49 +01:00
/**
* Change the ownership of contacts owned by a given account
*
* @ param int $account_id account - id of the old owner
* @ param int $new_owner account - id of the new owner
*/
function change_owner ( $account_id , $new_owner )
{
error_log ( " so_ldap::change_owner( $account_id , $new_owner ) not yet implemented " );
}
2005-11-04 00:47:52 +01:00
}