From 92ce7b810a273eb8579c75fd7ec49345c8e5be7d Mon Sep 17 00:00:00 2001 From: Roland Gruber Date: Sun, 27 Feb 2005 12:40:06 +0000 Subject: [PATCH] added tree view --- lam/HISTORY | 3 +- lam/help/help.inc | 6 + lam/lib/config.inc | 69 +- lam/lib/ldap.inc | 12 +- lam/lib/tree.inc | 1347 +++++++++++++++++ lam/session-vars.txt | 1 + lam/style/500_layout.css | 299 ++++ lam/templates/config/confmain.php | 19 +- lam/templates/config/confsave.php | 7 + lam/templates/main_header.php | 4 + lam/templates/tree/add_attr.php | 166 ++ lam/templates/tree/add_attr_form.php | 198 +++ lam/templates/tree/add_oclass.php | 97 ++ lam/templates/tree/add_oclass_form.php | 163 ++ lam/templates/tree/add_value.php | 100 ++ lam/templates/tree/add_value_form.php | 203 +++ lam/templates/tree/collapse.php | 75 + lam/templates/tree/create.php | 185 +++ lam/templates/tree/create_form.php | 145 ++ lam/templates/tree/creation_template.php | 96 ++ lam/templates/tree/delete.php | 112 ++ lam/templates/tree/delete_attr.php | 83 + lam/templates/tree/delete_form.php | 183 +++ lam/templates/tree/download_binary_attr.php | 87 ++ lam/templates/tree/edit.php | 544 +++++++ lam/templates/tree/expand.php | 88 ++ lam/templates/tree/rdelete.php | 154 ++ lam/templates/tree/refresh.php | 89 ++ .../tree/templates/creation/custom.php | 311 ++++ .../templates/creation/new_ou_template.php | 111 ++ lam/templates/tree/templates/templates.inc | 56 + lam/templates/tree/tree.php | 193 +++ lam/templates/tree/tree_view.php | 85 ++ lam/templates/tree/update.php | 109 ++ lam/templates/tree/update_confirm.php | 259 ++++ 35 files changed, 5647 insertions(+), 12 deletions(-) create mode 100644 lam/lib/tree.inc create mode 100644 lam/templates/tree/add_attr.php create mode 100644 lam/templates/tree/add_attr_form.php create mode 100644 lam/templates/tree/add_oclass.php create mode 100644 lam/templates/tree/add_oclass_form.php create mode 100644 lam/templates/tree/add_value.php create mode 100644 lam/templates/tree/add_value_form.php create mode 100644 lam/templates/tree/collapse.php create mode 100644 lam/templates/tree/create.php create mode 100644 lam/templates/tree/create_form.php create mode 100644 lam/templates/tree/creation_template.php create mode 100644 lam/templates/tree/delete.php create mode 100644 lam/templates/tree/delete_attr.php create mode 100644 lam/templates/tree/delete_form.php create mode 100644 lam/templates/tree/download_binary_attr.php create mode 100644 lam/templates/tree/edit.php create mode 100644 lam/templates/tree/expand.php create mode 100644 lam/templates/tree/rdelete.php create mode 100644 lam/templates/tree/refresh.php create mode 100644 lam/templates/tree/templates/creation/custom.php create mode 100644 lam/templates/tree/templates/creation/new_ou_template.php create mode 100644 lam/templates/tree/templates/templates.inc create mode 100644 lam/templates/tree/tree.php create mode 100644 lam/templates/tree/tree_view.php create mode 100644 lam/templates/tree/update.php create mode 100644 lam/templates/tree/update_confirm.php diff --git a/lam/HISTORY b/lam/HISTORY index d698bf61..2a48bf98 100644 --- a/lam/HISTORY +++ b/lam/HISTORY @@ -12,7 +12,8 @@ - new plugin for managing MAC addresses (RFE 926017) - new plugin for managing NIS mail aliases (RFE 1050036) - new plugin for managing mail routing with inetLocalMailRecipient (RFE 1092137) - - added schema browser + - schema browser + - tree view 26.01.2005 0.4.8 diff --git a/lam/help/help.inc b/lam/help/help.inc index 0ed1762b..8a0a9ec5 100644 --- a/lam/help/help.inc +++ b/lam/help/help.inc @@ -63,6 +63,12 @@ $helpArray = array ( _("Examples"). ":

". _("ou=People,dc=yourcompany,dc=com will read and store all accounts in this subtree.")), + "203" => array ("ext" => "FALSE", "Headline" => _("Configuration wizard") . " - " . _("Tree suffix"), + "Text" => _("This is the suffix for the LDAP tree viewer.") . + "

". + _("Example"). + ":

". + _("dc=yourcompany,dc=com")), "206" => array ("ext" => "FALSE", "Headline" => _("Configuration wizard") . " - " . _("List attributes"), "Text" => _("This is the list of attributes to show in the user/group/host list. The entries can either be predefined values, \"#attribute\", or individual ones, \"attribute:description\". Several entries are seperated by semicolons.") . "

" . diff --git a/lam/lib/config.inc b/lam/lib/config.inc index 5232afaf..587f886c 100644 --- a/lam/lib/config.inc +++ b/lam/lib/config.inc @@ -120,6 +120,9 @@ class Config { /** LDAP suffix for Samba 3 domains */ var $domainsuffix; + /** LDAP suffix for tree view */ + var $treesuffix; + /** Attributes that are shown in the user list */ var $userlistAttributes; /** Attributes that are shown in the group list */ @@ -165,7 +168,7 @@ class Config { var $file; /** List of all settings in config file */ - var $settings = array("ServerURL", "Passwd", "Admins", "usersuffix", "groupsuffix", "hostsuffix", + var $settings = array("ServerURL", "Passwd", "Admins", "usersuffix", "groupsuffix", "hostsuffix", "treesuffix", "domainsuffix", "userlistAttributes", "grouplistAttributes", "hostlistAttributes", "maxlistentries", "defaultLanguage", "scriptPath", "scriptServer", "cachetimeout", "usermodules", "groupmodules", "hostmodules", "modules"); @@ -288,10 +291,12 @@ class Config { "# e.g. ou=People,dc=yourdomain,dc=org\n" . "usersuffix: " . $this->usersuffix . "\n"); if (!in_array("groupsuffix", $saved)) array_push($file_array, "\n\n# suffix of groups\n" . "# e.g. ou=Groups,dc=yourdomain,dc=org\n" . "groupsuffix: " . $this->groupsuffix . "\n"); - if (!in_array("hostsuffix", $saved)) array_push($file_array, "\n\n# suffix of Samba hosts\n" . + if (!in_array("hostsuffix", $saved)) array_push($file_array, "\n\n# suffix of hosts\n" . "# e.g. ou=machines,dc=yourdomain,dc=org\n" . "hostsuffix: " . $this->hostsuffix . "\n"); if (!in_array("domainsuffix", $saved)) array_push($file_array, "\n\n# suffix of Samba 3 domains\n" . "# e.g. ou=domains,dc=yourdomain,dc=org\n" . "domainsuffix: " . $this->domainsuffix . "\n"); + if (!in_array("treesuffix", $saved)) array_push($file_array, "\n\n# suffix of tree view\n" . + "# e.g. dc=yourdomain,dc=org\n" . "treesuffix: " . $this->treesuffix . "\n"); if (!in_array("userlistAttributes", $saved)) array_push($file_array, "\n\n# list of attributes to show in user list\n# entries can either be predefined values (e.g. '#cn' or '#uid')" . "\n# or individual ones (e.g. 'uid:User ID' or 'host:Host Name')\n# values have to be seperated by semicolons\n" . "userlistAttributes: " . $this->userlistAttributes . "\n"); if (!in_array("grouplistAttributes", $saved)) array_push($file_array, "\n\n# list of attributes to show in group list\n# entries can either be predefined values (e.g. '#cn' or '#gidNumber')" . @@ -334,6 +339,7 @@ class Config { echo "" . _("GroupSuffix") . ": " . $this->groupsuffix . "
\n"; echo "" . _("HostSuffix") . ": " . $this->hostsuffix . "
\n"; echo "" . _("DomainSuffix") . ": " . $this->domainsuffix . "
\n"; + echo "" . _("TreeSuffix") . ": " . $this->treesuffix . "
\n"; echo "" . _("Attributes in User List") . ": " . $this->userlistAttributes . "
\n"; echo "" . _("Attributes in Group List") . ": " . $this->grouplistAttributes . "
\n"; echo "" . _("Attributes in Host List") . ": " . $this->hostlistAttributes . "
\n"; @@ -531,6 +537,65 @@ class Config { return true; } + /** + * Returns the LDAP suffix for the given account type + * + * @param string $scope account type + * @return string the LDAP suffix + */ + function get_Suffix($scope) { + switch ($scope) { + case 'user': + return $this->usersuffix; + break; + case 'group': + return $this->groupsuffix; + break; + case 'host': + return $this->hostsuffix; + break; + case 'domain': + return $this->domainsuffix; + break; + case 'tree': + return $this->treesuffix; + break; + } + return ""; + } + + /** + * Sets the LDAP suffix where accounts are saved + * + * @param string $scope account type + * @param string $value new LDAP suffix + * @return boolean true if $value has correct format + */ + function set_Suffix($scope, $value) { + if (!$value) $value = ""; + elseif (!is_string($value)) { + return false; + } + switch ($scope) { + case 'user': + $this->usersuffix = $value; + break; + case 'group': + $this->groupsuffix = $value; + break; + case 'host': + $this->hostsuffix = $value; + break; + case 'domain': + $this->domainsuffix = $value; + break; + case 'tree': + $this->treesuffix = $value; + break; + } + return true; + } + /** * Returns the list of attributes to show in user list * diff --git a/lam/lib/ldap.inc b/lam/lib/ldap.inc index 857f6686..c824296a 100644 --- a/lam/lib/ldap.inc +++ b/lam/lib/ldap.inc @@ -312,7 +312,7 @@ class Ldap{ $time = time(); $dir = @opendir($relpath); while ($file = @readdir($dir)) { - if (substr($file, -4) == '.pdf') { + if ((substr($file, -4) == '.pdf') || (substr($file, -4) == '.jpg')) { $path = $relpath . $file; if ($time - filemtime($path) > 600) { @unlink($path); @@ -323,11 +323,17 @@ class Ldap{ } } - /** Calculates a new value for rand */ + /** + * Calculates a new value for rand + * + * @return int New random value + */ function new_rand() { // change random number mt_srand($this->rand + (microtime() * 1000000)); - $this->rand = mt_rand(); + $r = mt_rand(); + $this->rand = $r; + return $r; } /** diff --git a/lam/lib/tree.inc b/lam/lib/tree.inc new file mode 100644 index 00000000..a8778688 --- /dev/null +++ b/lam/lib/tree.inc @@ -0,0 +1,1347 @@ +, so if I wanted to scroll to + * dc=example,dc=com for server 3, the URL would be: + * tree.php#3_dc%3Dexample%2Cdc%3Dcom + * + * @package lists + * @subpackage tree + * @author David Smith + * @author Roland Gruber + */ + +/** + * Prints the HTML of the tree view. + */ +function draw_server_tree() +{ + global $tree; + global $tree_icons; + + + $refresh_href = 'refresh.php'; + $create_href = 'create_form.php?container=' . + rawurlencode( $_SESSION['config']->get_Suffix('tree') ); + + // Draw the quick-links below the server name: + // ( schema | search | refresh | create ) + echo ''; + echo ''; + echo '( '; + echo '' . _('Refresh') . ' | '; + echo '' . _('Create new entry') . ''; + echo ' )'; + + // Fetch and display the base DN for this server + $base_dn = $_SESSION['config']->get_Suffix('tree'); + + // Did we get a base_dn for this server somehow? + if( $base_dn ) { + echo "\n\n\n\n"; + + // is the root of the tree expanded already? + if( isset( $tree[$base_dn] ) ) { + $expand_href = "collapse.php?" . + "dn=" . rawurlencode( $base_dn ); + $expand_img = "../../graphics/minus.png"; + $expand_alt = "-"; + $child_count = number_format( count( $tree[$base_dn] ) ); + } + else { + $expand_href = "expand.php?" . + "dn=" . rawurlencode( $base_dn ); + $expand_img = "../../graphics/plus.png"; + $expand_alt = "+"; + $child_count = count( get_container_contents( + $base_dn, 0, + '(objectClass=*)') ); + if( $child_count > $limit ) + $child_count = $limit . '+'; + } + + $edit_href = "edit.php?dn=" . rawurlencode( $base_dn ); + + $icon = isset( $tree_icons[ $base_dn ] ) + ? $tree_icons[ $base_dn ] + : get_icon( $base_dn ); + + echo ""; + echo "\"$expand_alt\""; + echo ""; + echo "\"img\"\n"; + echo "" . pretty_print_dn( $base_dn ) . ''; + if( $child_count ) + echo " ($child_count)"; + echo "\n"; + echo "\n"; + + if(isset($tree[ $base_dn ]) + && count( $tree[ $base_dn ] ) > 10 ) + draw_create_link( $base_dn, -1, urlencode( $base_dn )); + + } + + flush(); + + // Is the root of the tree expanded already? + if( isset( $tree[$base_dn] ) && is_array( $tree[$base_dn] ) ) { + foreach( $tree[ $base_dn ] as $child_dn ) + draw_tree_html( $child_dn, 0 ); + echo ''; + echo '' . 
+						_('Create new entry') . ''; + echo '' . _('Create new entry') . ''; + } + +} + +/** + * Checks and fixes an initial session's tree cache if needed. + * + * This function is not meant as a user-callable function, but rather a convenient, + * automated method for checking the initial data structure of the session. + */ +function initialize_session_tree() +{ + // From the PHP manual: If you use $_SESSION don't use + // session_register(), session_is_registered() or session_unregister()! + if( ! array_key_exists( 'tree', $_SESSION ) ) + $_SESSION['tree'] = array(); + if( ! array_key_exists( 'tree_icons', $_SESSION ) ) + $_SESSION['tree_icons'] = build_initial_tree_icons(); + + // Make sure that the tree index is indeed well formed. + if( ! is_array( $_SESSION['tree'] ) ) + $_SESSION['tree'] = array(); + if( ! is_array( $_SESSION['tree_icons'] ) ) + $_SESSION['tree_icons'] = build_initial_tree_icons(); + +} + +/** + * Builds the initial array that stores the icon-lookup for each server's DN in the tree browser. The returned + * array is then stored in the current session. The structure of the returned array is simple, and looks like + * this: + * + * Array + * ( + * [0] => Array + * ( + * [dc=example,dc=com] => "dcobject.png" + * ) + * [1] => Array + ( + * [o=Corporation] => "o.png" + * ) + * ) + * + * This function is not meant as a user-callable function, but rather a convenient, automated method for + * setting up the initial data structure for the tree viewer's icon cache. + */ +function build_initial_tree_icons() +{ + $tree_icons = array(); + + // initialize an empty array for each server + $tree_icons = array(); + $tree_icons[ $_SESSION['config']->get_Suffix('tree') ] = get_icon( $_SESSION['config']->get_Suffix('tree') ); + + return $tree_icons; +} + +/** + * Gets whether an entry exists based on its DN. If the entry exists, + * returns true. Otherwise returns false. + * + * @param string $dn The DN of the entry of interest. + * + * @return bool + */ +function dn_exists( $dn ) +{ + $search_result = @ldap_read( $_SESSION['ldap']->server, $dn, 'objectClass=*', array('dn') ); + + if( ! $search_result ) + return false; + + $num_entries = ldap_count_entries( $_SESSION['ldap']->server, $search_result ); + + if( $num_entries > 0 ) + return true; + else + return false; +} + +/** + * Gets a list of child entries for an entry. Given a DN, this function fetches the list of DNs of + * child entries one level beneath the parent. For example, for the following tree: + * + * + * dc=example,dc=com + * ou=People + * cn=Dave + * cn=Fred + * cn=Joe + * ou=More People + * cn=Mark + * cn=Bob + * + * + * Calling get_container_contents( "ou=people,dc=example,dc=com" ) + * would return the following list: + * + * + * cn=Dave + * cn=Fred + * cn=Joe + * ou=More People + * + * + * @param string $dn The DN of the entry whose children to return. + * @param int $size_limit (optional) The maximum number of entries to return. + * If unspecified, no limit is applied to the number of entries in the returned. + * @param string $filter (optional) An LDAP filter to apply when fetching children, example: "(objectClass=inetOrgPerson)" + * @return array An array of DN strings listing the immediate children of the specified entry. + */ +function get_container_contents( $dn, $size_limit=0, $filter='(objectClass=*)' ) +{ + $search = @ldap_list( $_SESSION['ldap']->server, $dn, $filter, array( 'dn' ), 1, $size_limit, 0); + if( ! $search ) + return array(); + $search = ldap_get_entries( $_SESSION['ldap']->server, $search ); + + $return = array(); + for( $i=0; $i<$search['count']; $i++ ) { + $entry = $search[$i]; + $dn = $entry['dn']; + $return[] = $dn; + } + + return $return; +} + +/** + * Given a DN and server ID, this function reads the DN's objectClasses and + * determines which icon best represents the entry. The results of this query + * are cached in a session variable so it is not run every time the tree + * browser changes, just when exposing new DNs that were not displayed + * previously. That means we can afford a little bit of inefficiency here + * in favor of coolness. :) + * + * This function returns a string like "country.png". All icon files are assumed + * to be contained in the /../../graphics/ directory of phpLDAPadmin. + * + * Developers are encouraged to add new icons to the images directory and modify + * this function as needed to suit their types of LDAP entries. If the modifications + * are general to an LDAP audience, the phpLDAPadmin team will gladly accept them + * as a patch. + * + * @param string $dn The DN of the entry whose icon you wish to fetch. + * + * @return string + */ +function get_icon( $dn ) +{ + // fetch and lowercase all the objectClasses in an array + $object_classes = get_object_attr( $dn, 'objectClass', true ); + + if( $object_classes === null || $object_classes === false || ! is_array( $object_classes ) ) + $object_classes = array(); + + foreach( $object_classes as $i => $class ) + $object_classes[$i] = strtolower( $class ); + + $rdn = get_rdn( $dn ); + $rdn_parts = explode( '=', $rdn, 2 ); + $rdn_value = isset( $rdn_parts[0] ) ? $rdn_parts[0] : null; + unset( $rdn_parts ); + + // return icon filename based upon objectClass value + if( in_array( 'sambaaccount', $object_classes ) && + '$' == $rdn{ strlen($rdn) - 1 } ) + return 'nt_machine.png'; + if( in_array( 'sambaaccount', $object_classes ) ) + return 'nt_user.png'; + elseif( in_array( 'person', $object_classes ) || + in_array( 'organizationalperson', $object_classes ) || + in_array( 'inetorgperson', $object_classes ) || + in_array( 'account', $object_classes ) || + in_array( 'posixaccount', $object_classes ) ) + return 'user.png'; + elseif( in_array( 'organization', $object_classes ) ) + return 'o.png'; + elseif( in_array( 'organizationalunit', $object_classes ) ) + return 'ou.png'; + elseif( in_array( 'organizationalrole', $object_classes ) ) + return 'uid.png'; + elseif( in_array( 'dcobject', $object_classes ) || + in_array( 'domainrelatedobject', $object_classes ) || + in_array( 'domain', $object_classes ) || + in_array( 'builtindomain', $object_classes )) + return 'dc.png'; + elseif( in_array( 'alias', $object_classes ) ) + return 'go.png'; + elseif( in_array( 'room', $object_classes ) ) + return 'door.png'; + elseif( in_array( 'device', $object_classes ) ) + return 'device.png'; + elseif( in_array( 'document', $object_classes ) ) + return 'document.png'; + elseif( in_array( 'jammvirtualdomain', $object_classes ) ) + return 'mail.png'; + elseif( in_array( 'locality', $object_classes ) ) + return 'locality.png'; + elseif( in_array( 'posixgroup', $object_classes ) || + in_array( 'groupofnames', $object_classes ) || + in_array( 'group', $object_classes ) ) + return 'ou.png'; + elseif( in_array( 'applicationprocess', $object_classes ) ) + return 'process.png'; + elseif( in_array( 'groupofuniquenames', $object_classes ) ) + return 'uniquegroup.png'; + elseif( in_array( 'iphost', $object_classes ) ) + return 'host.png'; + elseif( in_array( 'nlsproductcontainer', $object_classes ) ) + return 'n.png'; + elseif( in_array( 'ndspkikeymaterial', $object_classes ) ) + return 'lock.png'; + elseif( in_array( 'server', $object_classes ) ) + return 'server-small.png'; + elseif( in_array( 'volume', $object_classes ) ) + return 'hard-drive.png'; + elseif( in_array( 'ndscatcatalog', $object_classes ) ) + return 'catalog.png'; + elseif( in_array( 'resource', $object_classes ) ) + return 'n.png'; + elseif( in_array( 'ldapgroup', $object_classes ) ) + return 'ldap-server.png'; + elseif( in_array( 'ldapserver', $object_classes ) ) + return 'ldap-server.png'; + elseif( in_array( 'nisserver', $object_classes ) ) + return 'ldap-server.png'; + elseif( in_array( 'rbscollection', $object_classes ) ) + return 'ou.png'; + elseif( in_array( 'dfsconfiguration', $object_classes ) ) + return 'nt_machine.png'; + elseif( in_array( 'applicationsettings', $object_classes ) ) + return 'server-settings.png'; + elseif( in_array( 'aspenalias', $object_classes ) ) + return 'mail.png'; + elseif( in_array( 'container', $object_classes ) ) + return 'folder.png'; + elseif( in_array( 'ipnetwork', $object_classes ) ) + return 'network.png'; + elseif( in_array( 'samserver', $object_classes ) ) + return 'server-small.png'; + elseif( in_array( 'lostandfound', $object_classes ) ) + return 'find.png'; + elseif( in_array( 'infrastructureupdate', $object_classes ) ) + return 'server-small.png'; + elseif( in_array( 'filelinktracking', $object_classes ) ) + return 'files.png'; + elseif( in_array( 'automountmap', $object_classes ) || + in_array( 'automount', $object_classes ) ) + return 'hard-drive.png'; + elseif( 0 === strpos( $rdn_value, "ipsec" ) || + 0 == strcasecmp( $rdn_value, "IP Security" ) || + 0 == strcasecmp( $rdn_value, "MSRADIUSPRIVKEY Secret" ) || + 0 === strpos( $rdn_value, "BCKUPKEY_" ) ) + return 'lock.png'; + elseif( 0 == strcasecmp( $rdn_value, "MicrosoftDNS" ) ) + return 'dc.png'; + // Oh well, I don't know what it is. Use a generic icon. + else + return 'object.png'; +} + +/** + * Much like get_object_attrs(), but only returns the values for + * one attribute of an object. Example calls: + * + * + * print_r( get_object_attr( 0, "cn=Bob,ou=people,dc=example,dc=com", "sn" ) ); + * // prints: + * // Array + * // ( + * // [0] => "Smith" + * // ) + * + * print_r( get_object_attr( 0, "cn=Bob,ou=people,dc=example,dc=com", "objectClass" ) ); + * // prints: + * // Array + * // ( + * // [0] => "top" + * // [1] => "person" + * // ) + * + * + * @param string $dn The distinguished name (DN) of the entry whose attributes/values to fetch. + * @param string $attr The attribute whose value(s) to return (ie, "objectClass", "cn", "userPassword") + * @param bool $lower_case_attr_names (optional) If true, all keys of the returned associative + * array will be lower case. Otherwise, they will be cased as the LDAP server returns + * them. + * @see get_object_attrs + */ +function get_object_attr( $dn, $attr ) +{ + $search = @ldap_read( $_SESSION['ldap']->server, $dn, '(objectClass=*)', array( $attr ), 0, 0, 0 ); + + if( ! $search ) + return false; + + $entry = ldap_first_entry( $_SESSION['ldap']->server, $search ); + + if( ! $entry ) + return false; + + $attrs = ldap_get_attributes( $_SESSION['ldap']->server, $entry ); + + if( ! $attrs || $attrs['count'] == 0 ) + return false; + + $vals = ldap_get_values( $_SESSION['ldap']->server, $entry, $attr ); + unset( $vals['count'] ); + return $vals; +} + +/** + * Given a DN string, this returns the 'RDN' portion of the string. + * For example. given 'cn=Manager,dc=example,dc=com', this function returns + * 'cn=Manager' (it is really the exact opposite of get_container()). + * + * @param string $dn The DN whose RDN to return. + * @param bool $include_attrs If true, include attributes in the RDN string. + * See http://php.net/ldap_explode_dn for details + * + * @return string The RDN + * @see get_container + */ +function get_rdn( $dn, $include_attrs=0 ) +{ + if( $dn == null ) + return null; + $rdn = pla_explode_dn( $dn, $include_attrs ); + if( 0 == count($rdn) ) + return $dn; + if( ! isset( $rdn[0] ) ) + return $dn; + $rdn = $rdn[0]; + return $rdn; +} + +/** + * Explode a DN into an array of its RDN parts. This function is UTF-8 safe + * and replaces the buggy PHP ldap_explode_dn() which does not properly + * handle UTF-8 DNs and also causes segmentation faults with some inputs. + * + * @param string $dn The DN to explode. + * @param int $with_attriutes (optional) Whether to include attribute names (see http://php.net/ldap_explode_dn for details) + * + * @return array An array of RDN parts of this format: + * + * Array + * ( + * [0] => uid=ppratt + * [1] => ou=People + * [2] => dc=example + * [3] => dc=com + * ) + * + */ +function pla_explode_dn( $dn, $with_attributes=0 ) +{ + // replace "\," with the hexadecimal value for safe split + $var = preg_replace("/\\\,/","\\\\\\\\2C",$dn); + + // split the dn + $result = explode(",",$var); + + //translate hex code into ascii for display + foreach( $result as $key => $value ) + $result[$key] = preg_replace("/\\\([0-9A-Fa-f]{2})/e", "''.chr(hexdec('\\1')).''", $value); + + return $result; +} + +/** + * Returns an HTML-beautified version of a DN. + * Internally, this function makes use of pla_explode_dn() to break the + * the DN into its components. It then glues them back together with + * "pretty" HTML. The returned HTML is NOT to be used as a real DN, but + * simply displayed. + * + * @param string $dn The DN to pretty-print. + * @return string + */ +function pretty_print_dn( $dn ) +{ + $dn = pla_explode_dn( $dn ); + foreach( $dn as $i => $element ) { + $element = htmlspecialchars( $element ); + $element = explode( '=', $element, 2 ); + $element = implode( '=', $element ); + $dn[$i] = $element; + } + $dn = implode( ',', $dn ); + + return $dn; +} + +/** + * Compares 2 DNs. If they are equivelant, returns 0, otherwise, + * returns their sorting order (similar to strcmp()): + * Returns < 0 if dn1 is less than dn2. + * Returns > 0 if dn1 is greater than dn2. + * + * The comparison is performed starting with the top-most element + * of the DN. Thus, the following list: + * + * ou=people,dc=example,dc=com + * cn=Admin,ou=People,dc=example,dc=com + * cn=Joe,ou=people,dc=example,dc=com + * dc=example,dc=com + * cn=Fred,ou=people,dc=example,dc=org + * cn=Dave,ou=people,dc=example,dc=org + * + * Will be sorted thus using usort( $list, "pla_compare_dns" ): + * + * dc=com + * dc=example,dc=com + * ou=people,dc=example,dc=com + * cn=Admin,ou=People,dc=example,dc=com + * cn=Joe,ou=people,dc=example,dc=com + * cn=Dave,ou=people,dc=example,dc=org + * cn=Fred,ou=people,dc=example,dc=org + * + * + * @param string $dn1 The first of two DNs to compare + * @param string $dn2 The second of two DNs to compare + * @return int + */ +function pla_compare_dns( $dn1, $dn2 ) +{ + // If they are obviously the same, return immediately + if( 0 === strcasecmp( $dn1, $dn2 ) ) + return 0; + + $dn1_parts = pla_explode_dn( pla_reverse_dn($dn1) ); + $dn2_parts = pla_explode_dn( pla_reverse_dn($dn2) ); + assert( is_array( $dn1_parts ) ); + assert( is_array( $dn2_parts ) ); + + // Foreach of the "parts" of the smaller DN + for( $i=0; $i count($dn2_parts) ) { + return 1; + } elseif( count( $dn2_parts ) > count( $dn1_parts ) ) { + return -1; + } else { + return 0; + } +} + +/** + * Reverses a DN such that the top-level RDN is first and the bottom-level RDN is last + * For example: + * + * cn=Brigham,ou=People,dc=example,dc=com + * + * Becomes: + * + * dc=com,dc=example,ou=People,cn=Brigham + * + * This makes it possible to sort lists of DNs such that they are grouped by container. + * + * @param string $dn The DN to reverse + * + * @return string The reversed DN + * + * @see pla_compare_dns + */ +function pla_reverse_dn($dn) +{ + foreach (pla_explode_dn($dn) as $key => $branch) { + + // pla_expode_dn returns the array with an extra count attribute, we can ignore that. + if ( $key === "count" ) continue; + + if (isset($rev)) { + $rev = $branch.",".$rev; + } else { + $rev = $branch; + } + } + return $rev; +} + +/** + * Gets a DN string using the user-configured tree_display_format string to format it. + */ +function draw_formatted_dn( $dn ) +{ + $format = '%rdn'; + preg_match_all( "/%[a-zA-Z_0-9]+/", $format, $tokens ); + $tokens = $tokens[0]; + foreach( $tokens as $token ) { + if( 0 == strcasecmp( $token, '%dn' ) ) + $format = str_replace( $token, pretty_print_dn( $dn ), $format ); + elseif( 0 == strcasecmp( $token, '%rdn' ) ) + $format = str_replace( $token, pretty_print_dn( get_rdn( $dn ) ), $format ); + elseif( 0 == strcasecmp( $token, '%rdnvalue' ) ) { + $rdn = get_rdn( $dn ); + $rdn_value = explode( '=', $rdn, 2 ); + $rdn_value = $rdn_value[1]; + $format = str_replace( $token, $rdn_value, $format ); + } else { + $attr_name = str_replace( '%', '', $token ); + $attr_values = get_object_attr( $dn, $attr_name ); + if( null == $attr_values ) + $display = 'none'; + elseif( is_array( $attr_values ) ) + $display = htmlspecialchars( implode( ', ', $attr_values ) ); + else + $display = htmlspecialchars( $attr_values ); + $format = str_replace( $token, $display, $format ); + } + } + echo $format; +} + +/** + * Gets the attributes/values of an entry. Returns an associative array whose + * keys are attribute value names and whose values are arrays of values for + * said attribute. Optionally, callers may specify true for the parameter + * $lower_case_attr_names to force all keys in the associate array (attribute + * names) to be lower case. + * + * Sample return value of get_object_attrs( 0, "cn=Bob,ou=pepole,dc=example,dc=com" ) + * + * + * Array + * ( + * [objectClass] => Array + * ( + * [0] => person + * [1] => top + * ) + * [cn] => Array + * ( + * [0] => Bob + * ) + * [sn] => Array + * ( + * [0] => Jones + * ) + * [dn] => Array + * ( + * [0] => cn=Bob,ou=pepole,dc=example,dc=com + * ) + * ) + * + * + * @param string $dn The distinguished name (DN) of the entry whose attributes/values to fetch. + * @param bool $lower_case_attr_names (optional) If true, all keys of the returned associative + * array will be lower case. Otherwise, they will be cased as the LDAP server returns + * them. + * @param int $deref For aliases and referrals, this parameter specifies whether to + * follow references to the referenced DN or to fetch the attributes for + * the referencing DN. See http://php.net/ldap_search for the 4 valid + * options. + * @return array + * @see get_entry_system_attrs + * @see get_object_attr + */ +function get_object_attrs( $dn, $lower_case_attr_names=false, $deref=LDAP_DEREF_NEVER ) +{ + + $conn = $_SESSION['ldap']->server; + $search = @ldap_read( $conn, $dn, '(objectClass=*)', array( ), 0, 0, 0, $deref ); + + if( ! $search ) + return false; + + $entry = ldap_first_entry( $conn, $search ); + + if( ! $entry ) + return false; + + $attrs = ldap_get_attributes( $conn, $entry ); + + if( ! $attrs || $attrs['count'] == 0 ) + return false; + + $num_attrs = $attrs['count']; + unset( $attrs['count'] ); + + // strip numerical inices + for( $i=0; $i<$num_attrs; $i++ ) + unset( $attrs[$i] ); + + $return_array = array(); + foreach( $attrs as $attr => $vals ) { + if( $lower_case_attr_names ) + $attr = strtolower( $attr ); + if( is_attr_binary( $attr ) ) + $vals = ldap_get_values_len( $conn, $entry, $attr ); + unset( $vals['count'] ); + $return_array[ $attr ] = $vals; + } + + ksort( $return_array ); + + return $return_array; +} + +/** + * Given an attribute name and server ID number, this function returns + * whether the attrbiute may contain binary data. This is useful for + * developers who wish to display the contents of an arbitrary attribute + * but don't want to dump binary data on the page. + * + * @param string $attr_name The name of the attribute to test. + * @return bool + * + * @see is_jpeg_photo + */ +function is_attr_binary( $attr_name ) +{ + $attr_name = strtolower( $attr_name ); + /** Determining if an attribute is binary can be an expensive + operation. We cache the results for each attr name on each + server in the $attr_cache to speed up subsequent calls. + The $attr_cache looks like this: + Array + 0 => Array + 'objectclass' => false + 'cn' => false + 'usercertificate' => true + 1 => Array + 'jpegphoto' => true + 'cn' => false + */ + + static $attr_cache; + if( isset( $attr_cache[ $attr_name ] ) ) + return $attr_cache[ $attr_name ]; + + if( $attr_name == 'userpassword' ) { + $attr_cache[ $attr_name ] = false; + return false; + } + + // Quick check: If the attr name ends in ";binary", then it's binary. + if( 0 == strcasecmp( substr( $attr_name, strlen( $attr_name ) - 7 ), ";binary" ) ) { + $attr_cache[ $attr_name ] = true; + return true; + } + + // See what the server schema says about this attribute + $schema_attr = get_schema_attribute( $attr_name ); + if( ! $schema_attr ) { + // Strangely, some attributeTypes may not show up in the server + // schema. This behavior has been observed in MS Active Directory. + $type = null; + $syntax = null; + } else { + $type = $schema_attr->getType(); + $syntax = $schema_attr->getSyntaxOID(); + } + + if( 0 == strcasecmp( $type, 'Certificate' ) || + 0 == strcasecmp( $type, 'Binary' ) || + 0 == strcasecmp( $attr_name, 'usercertificate' ) || + 0 == strcasecmp( $attr_name, 'usersmimecertificate' ) || + 0 == strcasecmp( $attr_name, 'networkaddress' ) || + 0 == strcasecmp( $attr_name, 'objectGUID' ) || + 0 == strcasecmp( $attr_name, 'objectSID' ) || + $syntax == '1.3.6.1.4.1.1466.115.121.1.10' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.28' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.5' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.8' || + $syntax == '1.3.6.1.4.1.1466.115.121.1.9' ) { + $attr_cache[ $attr_name ] = true; + return true; + } else { + $attr_cache[ $attr_name ] = false; + return false; + } +} + +/** + * Prunes off anything after the ";" in an attr name. This is useful for + * attributes that may have ";binary" appended to their names. With + * real_attr_name(), you can more easily fetch these attributes' schema + * with their "real" attribute name. + * + * @param string $attr_name The name of the attribute to examine. + * @return string + */ +function real_attr_name( $attr_name ) +{ + $attr_name = preg_replace( "/;.*$/U", "", $attr_name ); + return $attr_name; +} + +/** + * Gets the operational attributes for an entry. Given a DN, this function fetches that entry's + * operational (ie, system or internal) attributes. These attributes include "createTimeStamp", + * "creatorsName", and any other attribute that the LDAP server sets automatically. The returned + * associative array is of this form: + * + * Array + * ( + * [creatorsName] => Array + * ( + * [0] => "cn=Admin,dc=example,dc=com" + * ) + * [createTimeStamp]=> Array + * ( + * [0] => "10401040130" + * ) + * [hasSubordinates] => Array + * ( + * [0] => "FALSE" + * ) + * ) + * + * + * @param string $dn The DN of the entry whose interal attributes are desired. + * @param int $deref For aliases and referrals, this parameter specifies whether to + * follow references to the referenced DN or to fetch the attributes for + * the referencing DN. See http://php.net/ldap_search for the 4 valid + * options. + * @return array An associative array whose keys are attribute names and whose values + * are arrays of values for the aforementioned attribute. + */ +function get_entry_system_attrs( $dn, $deref=LDAP_DEREF_NEVER ) +{ + $conn = $_SESSION['ldap']->server; + $attrs = array( 'creatorsname', 'createtimestamp', 'modifiersname', + 'structuralObjectClass', 'entryUUID', 'modifytimestamp', + 'subschemaSubentry', 'hasSubordinates', '+' ); + $search = @ldap_read( $conn, $dn, '(objectClass=*)', $attrs, 0, 0, 0, $deref ); + if( ! $search ) + return false; + $entry = ldap_first_entry( $conn, $search ); + if( ! $entry) + return false; + $attrs = ldap_get_attributes( $conn, $entry ); + if( ! $attrs ) + return false; + if( ! isset( $attrs['count'] ) ) + return false; + $count = $attrs['count']; + unset( $attrs['count'] ); + $return_attrs = array(); + for( $i=0; $i<$count; $i++ ) { + $attr_name = $attrs[$i]; + unset( $attrs[$attr_name]['count'] ); + $return_attrs[$attr_name] = $attrs[$attr_name]; + } + return $return_attrs; +} + +function arrayLower($array) { + foreach ($array as $key => $value) { + $newarray[$key] = strtolower($value); + } + return $newarray; +} + +/** + * Used to determine if the specified attribute is indeed a jpegPhoto. If the + * specified attribute is one that houses jpeg data, true is returned. Otherwise + * this function returns false. + * + * @param string $attr_name The name of the attribute to test. + * @return bool + * @see draw_jpeg_photos + */ +function is_jpeg_photo( $attr_name ) +{ + // easy quick check + if( 0 == strcasecmp( $attr_name, 'jpegPhoto' ) || + 0 == strcasecmp( $attr_name, 'photo' ) ) + return true; + + // go to the schema and get the Syntax OID + $schema_attr = get_schema_attribute( $attr_name ); + if( ! $schema_attr ) + return false; + + $oid = $schema_attr->getSyntaxOID(); + $type = $schema_attr->getType(); + + if( 0 == strcasecmp( $type, 'JPEG' ) ) + return true; + if( $oid == '1.3.6.1.4.1.1466.115.121.1.28' ) + return true; + + return false; +} + +/** + * Given an attribute name and server ID number, this function returns + * whether the attrbiute contains boolean data. This is useful for + * developers who wish to display the contents of a boolean attribute + * with a drop-down. + * + * @param string $attr_name The name of the attribute to test. + * @return bool + */ +function is_attr_boolean( $attr_name ) +{ + $type = ( $schema_attr = get_schema_attribute( $attr_name ) ) ? + $schema_attr->getType() : + null; + if( 0 == strcasecmp( 'boolean', $type ) || + 0 == strcasecmp( 'isCriticalSystemObject', $attr_name ) || + 0 == strcasecmp( 'showInAdvancedViewOnly', $attr_name ) ) + return true; + else + return false; +} + +/** + * Get whether a string looks like an email address (user@example.com). + * + * @param string $str The string to analyze. + * @return bool Returns true if the specified string looks like + * an email address or false otherwise. + */ +function is_mail_string( $str ) +{ + $mail_regex = "/^[_A-Za-z0-9-]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*$/"; + if( preg_match( $mail_regex, $str ) ) + return true; + else + return false; +} + +/** + * Get whether a string looks like a web URL (http://www.example.com/) + * + * @param string $str The string to analyze. + * @return bool Returns true if the specified string looks like + * a web URL or false otherwise. + */ +function is_url_string( $str ) +{ + $url_regex = '/(ftp|https?):\/\/+[\w\.\-\/\?\=\&]*\w+/'; + if( preg_match( $url_regex, $str ) ) + return true; + else + return false; + +} + +function sortAttrs($a,$b) { + return strcmp ($a, $b); +} + +/** + * Determines if an attribute's value can contain multiple lines. Attributes that fall + * in this multi-line category may be configured in config.php. Hence, this function + * accesses the global variable $multi_line_attributes; + * + * Usage example: + * + * if( is_muli_line_attr( "postalAddress" ) ) + * echo ""; + * else + * echo ""; + * + * + * @param string $attr_name The name of the attribute of interestd (case insensivite) + * @param string $val (optional) The current value of the attribute (speeds up the + * process by searching for carriage returns already in the attribute value) + * @return bool + */ +function is_multi_line_attr( $attr_name, $val=null ) +{ + // First, check the optional val param for a \n or a \r + if( null != $val && + ( false !== strpos( $val, "\n" ) || + false !== strpos( $val, "\r" ) ) ) + return true; + + // Next, compare strictly by name first + global $multi_line_attributes; + if( isset( $multi_line_attributes ) && is_array( $multi_line_attributes ) ) + foreach( $multi_line_attributes as $multi_line_attr_name ) + if( 0 == strcasecmp( $multi_line_attr_name, $attr_name ) ) { + return true; + } + + global $multi_line_syntax_oids; + if( isset( $multi_line_syntax_oids ) && is_array( $multi_line_syntax_oids ) ) { + $schema_attr = get_schema_attribute( $attr_name ); + if( ! $schema_attr ) + return false; + $syntax_oid = $schema_attr->getSyntaxOID(); + if( ! $syntax_oid ) + return false; + foreach( $multi_line_syntax_oids as $multi_line_syntax_oid ) + if( $multi_line_syntax_oid == $syntax_oid ) + return true; + } + + return false; + +} + +/** + * Returns true if the attribute specified is required to take as input a DN. + * Some examples include 'distinguishedName', 'member' and 'uniqueMember'. + * @param string $attr_name The name of the attribute of interest (case insensitive) + * @return bool + */ +function is_dn_attr( $attr_name ) +{ + // Simple test first + $dn_attrs = array( "aliasedObjectName" ); + foreach( $dn_attrs as $dn_attr ) + if( 0 == strcasecmp( $attr_name, $dn_attr ) ) + return true; + + // Now look at the schema OID + $attr_schema = get_schema_attribute( $attr_name ); + if( ! $attr_schema ) + return false; + $syntax_oid = $attr_schema->getSyntaxOID(); + if( '1.3.6.1.4.1.1466.115.121.1.12' == $syntax_oid ) + return true; + if( '1.3.6.1.4.1.1466.115.121.1.34' == $syntax_oid ) + return true; + $syntaxes = get_schema_syntaxes(); + if( ! isset( $syntaxes[ $syntax_oid ] ) ) + return false; + $syntax_desc = $syntaxes[ $syntax_oid ]->getDescription(); + if( false !== strpos( strtolower($syntax_desc), 'distinguished name' ) ) + return true; + return false; +} + +/** + * Checks if a string exists in an array, ignoring case. + */ +function in_array_ignore_case( $needle, $haystack ) +{ + if( ! is_array( $haystack ) ) + return false; + if( ! is_string( $needle ) ) + return false; + foreach( $haystack as $element ) + if( is_string( $element ) && 0 == strcasecmp( $needle, $element ) ) + return true; + return false; +} + +function get_enc_type( $user_password ) +{ + /* Capture the stuff in the { } to determine if this is crypt, md5, etc. */ + $enc_type = null; + if( preg_match( "/{([^}]+)}/", $user_password, $enc_type) ) + $enc_type = strtolower( $enc_type[1] ); + else + return null; + + /* handle crypt types */ + if( 0 == strcasecmp( $enc_type, 'crypt') ) { + $salt = null; + if( preg_match( "/{[^}]+}\\$(.)\\$/", $user_password, $salt) ) + $salt = $salt[1]; + else + $salt = null; + switch( $salt ) { + case '': // CRYPT_STD_DES + $enc_type = "crypt"; + break; + case '1': // CRYPT_MD5 + $enc_type = "md5crypt"; + break; + case '2': // CRYPT_BLOWFISH + $enc_type = "blowfish"; + break; + default: + $enc_type = "crypt"; + } + } + return $enc_type; +} + +/** + * Draw the jpegPhoto image(s) for an entry wrapped in HTML. Many options are available to + * specify how the images are to be displayed. + * + * Usage Examples: + * + * draw_jpeg_photos( 0, "cn=Bob,ou=People,dc=example,dc=com", "jpegPhoto" true, false, "border: 1px; width: 150px" ); + * draw_jpeg_photos( 1, "cn=Fred,ou=People,dc=example,dc=com" ); + * + * + * @param string $dn The DN of the entry that contains the jpeg attribute you want to draw. + * @param string $attr_name The name of the attribute containing the jpeg data (usually 'jpegPhoto'). + * @param bool $draw_delete_buttons If true, draws a button beneath the image titled 'Delete' allowing the user + * to delete the jpeg attribute by calling JavaScript function deleteAttribute() provided + * in the default modification template. + * @param bool $draw_bytes_and_size If true, draw text below the image indicating the byte size and dimensions. + * @param string $table_html_attrs Specifies optional CSS style attributes for the table tag. + * + * @return void + */ +function draw_jpeg_photos( $dn, $attr_name='jpegPhoto', $draw_delete_buttons=false, + $draw_bytes_and_size=true, $table_html_attrs='align="left"', $img_html_attrs='' ) +{ + $jpeg_temp_dir = $_SESSION['lampath'] . 'tmp'; + + $conn = $_SESSION['ldap']->server; + $search_result = ldap_read( $conn, $dn, 'objectClass=*', array( $attr_name ) ); + $entry = ldap_first_entry( $conn, $search_result ); + + echo "
\n\n"; + // for each jpegPhoto in the entry, draw it (there may be only one, and that's okay) + $jpeg_data = @ldap_get_values_len( $conn, $entry, $attr_name ); + if( ! is_array( $jpeg_data ) ) { + echo "Could not fetch jpeg data from LDAP server for attribute " . htmlspecialchars( $attr_name ); + return; + } + for( $i=0; $i<$jpeg_data['count']; $i++ ) + { + // ensures that the photo is written to the specified jpeg_temp_dir + $jpeg_temp_dir = realpath($jpeg_temp_dir.'/'); + $jpeg_filename = $jpeg_temp_dir . '/' . 'jpg' . $_SESSION['ldap']->new_rand() . '.jpg'; + $outjpeg = @fopen($jpeg_filename, "wb"); + fwrite($outjpeg, $jpeg_data[$i]); + fclose ($outjpeg); + $jpeg_data_size = filesize( $jpeg_filename ); + if( $jpeg_data_size < 6 && $draw_delete_buttons ) { + echo _('jpegPhoto contains errors'); + echo '
'. _('Delete') .''; + continue; + } + + if( function_exists( 'getimagesize' ) ) { + $jpeg_dimensions = @getimagesize( $jpeg_filename ); + $width = $jpeg_dimensions[0]; + $height = $jpeg_dimensions[1]; + } else { + $width = 0; + $height = 0; + } + if( $width > 300 ) { + $scale_factor = 300 / $width; + $img_width = 300; + $img_height = $height * $scale_factor; + } else { + $img_width = $width; + $img_height = $height; + } + echo "
\n"; + if( $draw_bytes_and_size ) { + echo "" . number_format($jpeg_data_size) . " bytes. "; + echo "$width x $height pixels.
\n\n"; + } + + if( $draw_delete_buttons ) + { ?> + + Delete Photo +
\n\n"; +} + +/** + * A handy ldap searching function very similar to PHP's ldap_search() with the + * following exceptions: Callers may specify a search scope and the return value + * is an array containing the search results rather than an LDAP result resource. + * + * Example usage: + * + * $samba_users = ldap_search( 0, "(&(objectClass=sambaAccount)(objectClass=posixAccount))", + * "ou=People,dc=example,dc=com", array( "uid", "homeDirectory" ) ); + * print_r( $samba_users ); + * // prints (for example): + * // Array + * // ( + * // [uid=jsmith,ou=People,dc=example,dc=com] => Array + * // ( + * // [dn] => "uid=jsmith,ou=People,dc=example,dc=com" + * // [uid] => "jsmith" + * // [homeDirectory] => "\\server\jsmith" + * // ) + * // [uid=byoung,ou=People,dc=example,dc=com] => Array + * // ( + * // [dn] => "uid=byoung,ou=Samba,ou=People,dc=example,dc=com" + * // [uid] => "byoung" + * // [homeDirectory] => "\\server\byoung" + * // ) + * // ) + * + * + * WARNING: This function will use a lot of memory on large searches since the entire result set is + * stored in a single array. For large searches, you should consider sing the less memory intensive + * PHP LDAP API directly (ldap_search(), ldap_next_entry(), ldap_next_attribute(), etc). + * + * @param string $filter The LDAP filter to use when searching (example: "(objectClass=*)") (see RFC 2254) + * @param string $base_dn The DN of the base of search. + * @param array $attrs An array of attributes to include in the search result (example: array( "objectClass", "uid", "sn" )). + * @param string $scope The LDAP search scope. Must be one of "base", "one", or "sub". Standard LDAP search scope. + * @param bool $sort_results Specify false to not sort results by DN or true to have the + * returned array sorted by DN (uses ksort) + * @param int $deref When handling aliases or referrals, this specifies whether to follow referrals. Must be one of + * LDAP_DEREF_ALWAYS, LDAP_DEREF_NEVER, LDAP_DEREF_SEARCHING, or LDAP_DEREF_FINDING. See the PHP LDAP API for details. + */ +function pla_ldap_search( $filter, $base_dn=null, $attrs=array(), $scope='sub', $sort_results=true, $deref=LDAP_DEREF_ALWAYS ) +{ + $ds = $_SESSION['ldap']->server; + switch( $scope ) { + case 'base': + $search = @ldap_read( $ds, $base_dn, $filter, $attrs, 0, 0, 0, $deref ); + break; + case 'one': + $search = @ldap_list( $ds, $base_dn, $filter, $attrs, 0, 0, 0, $deref ); + break; + case 'sub': + default: + $search = @ldap_search( $ds, $base_dn, $filter, $attrs, 0, 0, 0, $deref ); + break; + } + + if( ! $search ) + return array(); + + $return = array(); + //get the first entry identifier + if( $entry_id = ldap_first_entry($ds,$search) ) + + //iterate over the entries + while($entry_id) { + + //get the distinguished name of the entry + $dn = ldap_get_dn($ds,$entry_id); + + //get the attributes of the entry + $attrs = ldap_get_attributes($ds,$entry_id); + $return[$dn]['dn'] = $dn; + + //get the first attribute of the entry + if($attr = ldap_first_attribute($ds,$entry_id,$attrs)) + + //iterate over the attributes + while($attr){ + if( is_attr_binary($attr)) + $values = ldap_get_values_len($ds,$entry_id,$attr); + else + $values = ldap_get_values($ds,$entry_id,$attr); + + //get the number of values for this attribute + $count = $values['count']; + unset($values['count']); + if($count==1) + $return[$dn][$attr] = $values[0]; + else + $return[$dn][$attr] = $values; + + $attr = ldap_next_attribute($ds,$entry_id,$attrs); + }// end while attr + + $entry_id = ldap_next_entry($ds,$entry_id); + + } // end while entry_id + + if( $sort_results && is_array( $return ) ) + ksort( $return ); + + return $return; +} + +/** + * Given a DN string, this returns the parent container portion of the string. + * For example. given 'cn=Manager,dc=example,dc=com', this function returns + * 'dc=example,dc=com'. + * + * @param string $dn The DN whose container string to return. + * + * @return string The container + * @see get_rdn + */ +function get_container( $dn ) +{ + $parts = pla_explode_dn( $dn ); + if( count( $parts ) <= 1 ) + return null; + $container = $parts[1]; + for( $i=2; $i diff --git a/lam/session-vars.txt b/lam/session-vars.txt index 28bb0684..e4c83a47 100644 --- a/lam/session-vars.txt +++ b/lam/session-vars.txt @@ -63,6 +63,7 @@ convsave, confmain, conflogin: - conf_suffgroups: Eingabe von confmain - conf_suffhosts: Eingabe von confmain - conf_suffdomains: Eingabe von confmain +- conf_sufftree: Eingabe von confmain - conf_usrlstattr: Eingabe von confmain - conf_grplstattr: Eingabe von confmain - conf_hstlstattr: Eingabe von confmain diff --git a/lam/style/500_layout.css b/lam/style/500_layout.css index d1a8981a..10eb6611 100644 --- a/lam/style/500_layout.css +++ b/lam/style/500_layout.css @@ -57,6 +57,10 @@ color:red; text-decoration:none; } +a img { + border: 0px; +} + form { } input { font-weight:bold; @@ -597,3 +601,298 @@ h4.schema_oclass_sub { font-size: 80%; } +table.tree tr.login td { +} + +table.tree td.links { + padding: 0px; + font-size: 10px; + padding-left: 12px; + font-size: 10px; +} + +table.tree td.links a { + font-size: 10px; +} + +table.tree { + border: 0px; +} + +table.tree img { + border: 0px; +} + +table.tree td { + padding: 2px; + border: 0px solid black; +} + +table.tree tr { +} + +table.tree tr.server td.icon { + width: 14px; +} + +table.tree tr.server td { + padding-top: 15px; + padding-bottom: 0px; + vertical-align: top; + font-size: 20px; + text-align: left; +} + +table.tree td.icon { + text-align: center; + padding: 0px; + width: 14px; + font-size: 1px; +} + +table.tree td.rdn { + width: 500px; +} + +table.tree td.rdn a { + text-decoration: none; + color: black; +} + +table.tree td.rdn a:hover { + text-decoration: none; + color: blue; +} + +table.tree td.create { +} + +table.tree td.create a { + text-decoration: none; + color: black; +} + +table.tree td.create a:hover { + text-decoration: none; + color: blue; +} + + +table.tree td.spacer { + width: 22px; +} + +table.tree td.expander { + text-align: center; + width: 22px; + max-width: 22px; + min-width: 22px; +} + +table.tree td span.count { + color: gray; + font-size: 85%; +} + +h3.tree_title { + text-align: center; + margin: 0px; + padding: 10px; + color: white; + background-color: #018; + border: 1px solid black; + font-weight: normal; + font-size: 150%; +} + +h3.tree_subtitle { + text-align: center; + margin: 0px; + margin-bottom: 15px; + font-size: 75%; + color: white; + border-bottom: 1px solid black; + border-left: 1px solid black; + border-right: 1px solid black; + background: #018; + padding: 4px; + font-weight: normal; +} + +table.tree_edit_dn { + border-collapse: collapse; + border-spacing: 0px; + empty-cells: show; + width: 500px; +} + +table.tree_edit_dn input { + margin: 1px; +} + +table.tree_edit_dn input.val { + font-size: 14px; + width: 350px; + font-family: arial, helvetica, sans-serif + background-color: white; +} + +table.tree_edit_dn textarea.val { + font-size: 14px; + width: 350px; + font-family: arial, helvetica, sans-serif; + background-color: white; +} + +table.tree_edit_dn tr td { + padding: 4px; + padding-right: 0px; +} + +table.tree_edit_dn tr td.attr { + background-color: #eee; + vertical-align: top; +} + +table.tree_edit_dn tr td.heading { + border-top: 3px solid #ccc; + font-weight: bold; +} + +table.tree_edit_dn tr td.attr_note { + text-align: right; + background-color: #eee; +} + +table.tree_edit_dn tr td.attr a { + text-decoration: none; + color: black; +} + +table.tree_edit_dn tr td.attr a:hover { + text-decoration: underline; + color: #016; +} + +table.tree_edit_dn tr td.val { + text-align: left; + vertical-align: center; + padding-bottom: 10px; + padding-left: 80px; +} + + +/** When an attr is updated, it is highlighted to indicate such */ +table.tree_edit_dn tr.updated_attr td.attr { + border-top: 1px dashed green; + border-left: 1px dashed green; + background-color: #ded; +} + +table.tree_edit_dn tr.updated_attr td.attr_note { + border-top: 1px dashed green; + border-right: 1px dashed green; + background-color: #ded; +} + +/** An extra row that sits at the bottom of recently modified attrs to encase them in dashes */ +table.tree_edit_dn tr.updated_attr td.bottom { + border-top: 1px dashed green; +} + +/** Formatting for the value cell when it is the attribute that has been recently modified */ +table.tree_edit_dn tr.updated_attr td.val { + border-left: 1px dashed green; + border-right: 1px dashed green; +} + +/* Neede to prevent sub-tables (like the one in which jpegPhotos are displayed) + * from drawing borders as well. */ +table.tree_edit_dn tr.updated_attr td table td { + border: 0px; +} + +table.tree_edit_dn tr.updated_attr a { +} + +table.tree_edit_dn tr.mod_dn { + background: #def; +} + +table.tree_edit_dn tr.row1 { + background: #eee; +} + +table.tree_edit_dn tr.row2 { + background: #ccc; +} + +form.tree_edit_dn { + margin: 0px; + padding: 0px; +} + +table.tree_edit_dn_menu { + font-size: 75%; +} + +table.tree_edit_dn_menu td.icon { + width: 16px; + text-align: center; +} + +span.tree_hint { + font-size: small; + font-weight: normal; + color: #888; +} + +table.tree_confirm th { + background-color: #016; + padding: 5px; + color: white; + font-weight: normal; +} + +table.tree_confirm tr td { + padding: 4px; +} + +table.tree_confirm tr.spacer { + background-color: #ddd; +} + +table.tree_confirm tr.even { + background-color: #ccc; +} + +table.tree_confirm tr.odd { + background-color: #eee; +} + +table.tree_confirm tr td.heading { + text-align: right; + font-size: 75%; +} + +table.tree_confirm td.icon { + text-align: center; +} + +table.tree_delete_confirm { + width: 76%; + background-color: #ddf; + padding: 20px; + text-align: left; +} + +table.tree_create { + font-size: 75%; +} + +table.tree_create td.heading { + vertical-align: top; + padding: 10px; +} + + diff --git a/lam/templates/config/confmain.php b/lam/templates/config/confmain.php index 9797992c..41fd80b0 100644 --- a/lam/templates/config/confmain.php +++ b/lam/templates/config/confmain.php @@ -57,6 +57,7 @@ if (isset($_POST['back']) || isset($_POST['submitconf']) || isset($_POST['editmo $_SESSION['conf_suffgroups'] = $_POST['suffgroups']; $_SESSION['conf_suffhosts'] = $_POST['suffhosts']; $_SESSION['conf_suffdomains'] = $_POST['suffdomains']; + $_SESSION['conf_sufftree'] = $_POST['sufftree']; $_SESSION['conf_usrlstattr'] = $_POST['usrlstattr']; $_SESSION['conf_grplstattr'] = $_POST['grplstattr']; $_SESSION['conf_hstlstattr'] = $_POST['hstlstattr']; @@ -122,6 +123,7 @@ if (isset($_GET["modulesback"])) { $conf->set_GroupSuffix($_SESSION['conf_suffgroups']); $conf->set_HostSuffix($_SESSION['conf_suffhosts']); $conf->set_DomainSuffix($_SESSION['conf_suffdomains']); + $conf->set_Suffix('tree', $_SESSION['conf_sufftree']); $conf->set_userlistAttributes($_SESSION['conf_usrlstattr']); $conf->set_grouplistAttributes($_SESSION['conf_grplstattr']); $conf->set_hostlistAttributes($_SESSION['conf_hstlstattr']); @@ -167,28 +169,34 @@ echo (" "); // user suffix echo ("". - _("UserSuffix") . " *: ". + _("UserSuffix") . ":
". "get_UserSuffix() . "\">\n"); echo ("" . _("Help") . "\n"); $tabindex++; // group suffix echo ("". - _("GroupSuffix") . " *: ". + _("GroupSuffix") . ": ". "get_GroupSuffix() . "\">\n"); echo ("" . _("Help") . "\n"); $tabindex++; // host suffix echo ("". - _("HostSuffix") . " **: ". + _("HostSuffix") . ": ". "get_HostSuffix() . "\">\n"); echo ("" . _("Help") . "\n"); $tabindex++; // domain suffix echo ("". - _("DomainSuffix") . " ***: ". + _("DomainSuffix") . " **: ". "get_DomainSuffix() . "\">\n"); echo ("" . _("Help") . "\n"); $tabindex++; +// tree suffix +echo ("". + _("TreeSuffix") . ": ". + "get_Suffix('tree') . "\">\n"); +echo ("" . _("Help") . "\n"); +$tabindex++; // new line echo (" "); @@ -429,8 +437,7 @@ echo ("\n"); echo ("

"); echo ("

* = ". _("required") . "

"); -echo ("

** = ". _("required for Samba accounts") . "

"); -echo ("

*** = ". _("required for Samba 3 accounts") . "

"); +echo ("

** = ". _("required for Samba 3 accounts") . "

"); // password for configuration echo ("

\n"); diff --git a/lam/templates/config/confsave.php b/lam/templates/config/confsave.php index f7cca6ff..e60b734f 100644 --- a/lam/templates/config/confsave.php +++ b/lam/templates/config/confsave.php @@ -52,6 +52,7 @@ $suffusers = $_SESSION['conf_suffusers']; $suffgroups = $_SESSION['conf_suffgroups']; $suffhosts = $_SESSION['conf_suffhosts']; $suffdomains = $_SESSION['conf_suffdomains']; +$sufftree = $_SESSION['conf_sufftree']; $usrlstattr = $_SESSION['conf_usrlstattr']; $grplstattr = $_SESSION['conf_grplstattr']; $hstlstattr = $_SESSION['conf_hstlstattr']; @@ -122,6 +123,11 @@ if (!$conf->set_DomainSuffix($suffdomains)) { echo ("\n


" . _("Back to preferences...") . ""); exit; } +if (!$conf->set_Suffix("tree", $sufftree)) { + echo ("" . _("TreeSuffix is invalid!") . ""); + echo ("\n


" . _("Back to preferences...") . ""); + exit; +} if (!$conf->set_userlistAttributes($usrlstattr)) { echo ("" . _("User list attributes are invalid!") . ""); echo ("\n


" . _("Back to preferences...") . ""); @@ -260,6 +266,7 @@ unset($_SESSION['conf_suffusers']); unset($_SESSION['conf_suffgroups']); unset($_SESSION['conf_suffhosts']); unset($_SESSION['conf_suffdomains']); +unset($_SESSION['conf_sufftree']); unset($_SESSION['conf_usrlstattr']); unset($_SESSION['conf_grplstattr']); unset($_SESSION['conf_hstlstattr']); diff --git a/lam/templates/main_header.php b/lam/templates/main_header.php index a857257a..31c82a0c 100644 --- a/lam/templates/main_header.php +++ b/lam/templates/main_header.php @@ -44,6 +44,7 @@ $lists = 0; if ($_SESSION['config']->get_Usersuffix() != "") $lists++; if ($_SESSION['config']->get_Groupsuffix() != "") $lists++; if ($_SESSION['config']->get_Hostsuffix() != "") $lists++; +if ($_SESSION['config']->get_Suffix('tree') != "") $lists++; ?> @@ -73,6 +74,9 @@ if ($_SESSION['config']->get_Hostsuffix() != "") $lists++; get_Suffix('tree') != "") { + echo '' . _("Tree view") . '' . "\n"; + } if ($_SESSION['config']->get_Usersuffix() != "") { echo '' . _("Users") . '' . "\n"; } diff --git a/lam/templates/tree/add_attr.php b/lam/templates/tree/add_attr.php new file mode 100644 index 00000000..0ded677b --- /dev/null +++ b/lam/templates/tree/add_attr.php @@ -0,0 +1,166 @@ +LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage("ERROR", _('You left the attribute value blank. Please go back and try again.'), ''); + echo ""; + exit; +} + +// special case for binary attributes (like jpegPhoto and userCertificate): +// we must go read the data from the file and override $val with the binary data +// Secondly, we must check if the ";binary" option has to be appended to the name +// of the attribute. + +if( $is_binary_val ) { + if (( 0 == $_FILES['val']['size'] ) || (! is_uploaded_file( $_FILES['val']['tmp_name'] ))) { + echo $_SESSION['header']; + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage("ERROR", _('File upload failed!'), ''); + echo ""; + exit; + } + $file = $_FILES['val']['tmp_name']; + $f = fopen( $file, 'r' ); + $binary_data = fread( $f, filesize( $file ) ); + fclose( $f ); + $val = $binary_data; + + if( is_binary_option_required( $attr ) ) + $attr .=";binary"; +} + +// Automagically hash new userPassword attributes according to the +// chosen in config.php. +if( 0 == strcasecmp( $attr, 'userpassword' ) ) { + $val = pwd_hash($val); +} +elseif(0 == strcasecmp( $attr , 'sambalmpassword') ) { + $val = lmPassword($val); +} +elseif (0 == strcasecmp( $attr , 'sambantpassword' )) { + $val = ntPassword($val); +} + +$ds = $_SESSION['ldap']->server; +$new_entry = array( $attr => $val ); +$result = @ldap_mod_add( $ds, $dn, $new_entry ); + +if( $result ) + header( "Location: edit.php?dn=$encoded_dn&modified_attrs[]=$encoded_attr" ); +else { + echo $_SESSION['header']; + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage("ERROR", _('Failed to add the attribute.'), ldap_error($ds)); + echo ""; + exit; +} + +// check if we need to append the ;binary option to the name +// of some binary attribute + +function is_binary_option_required( $attr ){ + + // list of the binary attributes which need the ";binary" option + $binary_attributes_with_options = array( + // Superior: Ldapv3 Syntaxes (1.3.6.1.4.1.1466.115.121.1) + '1.3.6.1.4.1.1466.115.121.1.8' => "userCertificate", + '1.3.6.1.4.1.1466.115.121.1.8' => "caCertificate", + '1.3.6.1.4.1.1466.115.121.1.10' => "crossCertificatePair", + '1.3.6.1.4.1.1466.115.121.1.9' => "certificateRevocationList", + '1.3.6.1.4.1.1466.115.121.1.9' => "authorityRevocationList", + // Superior: Netscape Ldap attributes types (2.16.840.1.113730.3.1) + '2.16.840.1.113730.3.1.40' => "userSMIMECertificate" + ); + + // quick check by attr name (short circuits the schema check if possible) + //foreach( $binary_attributes_with_options as $oid => $name ) + //if( 0 == strcasecmp( $attr, $name ) ) + //return true; + + $schema_attr = get_schema_attribute( $attr ); + if( ! $schema_attr ) + return false; + + $syntax = $schema_attr->getSyntaxOID(); + if( isset( $binary_attributes_with_options[ $syntax ] ) ) + return true; + + return false; +} + +?> diff --git a/lam/templates/tree/add_attr_form.php b/lam/templates/tree/add_attr_form.php new file mode 100644 index 00000000..dbac2a35 --- /dev/null +++ b/lam/templates/tree/add_attr_form.php @@ -0,0 +1,198 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; + +$dn = $_GET['dn']; +$encoded_dn = rawurlencode( $dn ); +$rdn = get_rdn( $dn ); +?> + + + +

+

:

+ +getMustAttrNames( $schema_oclasses ), + $schema_oclass->getMayAttrNames( $schema_oclasses ), + $avail_attrs ); +} + +$avail_attrs = array_unique( $avail_attrs ); +$avail_attrs = array_filter( $avail_attrs, "not_an_attr" ); +sort( $avail_attrs ); + +$avail_binary_attrs = array(); +foreach( $avail_attrs as $i => $attr ) { + if( is_attr_binary( $attr ) ) { + $avail_binary_attrs[] = $attr; + unset( $avail_attrs[ $i ] ); + } +} + +?> + +
+
+ + + + + 0 ) { ?> + +
+
+
+ + + + + +
+ + +
+
+ +
+
+ + + + + 0 ) { ?> + +
+ + +
+ + + + " . _('Your PHP configuration has disabled file uploads. Please check php.ini before proceeding.') . "
"; + else + echo "
" . sprintf( _('Maximum file size: %s'), ini_get( 'upload_max_filesize' ) ) . "
"; + ?> +
+ + +
+
+ + + + +
+ + + + $values ) + if( 0 == strcasecmp( $attr, $x ) ) + return false; + return true; +} + + +?> diff --git a/lam/templates/tree/add_oclass.php b/lam/templates/tree/add_oclass.php new file mode 100644 index 00000000..485e4dc3 --- /dev/null +++ b/lam/templates/tree/add_oclass.php @@ -0,0 +1,97 @@ + $new_oclass ); + +if( is_array( $new_attrs ) && count( $new_attrs ) > 0 ) + foreach( $new_attrs as $attr => $val ) { + $new_entry[ $attr ] = $val; + } + +$ds = $_SESSION['ldap']->server; +$add_res = @ldap_mod_add( $ds, $dn, $new_entry ); + +if( ! $add_res ) +{ + echo $_SESSION['header']; + + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage('ERROR', _("Was unable to modify attribtues from DN: %s."), ldap_error( $ds ), array($dn)); + echo ""; +} +else +{ + header( "Location: edit.php?dn=$encoded_dn&modified_attrs[]=objectclass" ); +} + +?> diff --git a/lam/templates/tree/add_oclass_form.php b/lam/templates/tree/add_oclass_form.php new file mode 100644 index 00000000..936a23fb --- /dev/null +++ b/lam/templates/tree/add_oclass_form.php @@ -0,0 +1,163 @@ + $junk ) + $current_attrs[] = strtolower($attr); + +// grab the required attributes for the new objectClass +$oclass = get_schema_objectclass( $new_oclass ); +if( $oclass ) + $must_attrs = $oclass->getMustAttrs(); +else + $must_attrs = array(); + +// We don't want any of the attr meta-data, just the string +//foreach( $must_attrs as $i => $attr ) + //$must_attrs[$i] = $attr->getName(); + +// build a list of the attributes that this new objectClass requires, +// but that the object does not currently contain +$needed_attrs = array(); +foreach( $must_attrs as $attr ) { + $attr = get_schema_attribute( $attr->getName() ); + //echo "
"; var_dump( $attr ); echo "
"; + // First, check if one of this attr's aliases is already an attribute of this entry + foreach( $attr->getAliases() as $alias_attr_name ) + if( in_array( strtolower( $alias_attr_name ), $current_attrs ) ) + // Skip this attribute since it's already in the entry + continue; + if( in_array( strtolower($attr->getName()), $current_attrs ) ) + continue; + + // We made it this far, so the attribute needs to be added to this entry in order + // to add this objectClass + $needed_attrs[] = $attr; +} + +if( count( $needed_attrs ) > 0 ) +{ + echo $_SESSION['header']; + + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + ?> + + +

+

+ +
+ +
+ + + + + + + $attr ) { ?> + + + + +
getName()); ?>
+
+
+
+
+ + + + + server; + $add_res = @ldap_mod_add( $ds, $dn, array( 'objectClass' => $new_oclass ) ); + if( ! $add_res ) { + echo $_SESSION['header']; + + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage('ERROR', _("Was unable to modify attribtues from DN: %s."), ldap_error( $ds ), array($dn)); + echo ""; + } + else + header( "Location: edit.php?dn=$encoded_dn&modified_attrs[]=objectClass" ); + +} + +?> diff --git a/lam/templates/tree/add_value.php b/lam/templates/tree/add_value.php new file mode 100644 index 00000000..6f38ed04 --- /dev/null +++ b/lam/templates/tree/add_value.php @@ -0,0 +1,100 @@ +server; + +// special case for binary attributes: +// we must go read the data from the file. +if( $is_binary_val ) +{ + $file = $_FILES['new_value']['tmp_name']; + $f = fopen( $file, 'r' ); + $binary_value = fread( $f, filesize( $file ) ); + fclose( $f ); + $new_value = $binary_value; +} + +$new_entry = array( $attr => $new_value ); + +$add_result = @ldap_mod_add( $ds, $dn, $new_entry ); + +if( ! $add_result ) { + echo $_SESSION['header']; + + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage('ERROR', _('Adding attribute failed!'), ldap_error( $ds )); + echo ""; + exit; +} + +header( "Location: edit.php?dn=$encoded_dn&modified_attrs[]=$encoded_attr" ); + +?> diff --git a/lam/templates/tree/add_value_form.php b/lam/templates/tree/add_value_form.php new file mode 100644 index 00000000..a6db1bd0 --- /dev/null +++ b/lam/templates/tree/add_value_form.php @@ -0,0 +1,203 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; + +$dn = isset( $_GET['dn'] ) ? $_GET['dn'] : null; +$encoded_dn = rawurlencode( $dn ); +if( null != $dn ) { + $rdn = get_rdn( $dn ); +} else { + $rdn = null; +} +$attr = $_GET['attr']; +$encoded_attr = rawurlencode( $attr ); +$current_values = get_object_attr( $dn, $attr ); +$num_current_values = ( is_array($current_values) ? count($current_values) : 1 ); +$is_object_class = ( 0 == strcasecmp( $attr, 'objectClass' ) ) ? true : false; +$is_jpeg_photo = is_jpeg_photo( $attr ); //( 0 == strcasecmp( $attr, 'jpegPhoto' ) ) ? true : false; + +if( $is_object_class ) { + // fetch all available objectClasses and remove those from the list that are already defined in the entry + $schema_oclasses = get_schema_objectclasses(); + foreach( $current_values as $oclass ) + unset( $schema_oclasses[ strtolower( $oclass ) ] ); +} else { + $schema_attr = get_schema_attribute( $attr ); +} + +?> + + + +

+ + +

+

+ :

+ +" . htmlspecialchars($attr); ?> + + + + +

+ +

+ + + + + + + + + +
+
+ + + +
+ + + +
+ + + + > + + + + + + + + + + + getMaxLength() ) + echo "maxlength=\"" . $schema_attr->getMaxLength() . "\" "; + ?>name="new_value" size="40" value="" /> + + + + +
+ + getDescription() ) { ?> + : getDescription(); ?>
+ + + getType() ) { ?> + : getType(); ?>
+ + + getMaxLength() ) { ?> + : getMaxLength() ); ?>
+ + +
+ + + + + diff --git a/lam/templates/tree/collapse.php b/lam/templates/tree/collapse.php new file mode 100644 index 00000000..b8fcae4a --- /dev/null +++ b/lam/templates/tree/collapse.php @@ -0,0 +1,75 @@ + diff --git a/lam/templates/tree/create.php b/lam/templates/tree/create.php new file mode 100644 index 00000000..6ad3d7bb --- /dev/null +++ b/lam/templates/tree/create.php @@ -0,0 +1,185 @@ + $val ) { + if( $val == '' ) { + echo $_SESSION['header']; + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + + StatusMessage("ERROR", _('You left the value blank for required attribute: %s.'), '', array(htmlspecialchars($attr))); + + echo ""; + exit; + } + $new_entry[ $attr ][] = $val; + } +} + +if( isset( $attrs ) && is_array( $attrs ) ) { + foreach( $attrs as $i => $attr ) { + if( is_attr_binary( $attr ) ) { + if( isset( $_FILES['vals']['name'][$i] ) && $_FILES['vals']['name'][$i] != '' ) { + // read in the data from the file + $file = $_FILES['vals']['tmp_name'][$i]; + $f = fopen( $file, 'r' ); + $binary_data = fread( $f, filesize( $file ) ); + fclose( $f ); + $val = $binary_data; + $new_entry[ $attr ][] = $val; + } + } else { + $val = isset( $vals[$i] ) ? $vals[$i] : ''; + if( '' !== trim($val) ) + $new_entry[ $attr ][] = $val; + } + } +} + +$new_entry['objectClass'] = $object_classes; +if( ! in_array( 'top', $new_entry['objectClass'] ) ) + $new_entry['objectClass'][] = 'top'; + +foreach( $new_entry as $attr => $vals ) { + if( ! is_attr_binary( $attr ) ) + if( is_array( $vals ) ) { + foreach( $vals as $i => $v ) { + $new_entry[ $attr ][ $i ] = $v; + } + } + else { + $new_entry[ $attr ] = $vals; + } +} + +//echo "
"; var_dump( $new_dn );print_r( $new_entry ); echo "
"; + +$ds = $_SESSION['ldap']->server; + +// Check the user-defined custom call back first +$add_result = @ldap_add( $ds, $new_dn, $new_entry ); +if( $add_result ) +{ + $redirect_url = "edit.php?dn=" . rawurlencode( $new_dn ); + + if( array_key_exists( 'tree', $_SESSION ) ) + { + $tree = $_SESSION['tree']; + $tree_icons = $_SESSION['tree_icons']; + + if( isset( $tree[$container] ) ) { + $tree[$container][] = $new_dn; + sort( $tree[$container] ); + $tree_icons[$new_dn] = get_icon( $new_dn ); + } + + $_SESSION['tree'] = $tree; + $_SESSION['tree_icons'] = $tree_icons; + session_write_close(); + } + + ?> + \n"; + echo "\n"; + echo "\n"; + echo "\n"; + echo ""; + // print link if refresh does not work + echo "

\n"; + echo "" . _("Click here if you are not directed to the next page.") . "\n"; + echo "

\n"; + echo "\n"; + echo "\n"; + } + ?> + +LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + + StatusMessage("ERROR", _("LAM was unable to create account %s! An LDAP error occured."), ldap_error($ds), array($new_dn)); + + echo "\n"; + } + +?> diff --git a/lam/templates/tree/create_form.php b/lam/templates/tree/create_form.php new file mode 100644 index 00000000..185bf10c --- /dev/null +++ b/lam/templates/tree/create_form.php @@ -0,0 +1,145 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +?> + + + +

+

+

+
+ + + + + + + + + + + +
+ : + + + + + +
+ + $template ) { + $i++; + // Balance the columns properly + if( ( count( $templates ) % 2 == 0 && $i == intval( $count / 2 ) ) || + ( count( $templates ) % 2 == 1 && $i == intval( $count / 2 ) + 1 ) ) + echo "
"; + // Check and see if this template should be shown in the list + $isValid = false; + if( isset($template['regexp'] ) ) { + if( @preg_match( "/".$template['regexp']."/i", $container ) ) { + $isValid = true; + } + } else { + $isValid = true; + } + + ?> + + + + + + + +
/> + +
+
+
+ +
+ + + diff --git a/lam/templates/tree/creation_template.php b/lam/templates/tree/creation_template.php new file mode 100644 index 00000000..108086b2 --- /dev/null +++ b/lam/templates/tree/creation_template.php @@ -0,0 +1,96 @@ + $template ) { + if( $template['handler'] == 'custom.php' ) { + $template = $id; + break; + } + } +} + +$template_id = $template; +$template = isset( $templates[$template] ) ? $templates[$template_id] : null; +$ds = $_SESSION['ldap']->server; + +echo $_SESSION['header']; + +echo "LDAP Account Manager\n"; +echo "\n"; +echo "\n"; + +?> + + +

+

''

+ +\n"; + +?> diff --git a/lam/templates/tree/delete.php b/lam/templates/tree/delete.php new file mode 100644 index 00000000..73dcfb2e --- /dev/null +++ b/lam/templates/tree/delete.php @@ -0,0 +1,112 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +echo ""; + +$dn = $_POST['dn']; +$encoded_dn = rawurlencode( $dn ); + +$ds = $_SESSION['ldap']->server; + +$del_result = @ldap_delete( $ds, $dn ); + +if( $del_result ) +{ + // kill the DN from the tree browser session variable and + // refresh the tree viewer frame (left_frame) + + if( array_key_exists( 'tree', $_SESSION ) ) + { + $tree = $_SESSION['tree']; + if( isset( $tree ) && is_array( $tree ) ) { + + // does it have children? (it shouldn't, but hey, you never know) + if( isset( $tree[$dn] ) ) + unset( $tree[$dn] ); + + // search and destroy + foreach( $tree as $tree_dn => $subtree ) + foreach( $subtree as $key => $sub_tree_dn ) + if( 0 == strcasecmp( $sub_tree_dn, $dn ) ) + unset( $tree[$tree_dn][$key] ); + $_SESSION['tree'] = $tree; + } + } + + ?> + + + +
+
+
+ + "; diff --git a/lam/templates/tree/delete_attr.php b/lam/templates/tree/delete_attr.php new file mode 100644 index 00000000..7d2c47c8 --- /dev/null +++ b/lam/templates/tree/delete_attr.php @@ -0,0 +1,83 @@ +server; +$res = @ldap_modify( $ds, $dn, $update_array ); +if( $res ) { + $redirect_url = "edit.php?dn=$encoded_dn"; + foreach( $update_array as $attr => $junk ) { + $redirect_url .= "&modified_attrs[]=$attr"; + } + header( "Location: $redirect_url" ); +} +else { + echo $_SESSION['header']; + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage("ERROR", _('Could not perform ldap_modify operation.'), ldap_error($ds)); + echo ""; + exit; +} + +?> diff --git a/lam/templates/tree/delete_form.php b/lam/templates/tree/delete_form.php new file mode 100644 index 00000000..cda914dc --- /dev/null +++ b/lam/templates/tree/delete_form.php @@ -0,0 +1,183 @@ +0 ? true : false; + +echo $_SESSION['header']; + +echo "LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +?> + + + +

+

:

+ + + +


+ + + + + +
+ +

+ +
+
+ +
+
+ +
+
+ + + + + + +
+
+
+ + +
+
+
+
+ + +
+
+
+

+ +
+
+
+ + +
+ + + +
+ + + +
+ +
+
+:
+
+ + + + + + +
+
+
+ + +
+ +
+
+
+ + +
+
+
+ +
+ +
+ + + + + + diff --git a/lam/templates/tree/download_binary_attr.php b/lam/templates/tree/download_binary_attr.php new file mode 100644 index 00000000..26b413e9 --- /dev/null +++ b/lam/templates/tree/download_binary_attr.php @@ -0,0 +1,87 @@ +server; + +$search = @ldap_read( $ds, $dn, "(objectClass=*)", array($attr)); +if( ! $search ) { + echo $_SESSION['header']; + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + StatusMessage("ERROR", _('Encountered an error while performing search.'), ldap_error($ds)); + echo ""; + exit; +} +$entry = ldap_first_entry( $ds, $search ); +$attrs = ldap_get_attributes( $ds, $entry ); +$attr = ldap_first_attribute( $ds, $entry, $attrs ); +$values = ldap_get_values_len( $ds, $entry, $attr ); +$count = $values['count']; + +// Dump the binary data to the browser +header( "Content-type: octet-stream" ); +header( "Content-disposition: attachment; filename=$attr" ); +header( "Expires: Mon, 26 Jul 1997 05:00:00 GMT" ); +header( "Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT" ); +echo $values[$value_num]; + +?> diff --git a/lam/templates/tree/edit.php b/lam/templates/tree/edit.php new file mode 100644 index 00000000..d72021fe --- /dev/null +++ b/lam/templates/tree/edit.php @@ -0,0 +1,544 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +?> + + +

+

:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 ) { ?> + + + + + + $attr ) { + $modified_attrs[$i] = strtolower( $attr ); + } +} +?> + +
+
">">
+ + + +
+ + + $vals ) { + $counter++; + $schema_href = "../schema/schema.php?view=attributes&viewvalue=" . real_attr_name($attr); + ?> + + + + + \n"; +} + +?> + + + + + + + + + + +\n"; + echo "
+ + +
+ 1 ) { for( $i=1; $i<=count($vals); $i++ ) { ?> + "> ()
+ +
+ \n"; + } + } ?> +
(" . _('No internal attributes') . ")
(" . _('This entry has no attributes') . ")
"; + echo ""; + die(); +} + +uksort( $attrs, 'sortAttrs' ); +foreach( $attrs as $attr => $vals ) { + + flush(); + + $schema_attr = get_schema_attribute( $attr, $dn ); + if( $schema_attr ) + $attr_syntax = $schema_attr->getSyntaxOID(); + else + $attr_syntax = null; + + if( 0 == strcasecmp( $attr, 'dn' ) ) + continue; + + // Setup the $attr_note, which will be displayed to the right of the attr name (if any) + $attr_note = ''; + + $attr_note = ""; + $attr_display = $attr; + + // is this attribute required by an objectClass? + $required_by = ''; + if( $schema_attr ) + foreach( $schema_attr->getRequiredByObjectClasses() as $required ) + if( in_array( strtolower( $required ), arrayLower( $attrs['objectClass'] ) ) ) { + $required_by .= $required . ' '; + } + if( $required_by ) { + if( trim( $attr_note ) ) + $attr_note .= ', '; + $attr_note .= "" . _('required') . " "; + } + ?> + + + + + + + + + + + + + + + + + + + + + + + + + + + \n"; + if( $is_modified_attr ) + echo ''; + continue; + } + + + /* + * Is this attribute binary? + */ + if( is_attr_binary( $attr ) ) { + $href = "download_binary_attr.php?dn=$encoded_dn&attr=$attr"; + ?> + + +
+ 1 ) { for( $i=1; $i<=count($vals); $i++ ) { ?> + "> ()
+ +
+ + + + +
+ + + + '; + continue; + } + + + /* + * Note: at this point, the attribute must be text-based (not binary or jpeg) + */ + + + /* + * Is this a userPassword attribute? + */ + if( 0 == strcasecmp( $attr, 'userpassword' ) ) { + $user_password = $vals[0]; + $enc_type = get_enc_type( $user_password ); + + // Set the default hashing type if the password is blank (must be newly created) + if( $user_password == '' ) { + $enc_type = get_default_hash(); + } + ?> + + + + + + +
+ + + + +
+ + + + '; + continue; + } + + /* + * Is this a boolean attribute? + */ + if( is_attr_boolean( $attr) ) { + $val = $vals[0]; + ?> + + + + + + + + '; + continue; + } + + /* + * End of special case attributes (non plain text). + */ + + + /* + * This is a plain text attribute, to be displayed and edited in plain text. + */ + foreach( $vals as $i => $val ) { + + $input_name = "new_values[" . htmlspecialchars( $attr ) . "][$i]"; + // We smack an id="..." tag in here that doesn't have [][] in it to allow the + // draw_chooser_link() to identify it after the user clicks. + $input_id = "new_values_" . htmlspecialchars($attr) . "_" . $i; + + ?> + + + + + + type == 'structural') { + echo "$val (" . + _('structural') . ")
"; + ?> + + + + + + + + + + + + + + + + + + + +
+ + + + getIsSingleValue() ) + { + $add_href = "add_value_form.php?dn=$encoded_dn&attr=" . rawurlencode( $attr ); + echo "\n"; + } + + ?> + + + + + + + + + $vals ) */ ?> + +
+ + + + + + + + + + +
+ + +
+ +\n"; +?> diff --git a/lam/templates/tree/expand.php b/lam/templates/tree/expand.php new file mode 100644 index 00000000..1be466fd --- /dev/null +++ b/lam/templates/tree/expand.php @@ -0,0 +1,88 @@ + diff --git a/lam/templates/tree/rdelete.php b/lam/templates/tree/rdelete.php new file mode 100644 index 00000000..d562037f --- /dev/null +++ b/lam/templates/tree/rdelete.php @@ -0,0 +1,154 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; + +$dn = $_POST['dn']; +$encoded_dn = rawurlencode( $dn ); +$rdn = get_rdn( $dn ); + +$ds = $_SESSION['ldap']->server; + +echo "\n"; +echo "

" . sprintf( _('Deleting %s'), htmlspecialchars($rdn) ) . "

\n"; +echo "

" . _('Recursive delete progress') . "

"; +echo "

"; +echo "\n"; +flush(); + +// prevent script from bailing early on a long delete +@set_time_limit( 0 ); + +$del_result = pla_rdelete( $dn ); +echo "
\n"; +if( $del_result ) +{ + // kill the DN from the tree browser session variable and + // refresh the tree viewer frame (left_frame) + + if( array_key_exists( 'tree', $_SESSION ) ) + { + $tree = $_SESSION['tree']; + + // does it have children? (it shouldn't, but hey, you never know) + if( isset( $tree[$dn] ) ) + unset( $tree[$dn] ); + + // Get a tree in the session if not already gotten + initialize_session_tree(); + + // search and destroy from the tree sesssion + foreach( $tree as $tree_dn => $subtree ) + foreach( $subtree as $key => $sub_tree_dn ) + if( 0 == strcasecmp( $sub_tree_dn, $dn ) ) + unset( $tree[$tree_dn][$key] ); + } + + $_SESSION['tree'] = $tree; + + ?> + + + + ' . htmlspecialchars( $dn ) . '' ); + +} else { + StatusMessage("ERROR", _('Failed to delete entry %s'), '', array(htmlspecialchars($dn))); +} + +echo ""; + +exit; + + +function pla_rdelete( $dn ) +{ + $children = get_container_contents( $dn ); + global $ds; + $ds = $_SESSION['ldap']->server; + + if( ! is_array( $children ) || count( $children ) == 0 ) { + echo "" . sprintf( _('Deleting %s'), htmlspecialchars( $dn ) ) . "..."; + flush(); + if( @ldap_delete( $ds, $dn ) ) { + echo " " . _('Success') . "
\n"; + return true; + } else { + StatusMessage("ERROR", _('Failed to delete entry %s'), '', array(htmlspecialchars($dn))); + } + } else { + foreach( $children as $child_dn ) { + pla_rdelete( $child_dn ); + } + echo "" . sprintf( _('Deleting %s'), htmlspecialchars( $dn ) ) . "..."; + flush(); + if( @ldap_delete( $ds, $dn ) ) { + echo " " . _('Success') . "
\n"; + return true; + } else { + StatusMessage("ERROR", _('Failed to delete entry %s'), '', array(htmlspecialchars($dn))); + } + } + return false; +} diff --git a/lam/templates/tree/refresh.php b/lam/templates/tree/refresh.php new file mode 100644 index 00000000..ff1bd023 --- /dev/null +++ b/lam/templates/tree/refresh.php @@ -0,0 +1,89 @@ +get_Suffix('tree'); +$tree_icons[ $base_dn ] = get_icon( $base_dn ); + +// get all the icons and container contents for all expanded entries +if( isset($tree) && is_array( $tree ) ) +{ + foreach( $tree as $dn => $children ) + { + $tree[$dn] = get_container_contents( $dn, 0, '(objectClass=*)' ); + if( is_array( $tree[$dn] ) ) { + foreach( $tree[$dn] as $child_dn ) + $tree_icons[$child_dn] = get_icon( $child_dn ); + sort( $tree[ $dn ] ); + } + } +} +else +{ + header( "Location: tree.php" ); +} + +$_SESSION['tree'] = $tree; +$_SESSION['tree_icons'] = $tree_icons; +session_write_close(); + +header( "Location: tree.php" ); + + +?> diff --git a/lam/templates/tree/templates/creation/custom.php b/lam/templates/tree/templates/creation/custom.php new file mode 100644 index 00000000..08e49039 --- /dev/null +++ b/lam/templates/tree/templates/creation/custom.php @@ -0,0 +1,311 @@ + + +

+ +
+ + + + + + + + + + + + + + + + + + + + + +
:
+ +
+
+ + "; + exit; + } + + $oclasses = isset( $_POST['object_classes'] ) ? $_POST['object_classes'] : null; + if( count( $oclasses ) == 0 ) { + StatusMessage("ERROR", _('You did not select any object classes for this object. Please go back and do so.'), ''); + echo ""; + exit; + } + $dn = trim( $container ) ? $rdn . ',' . $container : $rdn; + + // incrementally build up the all_attrs and required_attrs arrays + $schema_oclasses = get_schema_objectclasses(); + $required_attrs = array(); + $all_attrs = array(); + foreach( $oclasses as $oclass_name ) { + $oclass = get_schema_objectclass( $oclass_name ); + if( $oclass ) { + $required_attrs = array_merge( $required_attrs, + $oclass->getMustAttrNames( $schema_oclasses ) ); + $all_attrs = array_merge( $all_attrs, + $oclass->getMustAttrNames( $schema_oclasses ), + $oclass->getMayAttrNames( $schema_oclasses ) ); + } + } + + $required_attrs = array_unique( $required_attrs ); + $all_attrs = array_unique( $all_attrs ); + remove_aliases( $required_attrs); + remove_aliases( $all_attrs); + sort( $required_attrs ); + sort( $all_attrs ); + + // if for some reason "ObjectClass" ends up in the list of + // $all_attrs or $required_attrs, remove it! This is a fix + // for bug 927487 + foreach( $all_attrs as $i => $attr_name ) + if( 0 == strcasecmp( $attr_name, 'objectClass' ) ) { + unset( $all_attrs[$i] ); + $all_attrs = array_values( $all_attrs ); + break; + } + + foreach( $required_attrs as $i => $attr_name ) + if( 0 == strcasecmp( $attr_name, 'objectClass' ) ) { + unset( $required_attrs[$i] ); + $required_attrs = array_values( $required_attrs ); + break; + } + + // remove binary attributes and add them to the binary_attrs array + $binary_attrs = array(); + foreach( $all_attrs as $i => $attr_name ) { + if( is_attr_binary( $attr_name ) ) { + unset( $all_attrs[ $i ] ); + $binary_attrs[] = $attr_name; + } + } + + // If we trim any attrs out above, then we will have a gap in the index + // sequence and will get an "undefined index" error below. This prevents + // that from happening. + $all_attrs = array_values( $all_attrs ); + + // add the required attribute based on the RDN provided by the user + // (ie, if the user specifies "cn=Bob" for their RDN, make sure "cn" is + // in the list of required attributes. + $rdn_attr = trim( substr( $rdn, 0, strpos( $rdn, '=' ) ) ); + $rdn_value = trim( substr( $rdn, strpos( $rdn, '=' ) + 1 ) ); + $rdn_value = @pla_explode_dn( $rdn ); + $rdn_value = @explode( '=', $rdn_value[0], 2 ); + $rdn_value = @$rdn_value[1]; + if( in_array( $rdn_attr, $all_attrs ) && ! in_array( $rdn_attr, $required_attrs ) ) + $required_attrs[] = $rdn_attr; + ?> + +

+ +
+ + + + + + + + + \n"; + } else + + foreach( $required_attrs as $count => $attr ) { ?> + + + + + + + + + + + + + + + + + + + + + + 0 ) { ?> + + + + + + + + + +
(" . _('none') . ")
" + name="required_attrs[]" + value="" size="40" /> +
 
()
+
+
+ +
+
+ +$attr_display\n"; + } + return $attr_select_html; +} + +/** +* Returns option values. +*/ +function get_binary_attr_select_html( $binary_attrs, $highlight_attr=null ) +{ + $binary_attr_select_html = ""; + if( ! is_array( $binary_attrs ) ) + return null; + if( count( $binary_attrs ) == 0 ) + return null; + foreach( $binary_attrs as $a ) { + $attr_display = htmlspecialchars( $a ); + $binary_attr_select_html .= ""; + $attr1 = get_schema_attribute( $attr_name1 ); + if( null == $attr1 ) + continue; + if( $attr1->isAliasFor( $attr_name2 ) ) { + //echo "* Removing attribute ". $attribute_list[ $k ] . "
"; + unset( $attribute_list[ $k ] ); + } + } + } + $attribute_list = array_values( $attribute_list ); +} +?> + diff --git a/lam/templates/tree/templates/creation/new_ou_template.php b/lam/templates/tree/templates/creation/new_ou_template.php new file mode 100644 index 00000000..2d039c84 --- /dev/null +++ b/lam/templates/tree/templates/creation/new_ou_template.php @@ -0,0 +1,111 @@ + + +

+ + + + + + + +
+ + + + + + + + + + + + + + +
:
: +

+
+ + +"; + exit(); + } + + ?> +
+ + + + + + + + + + + +
+
+
+ + + + +
+
+
+ + + diff --git a/lam/templates/tree/templates/templates.inc b/lam/templates/tree/templates/templates.inc new file mode 100644 index 00000000..c1da11c6 --- /dev/null +++ b/lam/templates/tree/templates/templates.inc @@ -0,0 +1,56 @@ + 'Organizational Unit', + 'icon' => '../../graphics/ou.png', + 'handler' => 'new_ou_template.php'); + +$templates[] = array( + 'desc' => 'Custom', + 'icon' => '../../graphics/object.png', + 'handler' => 'custom.php'); + + +?> \ No newline at end of file diff --git a/lam/templates/tree/tree.php b/lam/templates/tree/tree.php new file mode 100644 index 00000000..8e8d0321 --- /dev/null +++ b/lam/templates/tree/tree.php @@ -0,0 +1,193 @@ +, so if I wanted to scroll to + * dc=example,dc=com for server 3, the URL would be: + * tree.php#3_dc%3Dexample%2Cdc%3Dcom + * + * @package lists + * @subpackage tree + * @author David Smith + * @author Roland Gruber + */ + +/** tree functions */ +include_once('../../lib/tree.inc'); +/** access to configuration */ +include_once('../../lib/config.inc'); +/** LDAP functions */ +include_once('../../lib/ldap.inc'); + +// start session +session_save_path("../../sess"); +@session_start(); + +setlanguage(); + +// This allows us to display large sub-trees without running out of time. +@set_time_limit( 0 ); + +// do we not have a tree and tree icons yet? Build a new one. +initialize_session_tree(); + +// get the tree and tree icons. +$tree = $_SESSION['tree']; +$tree_icons = $_SESSION['tree_icons']; + + +echo $_SESSION['header']; + +echo "LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +?> + + + + + + + +
+"; print_r( $tree ); +?> + + + + +'; + + for( $i=0; $i<=$level; $i++ ) { + echo '' . "\n"; + } + + // is this node expanded? (deciding whether to draw "+" or "-") + if( isset( $tree[$dn] ) ) { ?> + + + - + + + + + + + + + + $limit ) + $child_count = $limit . '+'; + } ?> + + + img + + + + + + () + + + + + + 10 ) + draw_create_link( $rdn, $level, $encoded_dn ); + foreach( $tree[$dn] as $dn ) + draw_tree_html( $dn, $level+1 ); + // Always draw the "create new" link at the bottom of the listing + draw_create_link( $rdn, $level, $encoded_dn ); + } +} + +function draw_create_link( $rdn, $level, $encoded_dn ) +{ + // print the "Create New object" link. + $create_html = ""; + $create_href = "create_form.php?container=$encoded_dn"; + $create_html .= ''; + for( $i=0; $i<=$level; $i++ ) { + $create_html .= ''; + } + $create_html .= ''; + $create_html .= '' . _('Create new entry') . ''; + $create_html .= '' . + _('Create new entry') . ''; + $create_html .= ''; + echo $create_html; +} + +?> diff --git a/lam/templates/tree/tree_view.php b/lam/templates/tree/tree_view.php new file mode 100644 index 00000000..a6598329 --- /dev/null +++ b/lam/templates/tree/tree_view.php @@ -0,0 +1,85 @@ +get_Suffix('tree'); + +// init tree +if (! isset($_SESSION['tree'])) { + initialize_session_tree(); + $tree = $_SESSION['tree']; + $tree_icons = $_SESSION['tree_icons']; + $contents = get_container_contents($dn, 0, '(objectClass=*)'); + usort( $contents, 'pla_compare_dns' ); + $tree[$dn] = $contents; + + foreach( $contents as $c ) + $tree_icons[$c] = get_icon( $c ); + + $_SESSION['tree'] = $tree; + $_SESSION['tree_icons'] = $tree_icons; +} + +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo "LDAP Account Manager"; +echo ""; +echo ""; +echo ""; +echo ""; +echo ""; +echo "This page requires a browser that can show frames!"; +echo ""; +echo ""; + +echo ""; + +?> \ No newline at end of file diff --git a/lam/templates/tree/update.php b/lam/templates/tree/update.php new file mode 100644 index 00000000..48aadf0f --- /dev/null +++ b/lam/templates/tree/update.php @@ -0,0 +1,109 @@ + ''. For example, to + * delete the 'sn' attribute from an entry, the update array would look like this: + * Array ( + * sn => '' + * ) + * + * On success, redirect to edit.php + * On failure, echo an error. + * + * @package lists + * @subpackage tree + * @author David Smith + * @author Roland Gruber + */ + +/** tree functions */ +include_once('../../lib/tree.inc'); +/** access to configuration */ +include_once('../../lib/config.inc'); +/** LDAP functions */ +include_once('../../lib/ldap.inc'); + +// start session +session_save_path("../../sess"); +@session_start(); + +setlanguage(); + +$dn = $_POST['dn']; +$encoded_dn = rawurlencode( $dn ); +$update_array = $_POST['update_array']; + +// check for delete attributes (indicated by the attribute entry appearing like this: attr => '' +foreach( $update_array as $attr => $val ) + if( ! is_array( $val ) ) + if( $val == '' ) { + $update_array[ $attr ] = array(); + } + else { + $update_array[ $attr ] = $val; + } + else { + foreach( $val as $i => $v ) { + $update_array[ $attr ][ $i ] = $v; + } + } + +$ds = $_SESSION['ldap']->server; +$res = @ldap_modify( $ds, $dn, $update_array ); +if( $res ) +{ + $redirect_url = "edit.php?dn=$encoded_dn"; + foreach( $update_array as $attr => $junk ) + $redirect_url .= "&modified_attrs[]=$attr"; + header( "Location: $redirect_url" ); +} +else +{ + echo $_SESSION['header']; + + echo "LDAP Account Manager\n"; + echo "\n"; + echo "\n"; + echo ""; + StatusMessage('ERROR', _("Was unable to modify attribtues from DN: %s."), ldap_error( $ds ), array($dn)); + echo ""; +} + +?> diff --git a/lam/templates/tree/update_confirm.php b/lam/templates/tree/update_confirm.php new file mode 100644 index 00000000..61f0795f --- /dev/null +++ b/lam/templates/tree/update_confirm.php @@ -0,0 +1,259 @@ +LDAP Account Manager\n"; +echo "\n"; +echo "\n"; +?> + +

+

:

+ $old_val ) +{ + // Did the user delete the field? + if( ! isset( $new_values[ $attr ] ) ) { + $update_array[ $attr ] = ''; + } + // did the user change the field? + elseif( $old_val != $new_values[ $attr ] ) { + + $new_val = $new_values[ $attr ]; + + // special case for userPassword attributes + if( 0 == strcasecmp( $attr, 'userPassword' ) && $new_val != '' ) { + $new_val = password_hash( $new_val, $_POST['enc_type'] ); + $password_already_hashed = true; + } + // special case for samba password + else if (( 0 == strcasecmp($attr,'sambaNTPassword') || 0 == strcasecmp($attr,'sambaLMPassword')) && trim($new_val[0]) != '' ){ + if ( 0 == strcasecmp($attr,'sambaNTPassword')) { + $new_val = ntPassword($new_val[0]); + } + else { + $new_val = lmPassword($new_val[0]); + } + } + $update_array[ $attr ] = $new_val; + } +} + +// special case check for a new enc_type for userPassword (not otherwise detected) +if( isset( $_POST['enc_type'] ) && + ! isset( $password_already_hashed ) && + $_POST['enc_type'] != $_POST['old_enc_type'] && + $_POST['enc_type'] != 'clear' && + $_POST['new_values']['userpassword'] != '' ) { + + $new_password = password_hash( $_POST['new_values']['userpassword'], $_POST['enc_type'] ); + $update_array[ 'userpassword' ] = $new_password; +} + +// strip empty vals from update_array and ensure consecutive indices for each attribute +foreach( $update_array as $attr => $val ) { + if( is_array( $val ) ) { + foreach( $val as $i => $v ) + if( null == $v || 0 == strlen( $v ) ) + unset( $update_array[$attr][$i] ); + $update_array[$attr] = array_values( $update_array[$attr] ); + } +} + +// at this point, the update_array should look like this (example): +// Array ( +// cn => Array( +// [0] => 'Dave', +// [1] => 'Bob' ) +// sn => 'Smith', +// telephoneNumber => '555-1234' ) +// This array should be ready to be passed to ldap_modify() + +?> + 0 ) { ?> + +
+
+ +
+
+ + + + + + + + + $new_val ) { $counter++ ?> + + + + \n\n"; + } + + ?> + +
+ "; + else + if( 0 == strcasecmp( $attr, 'userPassword' ) && ( obfuscate_password_display() || is_null( get_enc_type( $old_values[ $attr ] ) ) ) ) { + echo preg_replace( '/./', '*', $old_values[ $attr ] ) . "
"; + } + else { + echo nl2br( htmlspecialchars( $old_values[ $attr ] ) ) . "
"; + } + echo "
"; + + // is this a multi-valued attribute? + if( is_array( $new_val ) ) { + foreach( $new_val as $i => $v ) { + if( $v == '' ) { + // remove it from the update array if it's empty + unset( $update_array[ $attr ][ $i ] ); + $update_array[ $attr ] = array_values( $update_array[ $attr ] ); + } else { + echo nl2br( htmlspecialchars( $v ) ) . "
"; + } + } + + // was this a multi-valued attribute deletion? If so, + // fix the $update_array to reflect that per update_confirm.php's + // expectations + if( $update_array[ $attr ] == array( 0 => '' ) || $update_array[ $attr ] == array() ) { + $update_array[ $attr ] = ''; + echo '' . _('[attribute deleted]') . ''; + } + } + else + if( $new_val != '' ) + if( 0 == strcasecmp( $attr, 'userPassword' ) && ( obfuscate_password_display() || is_null( get_enc_type( $new_values[ $attr ] ) ) ) ) { + echo preg_replace( '/./', '*', $new_val ) . "
"; + } + else { + echo htmlspecialchars( $new_val ) . "
"; + } + else + echo '' . _('[attribute deleted]') . ''; + echo "
+
+ + + + + + +
+ + + + $val ) { ?> + + $v ) { ?> + + + + + + + + + + + + +
+ + +
+
+
+ + + + +
+ +

+
+ + + + + + +