4 * assigns some standard variables to smarty templates
8 $smarty->assign('USER',$_SESSION['ldapab']['username']);
12 * Uses Username and Password from Session to initialize the LDAP handle
13 * If it fails it redirects to login.php
15 function ldap_login(){
17 if(!empty($_SESSION['ldapab']['username'])){
18 // existing session! Check if valid
19 if($_SESSION['ldapab']['browserid'] != auth_browseruid()){
20 //session hijacking detected
21 header('Location: login.php?username=');
24 } elseif ($conf['httpd_auth'] && !empty($_SERVER['PHP_AUTH_USER'])) {
25 // use HTTP auth if wanted and possible
26 $_SESSION['ldapab']['username'] = $_SERVER['PHP_AUTH_USER'];
27 $_SESSION['ldapab']['password'] = $_SERVER['PHP_AUTH_PW'];
28 } elseif (!empty($_COOKIE['ldapabauth'])) {
29 // check persistent cookie
30 $cookie = base64_decode($_COOKIE['ldapabauth']);
31 $cookie = x_Decrypt($cookie,get_cookie_secret());
32 list($u,$p) = unserialize($cookie);
33 $_SESSION['ldapab']['username'] = $u;
34 $_SESSION['ldapab']['password'] = $p;
37 if(empty($_SESSION['ldapab']) ||
38 !do_ldap_bind($_SESSION['ldapab']['username'],
39 $_SESSION['ldapab']['password'],
40 $_SESSION['ldapab']['binddn'])){
41 header('Location: login.php?username=');
47 * Creates a global LDAP connection handle called $LDAP_CON
49 function do_ldap_bind($user,$pass,$dn=""){
53 //create global connection to LDAP if necessary
55 if (!empty($conf['ldapurl'])){
56 $LDAP_CON = ldap_connect($conf['ldapurl']);
58 $LDAP_CON = ldap_connect($conf['ldapserver'],$conf['ldapport']);
61 die("couldn't connect to LDAP server");
65 if($conf['ldapv3']) ldap_set_option($LDAP_CON, LDAP_OPT_PROTOCOL_VERSION, 3);
68 //anonymous bind to lookup users
69 //blank binddn or blank bindpw will result in anonymous bind
70 if(!@ldap_bind($LDAP_CON,$conf['anonbinddn'],$conf['anonbindpw'])){
71 die("can not bind for user lookup");
74 //when no user was given stay connected anonymous
76 set_session('','','');
80 //get dn for given user
81 $filter = str_replace('%u',$user,$conf['userfilter']);
82 $sr = ldap_search($LDAP_CON, $conf['usertree'], $filter);;
83 $result = ldap_get_entries($LDAP_CON, $sr);
84 if($result['count'] != 1){
85 set_session('','','');
88 $dn = $result[0]['dn'];
92 if(@ldap_bind($LDAP_CON,$dn,$pass)){
93 //bind successful -> set up session
94 set_session($user,$pass,$dn);
97 //bind failed -> remove session
98 set_session('','','');
103 * Builds a pseudo UID from browser and IP data
105 * This is neither unique nor unfakable - still it adds some
106 * security. Using the first part of the IP makes sure
107 * proxy farms like AOLs are stil okay.
109 * @author Andreas Gohr <andi@splitbrain.org>
111 * @return string a MD5 sum of various browser headers
113 function auth_browseruid(){
115 if (empty($_SERVER['HTTP_USER_AGENT'])) { $_SERVER['HTTP_USER_AGENT']='USER_AGENT'; }
116 if (empty($_SERVER['HTTP_ACCEPT_ENCODING'])) { $_SERVER['HTTP_ACCEPT_ENCODING']='ACCEPT_ENCODING'; }
117 if (empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $_SERVER['HTTP_ACCEPT_LANGUAGE']='ACCEPT_LANGUAGE'; }
118 if (empty($_SERVER['HTTP_ACCEPT_CHARSET'])) { $_SERVER['HTTP_ACCEPT_CHARSET']='ACCEPT_CHARSET'; }
119 $uid .= $_SERVER['HTTP_USER_AGENT'];
120 $uid .= $_SERVER['HTTP_ACCEPT_ENCODING'];
121 $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
122 $uid .= $_SERVER['HTTP_ACCEPT_CHARSET'];
123 $uid .= substr($_SERVER['REMOTE_ADDR'],0,strpos($_SERVER['REMOTE_ADDR'],'.'));
129 * saves user data to Session and cookies
131 function set_session($user,$pass,$dn){
135 $_SESSION['ldapab']['username'] = $user;
136 $_SESSION['ldapab']['binddn'] = $dn;
137 $_SESSION['ldapab']['password'] = $pass;
138 $_SESSION['ldapab']['browserid'] = auth_browseruid();
140 // (re)set the persistent auth cookie
142 setcookie('ldapabauth','',time()+60*60*24*365);
143 }elseif(!empty($_REQUEST['remember'])){
144 $cookie = serialize(array($user,$pass));
145 $cookie = x_Encrypt($cookie,get_cookie_secret());
146 $cookie = base64_encode($cookie);
147 setcookie('ldapabauth',$cookie,time()+60*60*24*365);
152 * Creates a random string to encrypt persistent auth
153 * cookies; the string is stored inside the cache dir
155 function get_cookie_secret(){
156 $file = dirname(__FILE__).'/../cache/.htcookiesecret.php';
157 if(@file_exists($file)){
158 return md5(trim(file($file)));
161 $secret = '<?php #'.(rand()*time()).'?>';
162 if(!$fh = fopen($file,'w')) die("Couldn't write to $file");
163 if(fwrite($fh, $secret) === FALSE) die("Couldn't write to $file");
170 * binary safe function to get all search result data.
171 * It will use ldap_get_values_len() instead and build the array
172 * note: it's similar with the array returned by ldap_get_entries()
173 * except it has no "count" elements
175 * @author: Original code by Ovidiu Geaboc <ogeaboc@rdanet.com>
177 function ldap_get_binentries($conn,$srchRslt){
178 if(!@ldap_count_entries($conn,$srchRslt)){
181 $entry = ldap_first_entry($conn, $srchRslt);
184 $dn = ldap_get_dn($conn,$entry);
185 $attrs = ldap_get_attributes($conn, $entry);
186 for($j=0; $j<$attrs['count']; $j++) {
187 $vals = ldap_get_values_len($conn, $entry,$attrs[$j]);
188 for($k=0; $k<$vals['count']; $k++){
189 $data[$i][$attrs[$j]][$k]=$vals[$k];
194 }while ($entry = ldap_next_entry($conn, $entry));
201 * loads ldap names and their cleartext meanings from
202 * entries.conf file and returns it as hash
204 function namedentries($flip=false){
205 trigger_error('deprecated namedentries called',E_USER_WARNING);
209 * Creates an array for submission to ldap from websitedata
211 function prepare_ldap_entry($in){
217 if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['anniversary'])) $in['anniversary']='';
218 if(!preg_match('/\d\d\d\d-\d\d-\d\d/',$in['birthday'])) $in['birthday']='';
220 // we map all internal names to the configured LDAP attributes here
221 foreach($in as $key => $value){
223 // normal mapped field
224 $out[$FIELDS[$key]][] = $value;
225 }elseif($FIELDS["_$key"]){
226 // mapped multi field
227 if(is_array($value)){
228 $out[$FIELDS["_$key"]] = $value;
230 $out[$FIELDS["_$key"]][] = $value; //shouldn't happen, but to be sure
233 // no mapping found we ignore it
237 // special tag handling for Thunderbird
238 if($conf['tbtaghack'] && in_array('contactPerson',$OCLASSES)){
239 if($in['marker'][0]) $out['custom1'][] = $in['marker'][0];
240 if($in['marker'][1]) $out['custom2'][] = $in['marker'][1];
241 if($in['marker'][2]) $out['custom3'][] = $in['marker'][2];
242 if($in['marker'][3]) $out['custom4'][] = $in['marker'][3];
246 // add the Objectclasses
247 $out['objectclass'] = $OCLASSES;
249 return clear_array($out);
253 * remove empty element from arrays recursively
255 * @author Original by <xntx@msn.com>
257 function clear_array ( $a ) {
258 if ($a !== array()) {
260 foreach ( $a as $key => $value ) {
261 if (is_array($value)) {
262 if (clear_array($value) !== false) {
263 $b[$key] = clear_array ( $value );
265 } elseif ($value !== '') {
269 if ($b !== array()) {
280 * deletes an entryfrom ldap - optional with recursion
282 * @author Original by <gabriel@hrz.uni-marburg.de>
284 function ldap_full_delete($ds,$dn,$recursive=false){
285 if($recursive == false){
286 return(ldap_delete($ds,$dn));
288 //searching for sub entries
289 $sr=ldap_list($ds,$dn,"ObjectClass=*",array(""));
290 $info = ldap_get_entries($ds, $sr);
291 for($i=0;$i<$info['count'];$i++){
292 //deleting recursively sub entries
293 $result=myldap_delete($ds,$info[$i]['dn'],$recursive);
295 //return result code, if delete fails
299 return(ldap_delete($ds,$dn));
304 * Returns all User Accounts as assoziative array
306 function get_users(){
310 $sr = ldap_list($LDAP_CON,$conf['usertree'],"ObjectClass=inetOrgPerson");
311 $result = ldap_get_binentries($LDAP_CON, $sr);
314 foreach ($result as $entry){
315 if(!empty($entry['sn'][0])){
316 $users[$entry['dn']] = $entry['givenName'][0]." ".$entry['sn'][0];
324 * makes sure the given DN contains exactly one space
327 function normalize_dn($dn){
328 $dn = preg_replace('/,/',', ',$dn);
329 $dn = preg_replace('/,\s+/',', ',$dn);
334 * Merges the given classes with the existing ones
336 function ldap_store_objectclasses($dn,$classes){
340 $sr = ldap_search($LDAP_CON,$dn,"objectClass=*",array('objectClass'));
341 $result = ldap_get_binentries($LDAP_CON, $sr);
342 $set = $result[0]['objectClass'];
343 $set = array_unique_renumber(array_merge((array)$set,(array)$classes));
344 $add['objectClass'] = $set;
346 $r = @ldap_mod_replace($LDAP_CON,$dn,$add);
355 * Escape a string to be used in a LDAP filter
357 * Ported from Perl's Net::LDAP::Util escape_filter_value
359 * @author Andreas Gohr <andi@splitbrain.org>
361 function ldap_filterescape($string){
362 return preg_replace('/([\x00-\x1F\*\(\)\\\\])/e',
363 '"\\\\\".join("",unpack("H2","$1"))',
368 * Queries public and private addressbooks, combining the
371 * @todo This function should be used where ever possible, replacing
372 * lots of duplicate code
374 function ldap_queryabooks($filter,$types){
378 // make sure $types is an array
379 if(!is_array($types)){
380 $types = explode(',',$types);
381 $types = array_map('trim',$types);
389 // public addressbook
390 $sr = @ldap_list($LDAP_CON,$conf['publicbook'],
393 $result1 = ldap_get_binentries($LDAP_CON, $sr);
394 ldap_free_result($sr);
396 // private addressbook
397 if(!empty($_SESSION['ldapab']['binddn']) && $conf['privatebook']){
398 $sr = @ldap_list($LDAP_CON,$conf['privatebook'].
399 ','.$_SESSION['ldapab']['binddn'],
401 if(ldap_errno($LDAP_CON) != 32) tpl_ldaperror(); // ignore missing address book
402 $result2 = ldap_get_binentries($LDAP_CON, $sr);
405 // user account entries
406 if ($conf['displayusertree']) {
407 $sr = @ldap_list($LDAP_CON,$conf['usertree'],
410 $result3 = ldap_get_binentries($LDAP_CON, $sr);
411 ldap_free_result($sr);
416 // return merged results
417 return array_merge((array)$result1,(array)$result2,(array)$result3);
421 * Makes array unique and renumbers the entries
423 * @author <kay_rules@yahoo.com>
425 function array_unique_renumber($somearray){
426 $tmparr = array_unique($somearray);
428 foreach ($tmparr as $v) {
436 * Simple XOR encryption
438 * @author Dustin Schneider
439 * @link http://www.phpbuilder.com/tips/item.php?id=68
441 function x_Encrypt($string, $key){
442 for($i=0; $i<strlen($string); $i++){
443 for($j=0; $j<strlen($key); $j++){
444 $string[$i] = $string[$i]^$key[$j];
451 * Simple XOR decryption
453 * @author Dustin Schneider
454 * @link http://www.phpbuilder.com/tips/item.php?id=68
456 function x_Decrypt($string, $key){
457 for($i=0; $i<strlen($string); $i++){
458 for($j=0; $j<strlen($key); $j++){
459 $string[$i] = $key[$j]^$string[$i];
466 * Decodes UTF8 recursivly for the given array
470 function utf8_decode_array(&$array) {
471 trigger_error('deprecated utf8_decode_array called',E_USER_WARNING);
473 foreach (array_keys($array) as $key) {
474 if($key === 'dn') continue;
475 if($key === 'jpegPhoto') continue;
476 if (is_array($array[$key])) {
477 utf8_decode_array($array[$key]);
479 $array[$key] = utf8_decode($array[$key]);
485 * Encodes the given array to UTF8 recursively
489 function utf8_encode_array(&$array) {
490 trigger_error('deprecated utf8_encode_array called',E_USER_WARNING);
492 foreach (array_keys($array) as $key) {
493 if($key === 'dn') continue;
494 if($key === 'jpegPhoto') continue;
495 if (is_array($array[$key])) {
496 utf8_encode_array($array[$key]);
498 $array[$key] = utf8_encode($array[$key]);
504 * Returns all the fields used in the template
506 * Returned fields are already decoded to LDAP internals
508 function get_fields_from_template($tpl){
511 $tpl = $smarty->template_dir.'/'.$tpl;
512 $data = @file_get_contents($tpl);
514 preg_match_all('/\$entry\.(\w+)/',$data,$matches);
515 $matches = array_unique((array) $matches[1]);
517 foreach($matches as $f){
519 $return[] = $FIELDS[$f];
520 }elseif($FIELDS["_$f"]){
521 $return[] = $FIELDS["_$f"];