getInstance('Horde_Ldap'); // try { // $result = $ldap->search( // $GLOBALS['conf']['ldap']['user']['basedn'], // Horde_Ldap_Filter::create('uid', 'equals', $username), // array('attributes' => array('mail')) // ); // if ($result->count()) { // $entry = $result->shiftEntry(); // return $entry->getValue('mail', 'single'); // } // } catch (Horde_Ldap_Exception $e) { // } // // return $value; // // // case 'fullname': // // Examples on how to set the fullname. // // // Example #1: Set the fullname from the GECOS information in // // the passwd file. // if (is_null($username)) { // return $value; // } // // $user = $GLOBALS['registry']->getAuth('bare'); // // $array = posix_getpwnam($user); // $gecos_array = explode(',', $array['gecos']); // return empty($gecos_array) // ? $user // : $gecos_array[0]; // // // // Example #2: Set the fullname from LDAP information. // if (is_null($username)) { // return $value; // } // // $ldap = $GLOBALS['injector']->getInstance('Horde_Ldap'); // try { // $result = $ldap->search( // $GLOBALS['conf']['ldap']['user']['basedn'], // Horde_Ldap_Filter::create('uid', 'equals', $username), // array('attributes' => array('cn', 'cn;lang-es')) // ); // if ($result->count()) { // $entry = $result->shiftEntry(); // return $entry->getValue('cn;lang-es', 'single') // ?: $entry->getValue('cn', 'single'); // } // } catch (Horde_Ldap_Exception $e) { // } // // return $username; // } // } // // PREFERENCES CHANGE: See above for documentation. // public function prefs_change($pref) // { // switch ($pref) { // case 'theme': // $GLOBALS['notification']->push('You changed your theme to ' . $GLOBALS['prefs']->getValue('theme') . '.'); // break; // } // } // // PREAUTHENTICATE HOOK: See above for description of format. // public function preauthenticate($userId, $credentials) // { // // Example #1: Make Horde respect the Unix convention of not // // allowing login when a file named /etc/nologin exist. // return !file_exists('/etc/nologin'); // // // // Example #2: Block access to Horde if the remote host exists in // // the DNSBL. It requires the PEAR Net_DNSBL package. // $dnsbl = new Net_DNSBL(); // $dnsbl->setBlacklists(array( // 'sbl-xbl.spamhaus.org', // 'bl.spamcop.net' // )); // return !$dnsbl->isListed($_SERVER['REMOTE_ADDR']); // // // // Example #3: Block access for user 'foo'. // return ($userId != 'foo'); // // // // Example #4: Create credentials needed by the LDAP Horde_Auth // // driver for adding/deleting/updating users. // $entry = array( // 'dn' => 'uid=' . $userId . ',ou=People,dc=example.com', // 'cn' => isset($credentials['user_fullname']) ? $credentials['user_fullname'] : $userId, // 'sn' => $userId, // 'objectclass' => array( // 'top', // 'person', // 'qmailuser', // 'CourierMailAccount', // ), // 'mailhost' => 'mail.example.com', // 'mailMessageStore' => '/home/mail/' . $userId, // 'homeDirectory' => '/home/mail/' . $userId, // 'mailbox' => '/Maildir', // 'homeDirectory' => '/Maildir', // 'uid' => $userId, // 'accountStatus' => 'active', // 'mailQuota' => '104857600S', // 'mail' => $userId, // 'uidNumber' => 501, // 'gidNumber' => 501, // 'deliveryMode' => 'nolocal' // ); // // // Need to check for new users (password) and edited users // // (user_pass_2) // if (isset($credentials['password'])) { // $entry['userPassword'] = '{MD5}' . base64_encode(pack('H*', md5($credentials['password']))); // } elseif (isset($credentials['user_pass_2'])) { // $entry['userPassword'] = '{MD5}' . base64_encode(pack('H*', md5($credentials['user_pass_2']))); // } // $entry['deliveryMode'] = 'nolocal'; // // return array( // 'userId' => $userId, // 'credentials' => $entry // ); // } // // POSTAUTHENTICATE HOOK: See above for description of format. // public function postauthenticate($userId, $credentials) // { // // Example #1: Validating the user's right to login to Horde by // // by consulting group membership in an LDAP directory. That // // way, if your Horde installation is configured to authenticate // // against IMP which in turn authenticate via IMAP, it is still // // possible to limit access to Horde by group membership. The // // following example had been made with an MS Active Directory in // // mind. Note that if the LDAP directory is unavailable or some // // other error occur, authentication will fail. // $ldapServer = 'ad.example.com'; // $ldapPort = '389'; // // // Note that credential is sent plain-text in this case, so don't // // use privileged account here or setup SSL (by using port 636 // // above). // $binddn = 'cn=WithoutPrivilege,dc=ulaval-dev,dc=ca'; // $bindpw = 'Remember this is sent in the clear unless SSL is used'; // $searchBase = 'ou=People,dc=example,dc=com'; // // // Attribute to match $userId against in search // $userAttr = 'sAMAccountName'; // // // Group membership attribute, need to be all lowercase // $groupAttr = 'memberof'; // // // Attribute to check for right to use Horde // $groupdn = 'cn=HordeUser,ou=People,dc=example,dc=com'; // $ret = false; // // $ds = @ldap_connect($ldapServer, $ldapPort); // // if (@ldap_bind($ds, $binddn, $bindpw)) { // $searchResult = @ldap_search($ds, $searchBase, $userAttr . '=' . $userId, array($groupAttr), 0, 1, 5); // if ($information = @ldap_get_entries($ds, $searchResult)) { // // Make pattern case-insensitive // $pattern = '/' . $groupdn . '/i'; // foreach ($information[0][$groupAttr] as $group) { // if (preg_match($pattern, $group)) { // $ret = true; // break; // } // } // } // } // // ldap_close($ds); // // return $ret; // // // Example #2: Providing a client certificate <-> horde username store. // // Normally, the following would be looked up in a persistent storage // // backend. Note this example also stores a plaintext password to avoid // // cluttering up the example. DO NOT DO THIS. If your x509 client // // certificate setup requires that you still pass a password to the IMAP // // server, the password MUST be stored encrypted. In this example, we // // store a unique identifier for the certificate to add an additional level // // of security. It wouldn't be too hard to create a admin UI for managing // // client certificates and "registering" them to the user. // $mapping = array('user@example.com' => array( // 'credentials' => array('password' => 'passw0rd'), 'certificate_id' => '123456')); // // if (empty($mapping[$userId]) || $credentials['certificate_id'] != $mapping[$userId]['certificate_id']) { // return false; // } // // return $mapping[$userId]['credentials']; // } // // USERNAME HOOK: See above for description of format. // public function authusername($userId, $toHorde) // { // // Example #1: Append the virtual domain to the username. // // ex. $HTTP_HOST = 'mail.mydomain.com', $userId = 'myname' returns: // // 'myname@mydomain.com' // $vdomain = preg_replace('|^mail\.|i', '', getenv('HTTP_HOST')); // $vdomain = Horde_String::lower($vdomain); // // if ($toHorde) { // return $userId . '@' . $vdomain; // } // // return (substr($userId, -strlen($vdomain)) == $vdomain) // ? substr($userId, 0, -strlen($vdomain) - 1) // : $userId; // // // // Example #2: Convert username to all lowercase. This might be // // necessary if an authentication backend is case insensitive to // // take into account that Horde's preference system is case // // sensitive. // // ex. $userId = 'MyName' returns: 'myname' // return $toHorde // ? Horde_String::lower($userId) // : $userId; // // // // Example #3: Map the LDAP "uid" back to the LDAP "mail" // // attribute in case both are allowed user IDs for login. // if (!$toHorde) { // return $userId; // } // $ldapServer = 'localhost'; // $ldapPort = '389'; // $searchBase = 'dc=example,dc=com'; // $binddn = 'cn=manager,' . $searchBase; // $bindpw = 'PASSWORD'; // // $ds = @ldap_connect($ldapServer, $ldapPort); // $searchResult = @ldap_search($ds, $searchBase, 'uid=' . $userId); // $information = @ldap_get_entries($ds, $searchResult); // if (($information !== false) && ($information['count'] > 0)) { // $userId = $information[0]['mail'][0]; // } // return $userId; // // // Example #4: Essentially the same as Example #1, but in the reverse // // direction. This is used, e.g., when using x509 client certificates // // that provide the username as an email address and you want Horde // // logins to be usernames only. // // ex. $HTTP_HOST = 'mail.mydomain.com', $userId = 'myname' returns: // // 'myname@mydomain.com' // $vdomain = preg_replace('|^mail\.|i', '', getenv('HTTP_HOST')); // $vdomain = Horde_String::lower($vdomain); // // if (!$toHorde) { // return $userId . '@' . $vdomain; // } else { // return (substr($userId, -strlen($vdomain)) == $vdomain) // ? substr($userId, 0, -strlen($vdomain) - 1) // : $userId; // } // } // // APPLICATION AUTHENTICATED HOOK: See above for format. // public function appauthenticated() // { // // Code to run when an application is first authenticated // } // // PRE-PUSH HOOK: See above for format. // public function pushapp() // { // // Code to run immediately before the app is switched to horde // } // // POST-PUSH HOOK: See above for format. // public function pushapp_post() // { // // Code to run immediately after the app is successfully switched to // // horde // } // // ADD CSS HOOK: See above for description of format. // public function cssfiles($theme) // { // return array( // '/file/path/to/css' => 'uri/to/css' // ); // } /** * Modify the browser object. * * @param Horde_Core_Browser $browser The browser object. */ // public function browser_modify($browser) // { // // Example #1: Mark all browsers as mobile. Useful if this // // particular Horde installation is dedicated solely to serving // // mobile devices. // $browser->setMobile(true); // } /** * Allow altering or validating data submitted by a user during a signup * request before any attempts are made to add them to the system. * * @param array $info TODO * * @return array TODO */ // public function signup_preprocess($info) // { // $info['user_name'] = Horde_String::lower($info['user_name']); // return $info; // } /** * Callback when a signup is queued for administrative approval. * * @param string $userId The username. * @param array $data TODO */ // public function signup_queued($userId, $data) // { // // Example #1: Send a notification message to the web server // // administrator's e-mail address. // $headers = array( // 'To' => $_SERVER['SERVER_ADMIN'], // 'From' => $_SERVER['SERVER_ADMIN'], // 'Subject' => 'New ' . $GLOBALS['registry']->get('name', 'horde') . ' Signup' // ); // // try { // $extraFields = $GLOBALS['injector']->getInstance('Horde_Core_Hooks')->callHook('signup_getextra', 'horde'); // } catch (Horde_Exception $e) {} // // $msg = _("A new signup has been received and is awaiting your approval.") . // "\n\n" . // $this->_signup_queued_walkdata($extraFields, $data) . // "\n" . // sprintf(_("You can approve this signup at %s"), Horde::url('admin/user.php', true, array('append_session' => -1))); // // $GLOBALS['injector']->getInstance('Horde_Mail')->send($_SERVER['SERVER_ADMIN'], $headers, $msg); // } // // // Helper function for signup_queued Example #1 // private function _signup_queued_walkdata($fields, $data) // { // $msg = ''; // foreach ($data as $field => $value) { // if (in_array($field, array('password', 'url'))) { // continue; // } // // if (is_array($value)) { // $msg .= $this->_signup_queued_walkdata($fields, $value); // } else { // $field = isset($fields[$field]['label']) // ? $fields[$field]['label'] // : $field; // $msg .= "$field: $value\n"; // } // } // return $msg; // } /** * Provide any extra fields which need to be filled in when a * non-registered user wishes to sign up. * * @return array An array containing the following keys: * - desc: Any help text attached to the field * - label: The text that the user will see attached to this field * - params: Any allowed parameter to Horde_Form field types * - readonly: (boolean) Whether this editable * - required: (boolean) Whether this field is mandatory * - type: Any allowed Horde_Form field type */ // public function signup_getextra() // { // // Example #1: A hypothetical case where we would want to store // // extra information about a user into a turba sql address book. All // // this example does is include the attributes.php file from turba. // // NOTE: You NEED Turba to be correctly installed before you can use // // this example. // return $GLOBALS['registry']->loadConfigFile('attributes.php', 'attributes', 'turba')->config['attributes']; // } /** * TODO * * @param string $userId The username. * @param array $extra A hash with the extra information requested via * the signup_getextra hook. * @param string $password The password. */ // public function signup_addextra($userId, $extra, $password) // { // // Example #1: Continuation of Example #1 from signup_getextra(). // // Here we connect to the database using the sql parameters // // configured in $conf and store the extra fields in turba_objects, // // using the $userId as the key for the object and values from the // // $extra array. You could create your own sql syntax or code to // // store this in whichever backend you require. // // NOTE: You NEED Turba to be correctly installed before you can use // // this example. It also assumes you are using an SQL backend. // $db = $GLOBALS['injector']->getInstance('Horde_Db_Adapter'); // // $fields = $values = $markers = array(); // foreach ($extra as $field => $value) { // $fields[] = 'object_' . Horde_String::lower($field); // $markers[] = '?'; // $values[] = Horde_String::convertCharset($value, 'UTF-8', $db->getCharset()); // } // $fields[] = 'object_id'; // $markers[] = '?'; // $values[] = $userId; // // $query = 'INSERT INTO turba_objects ( owner_id, ' . implode(', ', $fields) . ')'; // $query .= ' VALUES ( \'admin\', ' . implode(', ', $markers) . ')'; // $db->insert($query, $values); // } /** * Alter the share list. * * @param string $userId TODO * @param TODO $perm TODO * @param string $owner TODO * @param array $share_list TODO * * @return array The altered share list. */ // public function share_list($userId, $perm, $owner, $share_list) // { // return $share_list; // } /** * Hook called if a user tries to make an action that is under permission * control that they don't have sufficient permissions for. It can be * used to show the user a custom message including HTML code (via the * notification system), or to interrupt the code flow and send the user * to a different page. * * @param string $app Horde application. * @param string $permission Permission that failed. */ // public function perms_denied($app, $permission) // { // // Example #1: Provide link to upgrade script in notification // // message. // $GLOBALS['notification']->push(sprintf('Permission denied. Click HERE to upgrade %s.', $app, $GLOBALS['registry']->get('name')), 'horde.error', array('content.raw')); // } /** * IMSP share init. TODO * * @param TODO $share_obj TODO * @param string $app TODO */ // public function share_init($share_obj, $app) // { // global $cfgSources, $prefs, $session; // // // TODO: Move to turba? // if (($app == 'turba') && // (!empty($cfgSources['imsp']['use_shares']))) { // // Only do this once per session or when this session variable // // is purposely unset. // if ($session->get('horde', 'imsp_synched')) { // return; // } // // $results = Net_IMSP_Utils::synchShares($share_obj, $cfgSources['imsp']); // if (!$results instanceof PEAR_Error) { // $session->set('horde', 'imsp_synched') = true; // // // Now deal with adding or removing address books from prefs. // $dirty = false; // $abooks = $prefs->getValue('addressbooks'); // $abooks = empty($abooks) // ? array() // : explode("\n", $prefs->getValue('addressbooks')); // // if (count($results['removed'] > 0)) { // foreach ($results['removed'] as $sharename) { // $key = array_search($sharename, $abooks); // if ($key === true) { // unset($abooks[$key]); // $dirty = true; // } // } // } // // if (count($results['added']) > 0) { // foreach ($results['added'] as $sharename) { // if (array_search($sharename, $abooks) === false) { // $abooks[] = $sharename; // $dirty = true; // } // } // } // // if ($dirty) { // $result = $prefs->setValue('addressbooks', implode("\n", $abooks)); // } // // // We have to save the connection info for the imsp server // // since the share_modify hook will not occur from within // // turba's context. // $session->set('horde', 'imsp_config', $cfgSources['imsp']['params']); // } // } // } /** * IMSP share modify. TODO * * @param TODO $share TODO */ // public function share_modify($share) // { // global $injector, $session, $share; // // $params = unserialize($share->get('params')); // if (is_array($params) && // !empty($params['source']) && // ($params['source'] == 'imsp') && // ($config = $session->get('horde', 'imsp_config'))) { // // Ensure we don't try to change ownership. // $params = @unserialize($share->get('params')); // $bookName = $params['name']; // if (strpos($bookName, $share->get('owner')) !== 0) { // throw new Horde_Exception('Changing ownership of IMSP address books is not supported.'); // } // // // Update the ACLS // $perms = $share->getPermission(); // $users = $injector->getInstance('Horde_Perms')->getUserPermissions(); // foreach ($users as $user => $perm) { // $acl = Net_IMSP_Utils::permsToACL($perm); // $result = Net_IMSP_Utils::setACL($config, $bookName, $user, $acl); // } // } // } /** * ActiveSync hook for determing a Horde username from an email address. * * @param string $email The email address * * @return string The username to use to authenticate to Horde with. */ // public function activesync_get_autodiscover_username($email) // { // return substr($email, 0, strpos($email, '@')); // } /** * ActiveSync hook for overriding the XML output. Takes an array of * autodiscover parameters and returns the raw XML string to send to the * client. USING THIS HOOK MEANS YOU ARE RESPONSIBLE FOR SENDING CORRECT * XML TO THE CLIENT. ACTIVESYNC WILL SEND THIS STRING AS-IS. ONLY USE THIS * IF YOU KNOW WHAT YOU ARE DOING! * * @param array $params The array of available Autodiscover parameters. * * @return string The XML string. */ // public function activesync_autodiscover_xml(array $params) // { // } /** * ActiveSync hook for modifying the various Autodiscover values before * the XML string is build by the ActiveSync server code. Use, e.g., if you * need to alter the value of the host before sending it to the client. * * @param array $params The array of Autodiscover parameters. * * @return array The possibly modified array of Autodiscover parameters. */ // public function activesync_autodiscover_parameters(array $params) // { // return $params; // } /** * Activesync hook for providing additional checks before allowing a device * to be paired with the server for the first time. * * @param Horde_ActiveSync_Device $device The device object. * * @return boolean|integer True on success (device passed checks) or a * Horde_ActiveSync_Status:: constant on failure. */ // public function activesync_create_device(Horde_ActiveSync_Device $device) // { // return true; // } /** * ActiveSync hook for providing additional policy checks on a device * after it has already been paired. Useful for enforcing things like * only allowing certain user agents to connect. * * @param Horde_ActiveSync_Device $device The device object. * * @return boolean|integer True on success (device passed checks) or a * Horde_ActiveSync_Status:: constant on failure. */ // public function activesync_device_check(Horde_ActiveSync_Device $device) // { // return true; // } /** * Hook for providing custom X509 certificate validation routines. You can * parse the $cert handle provided and/or use any of the available SSL * enviroment variables provided in $_SERVER by your webserver to determine * if the certificate should be honored. * * @param mixed $cert The certificate string or handle. * * @return boolean True if the certificate is valid. */ // public function x509_validate($cert) // { // $parsed = openssl_x509_parse($cert); // return ($parsed['issuer']['CN'] == 'My CA CN'); // } /** * Hook for modifying the device object prior to request processing. The * device object is fully populated here, so you have access to all * properties: * - id: The device id. * - policykey: The device's policy key, if provisioned. * - userAgent: The device's user agent string. * - multiplex: Bitmask for forced multiplex collections. * @see Horde_ActiveSync_Device * - version: The EAS version in use by the device. * - properties: A hash containing the following key/values (note that not * all devices provide all values): * - Horde_ActiveSync_Device::MODEL: => The model name. * - Horde_ActiveSync_Device::IMEI: => The device's IMEI #. * - Horde_ActiveSync_Device::NAME: => The device's common * name. * - Horde_ActiveSync_Device::OS: => The device's OS. * - Horde_ActiveSync_Device::OS_LANGUAGE => The language. * - Horde_ActiveSync_Device::PHONE_NUMBER => The phone number. * * @param Horde_ActiveSync_Device $device The device object. * * @return Horde_ActiveSync_Device The possibly modified device object. */ // public function activesync_device_modify(Horde_ActiveSync_Device $device) // { // // Example for forcing certain device to force multiplexed // // collections for collection types they don't support multiple // // collections for. Note that this doesn't apply to email folders, // // which are NEVER sent multiplexed. // // NOTE: The Horde_ActiveSync library already determines this based // // on some userAgent and version sniffing. You should only // // perform this here if it doesn't work for you, or you have // // discovered a device that doesn't fit the logic. // // For android devices that don't advertise the android version we have // // to manually set the multiplex flag if we want to force any. E.g., // // the Galaxy Note 3 doesn't support multiple collections prior to // // KitKat so we have to force them all. // if (empty($device->multiplex)) { // switch (strtolower($device->userAgent)) { // case 'SAMSUNG-SM-N900V/101.403': // // Note 3, Android 4.3 // $device->multiplex = Horde_ActiveSync_Device::MULTIPLEX_NOTES | // Horde_ActiveSync_Device::MULTIPLEX_CONTACTS | // Horde_ActiveSync_Device::MULTIPLEX_CALENDAR | // Horde_ActiveSync_Device::MULTIPLEX_TASKS; // } // } // return $device; // } }