LDAPab is a webbased address book for small companies. It features a public
address book which is writable for all company staff and a personal address
-book for each staff member. LDAPab require an already setup LDAP server to
+book for each staff member. LDAPab requires an already setup LDAP server to
authenticate users.
=== REQUIREMENTS ===
Copy the contents of the .tgz file to a directory below your webserver document
root. Make sure the webserver is able to write to the cache directory which is
-used for the template engines cache files. A 'chmod 777 cache' will do.
+used for the template engine's cache files. A 'chmod 777 cache' will do.
Open the config.php file in your favourite editor and edit the options
according to your needs. Some knowlege about LDAP may come in handy.
+If HTTP auth is wanted have a look at the provided _htaccess file, edit it as
+needed and rename it back to .htaccess
+
=== SETUP THE LDAP SERVER ===
The following only describes what to do for open-ldap 2.0! If you use any
--- /dev/null
+#
+# Example .htaccess to use with mod_authldap
+#
+
+AuthName "LDAPab"
+AuthType basic
+
+AuthLDAPURL "ldap://ldap/ou=people,o=cosmocode,c=de?uid?one"
+
+### LDAP Bind information
+#AuthLDAPBindDN cn=NonAnon,o=cosmocode,c=de
+#AuthLDAPBindPassword secret
+
+### For LDAP group authentication
+#AuthLDAPGroupAttribute memberUid
+#AuthLDAPGroupAttributeIsDN off
+#require group cn=Users,ou=Groups,o=cosmocode,c=de
+
+### Authorize users individually
+#require user myusername
+
+### Authorize any authenticated user
+require valid-user
+
+### Require SSL for access
+#SSLRequireSSL
+
// Which LDAP Server to use?
$conf[ldapserver] = 'ldap';
+ // Which LDAP Port Server to use? (389 is standard, 636 for ssl)
+ $conf[ldapport] = 389;
+
// What is the root dn on this Server?
$conf[ldaprootdn] = 'o=cosmocode, c=de';
// How to match users? %u is replaced by the given login
$conf[userfilter] = '(&(uid=%u)(objectClass=posixAccount))';
+ // Use these values to bind to the ldap directory when not logged in (leave blank for anonymous bind)
+ $conf[anonbinddn] = '';
+ $conf[anonbindpw] = '';
+
// Which language to use (see lang directory)
$conf[lang] = 'de';
// Where to store private contacts (relative to $conf[usertree])
$conf[privatebook] = 'ou=contacts';
+ // Should the additional schema ldapab.schema be used? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf[extended] = 0;
+
+ // Should we use some parts of the openxchange.schema? (0|1)
+ // Note: openxchange and extended are currently exclusive, do not use both at the same time!
+ $conf[openxchange] = 1;
+
+ // Should we try to login using the username and password provided by httpd? (0|1)
+ $conf[httpd_auth] = 1;
+
// Should the additional schema ldapab.schema be used? (0|1)
$conf[extended] = 1;
// Force recompilation of smarty templates?
$conf[smartycompile] = 0;
+
?>
tpl_std();
tpl_orgs();
tpl_markers();
+ tpl_categories();
+ tpl_timezone();
+ tpl_country();
//display templates
if($_REQUEST[mode]=='vcf'){
$entry = $smarty->get_template_vars('entry');
if($conf[extended]){
ldap_store_objectclasses($dn,array('inetOrgPerson','contactPerson'));
}
+ // in openxchange mode we have to make sure the right classes are set
+ if ($conf[openxchange]){
+ ldap_store_objectclasses($dn,array('inetOrgPerson','OXUserObject'));
+ }
//modify entry (touches only our attributes)
foreach (array_keys($entries) as $key){
if($key == 'dn'){
* If it fails it redirects to login.php
*/
function ldap_login(){
+ global $conf;
if(!empty($_SESSION[ldapab][username])){
//existing session! Check if valid
if($_COOKIE[ldapabconid] != $_SESSION[ldapab][conid]){
header('Location: login.php?username=');
exit;
}
+ } elseif ($conf[httpd_auth] && !empty($_SERVER[PHP_AUTH_USER])) {
+ $_SESSION[ldapab][username] = $_SERVER[PHP_AUTH_USER];
+ $_SESSION[ldapab][password] = $_SERVER[PHP_AUTH_PW];
}
if(!do_ldap_bind($_SESSION[ldapab][username],
//create global connection to LDAP if nessessary
if(!$LDAP_CON){
- $LDAP_CON = ldap_connect($conf[ldapserver]);
+ $LDAP_CON = ldap_connect($conf[ldapserver],$conf[ldapport]);
if(!$LDAP_CON){
die("couldn't connect to LDAP server");
}
if(empty($dn)){
//anonymous bind to lookup users
- if(!ldap_bind($LDAP_CON)){
+ //blank binddn or blank bindpw will result in anonymous bind
+ if(!ldap_bind($LDAP_CON,$conf[anonbinddn],$conf[anonbindpw])){
die("can not bind anonymously");
}
if($conf[extended]){
$entries[anniversary] = 'anniversary';
}
+ if($conf[openxchange]){
+ $entries[mailDomain] = 'domain';
+ $entries[userCountry] = 'country';
+ $entries[birthDay] = 'birthday';
+ $entries[IPPhone] = 'ipphone';
+ $entries[OXUserCategories] = 'categories';
+ $entries[OXUserInstantMessenger] = 'instantmessenger';
+ $entries[OXTimeZone] = 'timezone';
+ $entries[OXUserPosition] = 'position';
+ $entries[relClientCert] = 'certificate';
+ }
if($flip){
$entries = array_reverse($entries);
if($conf[extended]){
$out[objectclass][] = 'contactPerson';
}
+ if($conf[openxchange]){
+ $out[objectclass][] = 'OXUserObject';
+ }
utf8_encode_array($out);
//prepare templates
tpl_std();
tpl_markers();
+ tpl_categories();
+ tpl_timezone();
+ tpl_country();
$smarty->assign('list',$list);
$smarty->assign('filter',$_REQUEST['filter']);
$smarty->assign('marker',$_REQUEST['marker']);
$search = $_REQUEST['search'];
$org = $_REQUEST['org'];
$marker = $_REQUEST['marker'];
+ $categories = $_REQUEST['categories'];
$_SESSION[ldapab][filter] = $filter;
if(empty($filter)) $filter='a';
if(!empty($marker)){
$marker = utf8_encode($marker);
$ldapfilter = "(&(objectClass=contactPerson)(marker=$marker))";
+ }elseif(!empty($categories)){
+ $categories = utf8_encode($categories);
+ $ldapfilter = "(&(objectClass=OXUserObject)(OXUserCategories=$categories))";
}elseif(!empty($search)){
$search = trim($search);
$words=preg_split('/\s+/',$search);
$lang[ldapab] = 'The LDAP address book';
-$lang[name] = 'Name';
+$lang[name] = 'Last Name';
$lang[givenname] = 'First Name';
$lang[title] = 'Title';
$lang[organization] = 'Company';
$lang[err_noentries] = 'No entries';
$lang[err_ldap] = 'The LDAP server returned the following errors';
+
+$lang[openxchange] = 'Xchange Information';
+$lang[moreopenxchange] = 'Xchange Information Cont\'d';
+$lang[instantmessenger] = 'Inst Msg';
+$lang[categories] = 'Categories';
+$lang[birthday] = 'Birthday';
+$lang[domain] = 'Mail Domain';
+$lang[country] = 'Country';
+$lang[certificate] = 'x.509 Cert';
+$lang[timezone] = 'Time Zone';
+$lang[position] = 'Position';
+$lang[ipphone] = 'IP Phone';
?>
//handle marker special in extended mode
$out['marker'] = $in['marker'];
}
+ if ($conf[openxchange]){
+ //handle categories special in openxchange mode
+ $out['categories'] = $in['OXUserCategories'];
+ }
//decode array
utf8_decode_array($out);
$smarty->assign('orgs',$orgs);
}
+/**
+ * assigns all categories to the template
+ */
+function tpl_categories(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $categories = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("OXUserCategories"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("OXUserCategories"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['OXUserCategories'])){
+ foreach($entry['OXUserCategories'] as $category){
+ array_push($categories, $category);
+ }
+ }
+ }
+ }
+ $categories = array_unique($categories);
+ sort($categories,SORT_STRING);
+
+ utf8_decode_array($categories);
+ $smarty->assign('categories',$categories);
+}
+
+/**
+ * assigns all timezones to the template
+ */
+function tpl_timezone(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $timezone = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("OXTimeZone"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("OXTimeZone"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['OXTimeZone'])){
+ foreach($entry['OXTimeZone'] as $tz){
+ array_push($timezone, $tz);
+ }
+ }
+ }
+ }
+ $timezone = array_unique($timezone);
+ sort($timezone,SORT_STRING);
+
+ utf8_decode_array($timezone);
+ $smarty->assign('timezone',$timezone);
+}
+
+/**
+ * assigns all countries to the template
+ */
+function tpl_country(){
+ global $conf;
+ global $LDAP_CON;
+ global $smarty;
+
+ if(!$conf[openxchange]) return;
+
+ $country = array();
+
+ $sr = ldap_list($LDAP_CON,$conf[publicbook],"ObjectClass=OXUserObject",array("userCountry"));
+ $result1 = ldap_get_binentries($LDAP_CON, $sr);
+ //check users private addressbook
+ if(!empty($_SESSION[ldapab][binddn])){
+ $sr = @ldap_list($LDAP_CON,
+ $conf[privatebook].','.$_SESSION[ldapab][binddn],
+ "ObjectClass=OXUserObject",array("userCountry"));
+ $result2 = ldap_get_binentries($LDAP_CON, $sr);
+ }
+ $result = array_merge($result1,$result2);
+
+ if(count($result)){
+ foreach ($result as $entry){
+ if(count($entry['userCountry'])){
+ foreach($entry['userCountry'] as $c){
+ array_push($country, $c);
+ }
+ }
+ }
+ }
+ $country = array_unique($country);
+ sort($country,SORT_STRING);
+
+ utf8_decode_array($country);
+ $smarty->assign('country',$country);
+}
+
?>
</td>
</tr>
{/if}
+ {if $conf.openxchange}
+ {include file="openxchange_edit.tpl"}
+ {/if}
<tr>
<td colspan="2" align="center"><br><input type="submit" class="input" value="{$lang.submit}"></td>
</tr>
</td>
</tr>
</table>
+ {if $conf.openxchange}
+ {include file="openxchange_show.tpl"}
+ {/if}
<br><br><br>
<a href="index.php?filter=other">#</a>
<a href="index.php?filter=*">*</a>
</td>
+{if $conf.openxchange}
+<td class="filterrow" align="right">
+ <form method="get" action="index.php" style="display:inline">
+ <select name="categories" class="searchfield">
+ <option value="">--- {$lang.categories} ---</option>
+ {html_options values=$categories output=$categories selected=$smarty.request.categories}
+ </select>
+ <input type="submit" value="{$lang.search}" class="searchbutton">
+ </form>
+</td>
+{/if}
{if $conf.extended}
<td class="filterrow" align="right">
<form method="get" action="index.php" style="display:inline">
--- /dev/null
+<tr>
+<td width="50%" valign="top" align="center">
+ <table>
+ <tr>
+ <td colspan="2"><b>{$lang.openxchange}</b></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.birthday}:</td>
+ <td><input type="text" class="input" name="entry[birthday]" value="{$entry.birthday|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.ipphone}:</td>
+ <td><input type="text" class="input" name="entry[ipphone]" value="{$entry.ipphone|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.instantmessenger}:</td>
+ <td><input type="text" class="input" name="entry[instantmessenger]" value="{$entry.instantmessenger|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.domain}:</td>
+ <td><input type="text" class="input" name="entry[domain]" value="{$entry.domain|escape}"></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.certificate}:</td>
+ <td>
+ <textarea name="entry[certificate]" rows=6 cols=28 onClick="this.form.elements['entry[certificate]'].select();">{$entry.certificate|escape}</textarea>
+ <br>
+ <input type=button name=clearCert value="Clear" onClick="if (confirm('Are you sure?')) this.form.elements['entry[certificate]'].value='';">
+ </td>
+ </tr>
+ </table>
+</td>
+<td width="50%" valign="top" align="center">
+ <table>
+ <tr>
+ <td colspan="2"><b>{$lang.moreopenxchange}</b></td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.country}:</td>
+ <td>
+ <select name="entry[country][]" class="input">
+ <option value="">--- {$lang.select} ---</option>
+ {html_options values=$country output=$country selected=$entry.country}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[country][]"><br>
+ </td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.timezone}:</td>
+ <td>
+ <select name="entry[timezone][]" class="input">
+ <option value="">--- {$lang.select} ---</option>
+ {html_options values=$timezone output=$timezone selected=$entry.timezone}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[timezone][]"><br>
+ </td>
+ </tr>
+ <tr>
+ <td align="right" valign="top">{$lang.categories}:</td>
+ <td>
+ <select name="entry[categories][]" size="5" class="input" multiple="multiple">
+ {html_options values=$categories output=$categories selected=$entry.categories}
+ </select><br>
+ <input type="text" class="inputbr" name="entry[categories][]"><br>
+ <input type="text" class="inputbr" name="entry[categories][]"><br>
+ </td>
+
+ </tr>
+</table>
+</td>
+</tr>
--- /dev/null
+<table width="100%">
+ <tr>
+ <td width="50%" valign="top">
+ <b>{$lang.openxchange}</b>
+<dl><dd>
+ <table>
+ {if $entry.birthday}
+ <tr>
+ <td align="right">{$lang.birthday}:</td>
+ <td>{$entry.birthday}</td>
+ </tr>
+ {/if}
+ {if $entry.ipphone}
+ <tr>
+ <td align="right">{$lang.ipphone}:</td>
+ <td>{$entry.ipphone}</td>
+ </tr>
+ {/if}
+ {if $entry.instantmessenger}
+ <tr>
+ <td align="right">{$lang.instantmessenger}:</td>
+ <td>{$entry.instantmessenger}</td>
+ </tr>
+ {/if}
+ {if $entry.domain}
+ <tr>
+ <td align="right">{$lang.domain}:</td>
+ <td>{$entry.domain}</td>
+ </tr>
+ {/if}
+ {if $entry.certificate}
+ <tr>
+ <td align="right">{$lang.certificate}:</td>
+ <td><form>
+ <textarea rows=3 cols=28 name='certificate' onClick='this.form.certificate.select();'>{$entry.certificate}</textarea>
+ </form></td>
+ </tr>
+ {/if}
+ </table>
+</dd></dl>
+</td>
+ <td width="50%" valign="top">
+ <b>{$lang.moreopenxchange}</b>
+<dl><dd>
+<table >
+ {if $entry.country}
+ <tr>
+ <td align="right">{$lang.country}:</td>
+ <td>{$entry.country}</td>
+ </tr>
+ {/if}
+ {if $entry.timezone}
+ <tr>
+ <td align="right">{$lang.timezone}:</td>
+ <td>{$entry.timezone}</td>
+ </tr>
+ {/if}
+ {if $entry.categories}
+ <tr>
+ <td valign="top" align="right">{$lang.categories}:</td>
+ <td>
+ {foreach from=$entry.categories item=categories}
+ {$categories}<br>
+ {/foreach}
+ </td>
+ </tr>
+ {/if}
+</table>
+</dd></dl>
+ </td>
+ </tr>
+</table>