From: Ben Klang Date: Mon, 21 Dec 2009 02:19:24 +0000 (+0000) Subject: Big update toward new UI. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=55defafc1063f50b24a44dc2ebb92bfc1df3cb51;p=horde.git Big update toward new UI. Continue improving H4 compliance with Exception handling More revisions to config.xml to handle differentiated backends Re-merge all the code from the include dir (extensions) back into single file (extensions.php) git-svn-id: https://svn.alkaloid.net/gpl/shout/trunk@499 06cd67b6-e706-0410-b29e-9de616bca6e9 --- diff --git a/config/conf.xml b/config/conf.xml index 26eeda0b7..317511531 100644 --- a/config/conf.xml +++ b/config/conf.xml @@ -1,18 +1,32 @@ + + + Context Storage + Sql + + + + contexts + + + + + Extension Storage - ldap - + Ldap + - + - + - + + @@ -21,15 +35,16 @@ Device Storage - ldap - + Ldap + - + - + - + + sip_peers diff --git a/extensions/add.php b/extensions/add.php deleted file mode 100644 index 16f3f3b8c..000000000 --- a/extensions/add.php +++ /dev/null @@ -1,12 +0,0 @@ - - * - * See the enclosed file LICENSE for license information (GPL). If you - * did not receive this file, see http://www.horde.org/licenses/gpl.php. - */ -@define('SHOUT_BASE', dirname(__FILE__) . '/..'); - -require SHOUT_BASE . '/users/edit.php'; \ No newline at end of file diff --git a/extensions/delete.php b/extensions/delete.php deleted file mode 100644 index 4400c5e36..000000000 --- a/extensions/delete.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * See the enclosed file LICENSE for license information (GPL). If you - * did not receive this file, see http://www.horde.org/licenses/gpl.php. - * - * @package shout - */ -@define('SHOUT_BASE', dirname(__FILE__) . '/..'); -//require_once 'Horde/Variables.php'; - -$context = Horde_Util::getFormData('context'); -$extension = Horde_Util::getFormData('extension'); - -$res = $shout->deleteUser($context, $extension); - -if (!$res) { - echo "Failed!"; - print_r($res); -} -$notification->push("User Deleted."); -$notification->notify(); -require SHOUT_TEMPLATES . '/common-footer.inc'; \ No newline at end of file diff --git a/extensions/edit.php b/extensions/edit.php deleted file mode 100644 index 3c3fe1e9f..000000000 --- a/extensions/edit.php +++ /dev/null @@ -1,93 +0,0 @@ - - * - * See the enclosed file LICENSE for license information (GPL). If you - * did not receive this file, see http://www.horde.org/licenses/gpl.php. - * - * @package shout - */ -if (!isset($SHOUT_RUNNING) || !$SHOUT_RUNNING) { - header('Location: /'); - exit(); -} - -require_once SHOUT_BASE . '/lib/User.php'; -require_once 'Horde/Variables.php'; - -$RENDERER = &new Horde_Form_Renderer(); - -$empty = ''; -$beendone = 0; -$wereerrors = 0; - -$vars = &Variables::getDefaultVariables($empty); - -$FormName = 'UserDetailsForm'; -$Form = &Horde_Form::singleton($FormName, $vars); -if (is_a($Form, 'PEAR_Error')) { - $notification->push($Form); -} else { - $FormValid = $Form->validate($vars, true); - if (is_a($FormValid, 'PEAR_Error')) { - $notification->push($FormValid); - } else { - $Form->fillUserForm(&$vars, $extension); - } -} - - -$notification->notify(); - -if (!$FormValid || !$Form->isSubmitted()) { - # Display the form for editing - $Form->open($RENDERER, $vars, 'index.php', 'post'); - $Form->preserveVarByPost(&$vars, 'extension'); - $Form->preserveVarByPost(&$vars, 'context'); - $Form->preserveVarByPost(&$vars, 'section'); - $RENDERER->beginActive($Form->getTitle()); - $RENDERER->renderFormActive($Form, $vars); - $RENDERER->submit(); - $RENDERER->end(); - $Form->close($RENDERER); -} else { - # Process the Valid and Submitted form -$notification->push("How did we get HERE?!", 'horde.error'); -$notification->notify(); -// $info = array(); -// $Form->getInfo($vars, $info); -// -// $name = $info['name']; -// $extension = $info['extension']; -// $newextension = $info['newextension']; -// $email = $info['email']; -// $pin = $info['pin']; -// -// -// $limits = $shout->getLimits($context, $extension); -// -// $userdetails = array("newextension" => $newextension, -// "name" => $name, -// "pin" => $pin, -// "email" => $email); -// -// $i = 1; -// $userdetails['telephonenumbers'] = array(); -// while ($i <= $limits['telephonenumbersmax']) { -// $tmp = $info['telephone'.$i]; -// if (!empty($tmp)) { -// $userdetails['telephonenumbers'][] = $tmp; -// } -// $i++; -// } -// -// $userdetails['dialopts'] = array(); -// if ($info['moh']) { -// $userdetails['dialopts'][] = 'm'; -// } -// if ($info['transfer']) { -// $userdetails['dialopts'][] = 't'; -// } -} \ No newline at end of file diff --git a/extensions/list.php b/extensions/list.php deleted file mode 100644 index 45cfc22c4..000000000 --- a/extensions/list.php +++ /dev/null @@ -1,18 +0,0 @@ - - * - * See the enclosed file COPYING for license information (GPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. - * - * @package shout - */ - -if (!isset($SHOUT_RUNNING) || !$SHOUT_RUNNING) { - header('Location: /'); - exit(); -} - -$users = &$shout->getUsers($context); diff --git a/extensions/save.php b/extensions/save.php deleted file mode 100644 index e5a935501..000000000 --- a/extensions/save.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * See the enclosed file LICENSE for license information (GPL). If you - * did not receive this file, see http://www.horde.org/licenses/gpl.php. - * - * @package shout - */ -if (!isset($SHOUT_RUNNING) || !$SHOUT_RUNNING) { - header('Location: /'); - exit(); -} - -require_once SHOUT_BASE . '/lib/User.php'; -require_once 'Horde/Variables.php'; - -$RENDERER = &new Horde_Form_Renderer(); - -$vars = &Variables::getDefaultVariables(); -$FormName = $vars->get('formname'); - -$Form = &Horde_Form::singleton($FormName, $vars); - -$FormValid = $Form->validate($vars, true); - -if (!$FormValid || !$Form->isSubmitted()) { - require SHOUT_BASE . '/usermgr/edit.php'; -} else { - # Form is Valid and Submitted - $extension = $vars->get('extension'); - - $limits = $shout->getLimits($context, $extension); - - # FIXME: Input Validation (Text::??) - $userdetails = array( - "newextension" => $vars->get('newextension'), - "name" => $vars->get('name'), - "mailboxpin" => $vars->get('mailboxpin'), - "email" => $vars->get('email'), - "uid" => $vars->get('uid'), - ); - - $userdetails['telephonenumber'] = array(); - $telephonenumber = $vars->get("telephonenumber"); - if (!empty($telephonenumber) && is_array($telephonenumber)) { - $i = 1; - while ($i <= $limits['telephonenumbersmax']) { - if (!empty($telephonenumber[$i])) { - $userdetails['telephonenumber'][] = $telephonenumber[$i++]; - } else { - $i++; - } - } - } - - $userdetails['dialopts'] = array(); - - if ($vars->get('moh')) { - $userdetails['dialopts'][] = 'm'; - } - if ($vars->get('transfer')) { - $userdetails['dialopts'][] = 't'; - } - if ($vars->get('eca')) { - $userdetails['dialopts'][] = 'e'; - } - $res = $shout->saveUser($context, $extension, $userdetails); - if (is_a($res, 'PEAR_Error')) { - $notification->push($res); - } else { - $notification->push('User information updated. '. - 'Changes will take effect within 10 minutes', - 'horde.success'); - } - - $notification->notify(); -} diff --git a/index.php b/index.php index 74f7aead1..4097661e1 100644 --- a/index.php +++ b/index.php @@ -11,15 +11,13 @@ */ @define('SHOUT_BASE', dirname(__FILE__)); -$shout_configured = (is_readable(SHOUT_BASE . '/config/conf.php') && - is_readable(SHOUT_BASE . '/config/applist.xml') && - is_readable(SHOUT_BASE . '/config/defines.php')); +$shout_configured = (is_readable(SHOUT_BASE . '/config/conf.php')); if (!$shout_configured) { require SHOUT_BASE . '/../lib/Test.php'; Horde_Test::configFilesMissing('Shout', SHOUT_BASE, - array('conf.php', 'applist.xml', 'defines.php')); + array('conf.php')); } require_once SHOUT_BASE . '/lib/base.php'; -header('Location: ' . Horde::applicationUrl('usermgr.php')); +header('Location: ' . Horde::applicationUrl('extensions.php')); diff --git a/lib/Driver.php b/lib/Driver.php index f06fc8928..d5278e07c 100644 --- a/lib/Driver.php +++ b/lib/Driver.php @@ -129,69 +129,32 @@ class Shout_Driver { * @return mixed The newly created concrete Shout_Driver instance, or * false on an error. */ - function &factory($driver = null, $params = null) + function &factory($class, $driver = null, $params = null) { if (is_null($driver)) { - $driver = $GLOBALS['conf']['storage']['driver']; + $driver = $GLOBALS['conf'][$class]['driver']; } $driver = basename($driver); if (is_null($params)) { - $params = Horde::getDriverConfig('storage', $driver); + if ($GLOBALS['conf'][$class]['params']['driverconfig'] == 'horde') { + $params = array_merge($GLOBALS['conf'][$class]['params'], + Horde::getDriverConfig('storage', $driver)); + } else { + $params = $GLOBALS['conf'][$class]['params']; + } } + $params['class'] = $class; + require_once dirname(__FILE__) . '/Driver/' . $driver . '.php'; $class = 'Shout_Driver_' . $driver; if (class_exists($class)) { - $shout = new $class($params); - return $shout; + return new $class($params); } else { return false; } } - /** - * Attempts to return a reference to a concrete Shout_Driver - * instance based on $driver. It will only create a new instance - * if no Shout_Driver instance with the same parameters currently - * exists. - * - * This method must be invoked as: $var = &Shout_Driver::singleton() - * - * @param string $driver The type of concrete Shout_Driver subclass - * to return. The is based on the storage - * driver ($driver). The code is dynamically - * included. - * @param array $params (optional) A hash containing any additional - * configuration or connection parameters a - * subclass might need. - * - * @return mixed The created concrete Shout_Driver instance, or false - * on error. - */ - function &singleton($driver = null, $params = null) - { - static $instances; - - if (is_null($driver)) { - $driver = $GLOBALS['conf']['storage']['driver']; - } - - if (is_null($params)) { - $params = Horde::getDriverConfig('storage', $driver); - } - - if (!isset($instances)) { - $instances = array(); - } - - $signature = serialize(array($driver, $params)); - if (!isset($instances[$signature])) { - $instances[$signature] = &Shout_Driver::factory($driver, $params); - } - - return $instances[$signature]; - } } -// }}} diff --git a/lib/Driver/Ldap.php b/lib/Driver/Ldap.php index 8dfe2a87c..6fd1640ae 100644 --- a/lib/Driver/Ldap.php +++ b/lib/Driver/Ldap.php @@ -1,9 +1,6 @@ _connect(); /* These next lines will translate between indexes used in the @@ -71,165 +68,13 @@ class Shout_Driver_ldap extends Shout_Driver } /** - * Get a list of contexts from the backend - * - * @param string $filter Search filter - * - * @return array Contexts valid for this system - * - * @access private - */ - function &getContexts($searchfilters = SHOUT_CONTEXT_ALL, - $filterperms = null) - { - static $entries = array(); - if (isset($entries[$searchfilters])) { - return $entries[$searchfilters]; - } - - if ($filterperms === null) { - $filterperms = PERMS_SHOW|PERMS_READ; - } - - # TODO Add caching mechanism here. Possibly cache results per - # filter $this->contexts['customer'] and return either data - # or possibly a reference to that data - - # Determine which combination of contexts need to be returned - if ($searchfilters == SHOUT_CONTEXT_ALL) { - $searchfilter="(objectClass=asteriskObject)"; - } else { - $searchfilter = "(&"; - # FIXME Change this to non-V-Office specific objectClass - if ($searchfilters & SHOUT_CONTEXT_CUSTOMERS) { - # FIXME what does this objectClass really do for us? - $searchfilter.="(objectClass=vofficeCustomer)"; - } - if ($searchfilters & SHOUT_CONTEXT_EXTENSIONS) { - $searchfilter.="(objectClass=asteriskExtensions)"; - } - if ($searchfilters & SHOUT_CONTEXT_MOH) { - $searchfilter.="(objectClass=asteriskMusicOnHold)"; - } - if ($searchfilters & SHOUT_CONTEXT_CONFERENCE) { - $searchfilter.="(objectClass=asteriskMeetMe)"; - } - $searchfilter .= ")"; - } - - $attributes = array(SHOUT_ACCOUNT_ID_ATTRIBUTE, 'objectClass', 'context'); - - # Collect all the possible contexts from the backend - $res = @ldap_search($this->_LDAP, - SHOUT_ASTERISK_BRANCH.','.$this->_params['basedn'], - "$searchfilter"); - #array('context', 'associatedDomain')); - if (!$res) { - return PEAR::raiseError("Unable to locate any contexts " . - "underneath ".SHOUT_ASTERISK_BRANCH.",".$this->_params['basedn'] . - " matching those search filters" . ldap_error($this->_LDAP)); - } - - $res = ldap_get_entries($this->_LDAP, $res); - $i = 0; - $entries[$searchfilters] = array(); - while ($i < $res['count']) { - $context = $res[$i]['context'][0]; - $type = SHOUT_CONTEXT_NONE; - foreach ($res[$i][strtolower('objectClass')] as $objectClass) { - switch ($objectClass) { - case SHOUT_CONTEXT_CUSTOMERS_OBJECTCLASS: - # FIXME What does this objectClass really get us? - $type = $type | SHOUT_CONTEXT_CUSTOMERS; - break; - case SHOUT_CONTEXT_EXTENSIONS_OBJECTCLASS: - $type = $type | SHOUT_CONTEXT_EXTENSIONS; - break; - case SHOUT_CONTEXT_MOH_OBJECTCLASS: - $type = $type | SHOUT_CONTEXT_MOH; - break; - case SHOUT_CONTEXT_CONFERENCE_OBJECTCLASS: - $type = $type | SHOUT_CONTEXT_CONFERENCE; - break; - case SHOUT_CONTEXT_VOICEMAIL_OBJECTCLASS: - $type = $type | SHOUT_CONTEXT_VOICEMAIL; - break; - } - } - if (Shout::checkRights("shout:contexts:$context", $filterperms)) { - $entries[$searchfilters][$context] = - array( - 'custid' => SHOUT_ACCOUNT_ID_ATTRIBUTE, - 'type' => $type, - ); - } - $i++; - } - # return the array - return $entries[$searchfilters]; - } - - /** - * For the given context and type, make sure the context has the - * appropriate properties, that it is effectively of that "type" - * - * @param string $context the context to check type for - * - * @param string $type the type to verify the context is of - * - * @return boolean true of the context is of type, false if not - * - * @access public - */ - function checkContextType($context, $type) { - switch ($type) { - case "users": - $searchfilter = '(objectClass='.SHOUT_CONTEXT_VOICEMAIL_OBJECTCLASS.')'; - break; - case "dialplan": - $searchfilter = '(objectClass='.SHOUT_CONTEXT_EXTENSIONS_OBJECTCLASS.')'; - break; - case "moh": - $searchfilter='(objectClass='.SHOUT_CONTEXT_MOH_OBJECTCLASS.')'; - break; - case "conference": - $searchfilter='(objectClass='.SHOUT_CONTEXT_CONFERENCE_OBJECTCLASS.')'; - break; - case "all": - default: - $searchfilter=""; - break; - } - - $res = @ldap_search($this->_LDAP, - SHOUT_ASTERISK_BRANCH.','.$this->_params['basedn'], - "(&(objectClass=asteriskObject)$searchfilter(context=$context))", - array("context")); - if (!$res) { - return PEAR::raiseError("Unable to search directory for context -type"); - } - - $res = ldap_get_entries($this->_LDAP, $res); - if (!$res) { - return PEAR::raiseError("Unable to get results from LDAP query"); - } - - if ($res['count'] == 1) { - return true; - } else { - return false; - } - } - - /** * Get a list of users valid for the contexts * - * @param string $context Context on which to search + * @param string $context Context in which to search * * @return array User information indexed by voice mailbox number */ - function getUsers($context) + public function getExtensions($context) { static $entries = array(); @@ -237,84 +82,61 @@ type"); return $entries[$context]; } - $basedn = SHOUT_USERS_BRANCH.','.$this->_params['basedn']; + $this->_params['basedn']; $filter = '(&'; - $filter .= '(objectClass='.SHOUT_USER_OBJECTCLASS.')'; - $filter .= '(context='.$context.')'; + $filter .= '(objectClass=AsteriskVoiceMail)'; + $filter .= '(AstContext='.$context.')'; $filter .= ')'; $attributes = array( - 'voiceMailbox', - 'asteriskUserDialOptions', - 'asteriskVoiceMailboxOptions', - 'voiceMailboxPin', 'cn', - 'telephoneNumber', - 'asteriskUserDialTimeout', 'mail', - 'asteriskPager', + 'AstVoicemailMailbox', + 'AstVoicemailPassword', + 'AstVoicemailOptions', + 'AstVoicemailPager' ); - $search = @ldap_search($this->_LDAP, $basedn, $filter, $attributes); - - if (!$search) { - return PEAR::raiseError("Unable to search directory: " . - ldap_error($this->_LDAP)); + $search = ldap_search($this->_LDAP, $this->_params['basedn'], $filter, $attributes); + if ($search === false) { + throw new Shout_Exception("Unable to search directory: " . + ldap_error($this->_LDAP), ldap_errno($this->_LDAP)); } $res = ldap_get_entries($this->_LDAP, $search); - # - # ATTRIBUTES RETURNED FROM ldap_get_entries ARE ALL LOWER CASE!! - # + if ($res === false) { + throw new Shout_Exception("Unable to fetch results from directory: " . + ldap_error($this->_LDAP), ldap_errno($this->_LDAP)); + } + + // ATTRIBUTES RETURNED FROM ldap_get_entries ARE ALL LOWER CASE!! + // It's a PHP thing. $entries[$context] = array(); $i = 0; while ($i < $res['count']) { - $extension = $res[$i]['voicemailbox'][0]; - $uid = md5($res[$i]['dn']); + list($extension) = explode('@', $res[$i]['AstVoicemailMailbox'][0]); $entries[$context][$extension] = array(); - $entries[$context][$extension]['uid'] = $uid; - - $j = 0; - $entries[$context][$extension]['dialopts'] = array(); - while ($j < @$res[$i]['asteriskuserdialoptions']['count']) { - $entries[$context][$extension]['dialopts'][] = - $res[$i]['asteriskuserdialoptions'][$j]; - $j++; - } $j = 0; $entries[$context][$extension]['mailboxopts'] = array(); - while ($j < @$res[$i]['asteriskvoicemailboxoptions']['count']) { + while ($j < @$res[$i]['astvoicemailoptions']['count']) { $entries[$context][$extension]['mailboxopts'][] = - $res[$i]['asteriskvoicemailboxoptions'][$j]; + $res[$i]['astvoicemailoptions'][$j]; $j++; } $entries[$context][$extension]['mailboxpin'] = - $res[$i]['voicemailboxpin'][0]; + $res[$i]['astvoicemailpassword'][0]; - @$entries[$context][$extension]['name'] = + $entries[$context][$extension]['name'] = $res[$i]['cn'][0]; - $j = 0; - $entries[$context][$extension]['telephonenumber'] = array(); - while ($j < @$res[$i]['telephonenumber']['count']) { - // Start with 1 for telephone numbers for user convenience - $entries[$context][$extension]['telephonenumber'][$j+1] = - $res[$i]['telephonenumber'][$j]; - $j++; - } - - # FIXME Do some sanity checking here. Also set a default? - @$entries[$context][$extension]['dialtimeout'] = - $res[$i]['asteriskuserdialtimeout'][0]; - @$entries[$context][$extension]['email'] = $res[$i]['mail'][0]; @$entries[$context][$extension]['pageremail'] = - $res[$i]['asteriskpager'][0]; + $res[$i]['astvoicemailpager'][0]; $i++; @@ -326,51 +148,13 @@ type"); } /** - * Returns the name of the user's default context - * - * @return string User's default context - */ - function getHomeContext() - { - # FIXME Cache this lookup? - - $basedn = SHOUT_USERS_BRANCH.','.$this->_params['basedn']; - $filter = '(&'; - $filter .= '(mail='.Auth::getAuth().')'; - $filter .= '(objectClass='.SHOUT_USER_OBJECTCLASS.')'; - $filter .= ')'; - $attributes = array('context'); - - $res = @ldap_search($this->_LDAP, $basedn, $filter, $attributes); - if (!$res) { - return PEAR::raiseError("Unable to locate any customers " . - "underneath ".SHOUT_ASTERISK_BRANCH.",".$this->_params['basedn'] . - " matching those search filters"); - # FIXME Better error string above - } - - $res = ldap_get_entries($this->_LDAP, $res); - - # Assume the user only has one context. The schema enforces this - # FIXME: Handle cases where the managing user isn't a valid telephone - # system user - # FIXME: Do we want to warn? If so, how? This PEAR::Error shows up - # in unfavorable places (ie. perms screen) - if ($res['count'] != 1) { - //return PEAR::raiseError(_("Unable to determine default context")); - return ''; - } - return $res[0]['context'][0]; - } - - /** * Get a context's properties * * @param string $context Context to get properties for * * @return integer Bitfield of properties valid for this context */ - function getContextProperties($context) + public function getContextProperties($context) { $res = @ldap_search($this->_LDAP, @@ -424,7 +208,7 @@ for $context")); * @return array Multi-dimensional associative array of extensions data * */ - function &getDialplan($context, $preprocess = false) + public function getDialplan($context, $preprocess = false) { # FIXME Implement preprocess functionality. Don't forget to cache! static $dialplans = array(); @@ -556,7 +340,7 @@ for $context")); * @return array Array with elements indicating various limits */ # FIXME Figure out how this fits into Shout/Congregation better - function &getLimits($context = null, $extension = null) + public function getLimits($context = null, $extension = null) { $limits = array('telephonenumbersmax', @@ -684,7 +468,7 @@ for $context")); * * @return TRUE on success, PEAR::Error object on error */ - function saveUser($context, $extension, $userdetails) + public function saveUser($context, $extension, $userdetails) { $ldapKey = &$this->_ldapKey; $appKey = &$this->_appKey; @@ -852,7 +636,7 @@ for $context")); * * @return boolean True on success, PEAR::Error object on error */ - function deleteUser($context, $extension) + public function deleteUser($context, $extension) { $ldapKey = &$this->_ldapKey; $appKey = &$this->_appKey; @@ -881,7 +665,7 @@ for $context")); /* Needed because uksort can't take a classed function as its callback arg */ - function _sortexten($e1, $e2) + protected function _sortexten($e1, $e2) { print "$e1 and $e2\n"; $ret = Shout::extensort($e1, $e2); @@ -896,37 +680,75 @@ for $context")); * * @access private */ - function _connect() + protected function _connect() { - if (!$this->_connected) { - # FIXME What else is needed for this assert? - Horde::assertDriverConfig($this->_params, 'storage', - array('hostspec', 'basedn', 'binddn', 'password')); - - # FIXME Add other sane defaults here (mostly objectClass related) - if (!isset($this->_params['userObjectclass'])) { - $this->_params['userObjectclass'] = 'asteriskUser'; + if ($this->_connected) { + return; + } + + if (!Horde_Util::extensionExists('ldap')) { + throw new Horde_Exception('Required LDAP extension not found.'); + } + + Horde::assertDriverConfig($this->_params, $this->_params['class'], + array('hostspec', 'basedn', 'writedn')); + + /* Open an unbound connection to the LDAP server */ + $conn = ldap_connect($this->_params['hostspec'], $this->_params['port']); + if (!$conn) { + Horde::logMessage( + sprintf('Failed to open an LDAP connection to %s.', + $this->_params['hostspec']), + __FILE__, __LINE__, PEAR_LOG_ERR); + throw new Horde_Exception('Internal LDAP error. Details have been logged for the administrator.'); + } + + /* Set hte LDAP protocol version. */ + if (isset($this->_params['version'])) { + $result = ldap_set_option($conn, LDAP_OPT_PROTOCOL_VERSION, + $this->_params['version']); + if ($result === false) { + Horde::logMessage( + sprintf('Set LDAP protocol version to %d failed: [%d] %s', + $this->_params['version'], + ldap_errno($conn), + ldap_error($conn)), + __FILE__, __LINE__, PEAR_LOG_WARNING); + throw new Horde_Exception('Internal LDAP error. Details have been logged for the administrator.', ldap_errno($conn)); } + } - $this->_LDAP = ldap_connect($this->_params['hostspec'], 389); #FIXME - if (!$this->_LDAP) { - Horde::fatal("Unable to connect to LDAP server $hostname on -$port", __FILE__, __LINE__); #FIXME: $port - } - $res = ldap_set_option($this->_LDAP, LDAP_OPT_PROTOCOL_VERSION, -$this->_params['version']); - if (!$res) { - return PEAR::raiseError("Unable to set LDAP protocol version"); - } - $res = ldap_bind($this->_LDAP, $this->_params['binddn'], -$this->_params['password']); - if (!$res) { - return PEAR::raiseError("Unable to bind to the LDAP server. -Check authentication credentials."); + /* Start TLS if we're using it. */ + if (!empty($this->_params['tls'])) { + if (!@ldap_start_tls($conn)) { + Horde::logMessage( + sprintf('STARTTLS failed: [%d] %s', + @ldap_errno($this->_ds), + @ldap_error($this->_ds)), + __FILE__, __LINE__, PEAR_LOG_ERR); } + } - $this->_connected = true; + /* If necessary, bind to the LDAP server as the user with search + * permissions. */ + if (!empty($this->_params['searchdn'])) { + $bind = ldap_bind($conn, $this->_params['searchdn'], + $this->_params['searchpw']); + if ($bind === false) { + Horde::logMessage( + sprintf('Bind to server %s:%d with DN %s failed: [%d] %s', + $this->_params['hostspec'], + $this->_params['port'], + $this->_params['searchdn'], + @ldap_errno($conn), + @ldap_error($conn)), + __FILE__, __LINE__, PEAR_LOG_ERR); + throw new Horde_Exception('Internal LDAP error. Details have been logged for the administrator.', ldap_errno($conn)); + } } - return true; + + /* Store the connection handle at the instance level. */ + $this->_LDAP = $conn; } + } diff --git a/lib/Driver/Sql.php b/lib/Driver/Sql.php new file mode 100644 index 000000000..12166ac18 --- /dev/null +++ b/lib/Driver/Sql.php @@ -0,0 +1,222 @@ +_connect(); + } + + public function getContexts() + { + $this->_connect(); + + $sql = 'SELECT context FROM %s'; + $sql = sprintf($sql, $this->_params['table']); + $vars = array(); + + $result = $this->_db->query($sql, $vars); + if ($result instanceof PEAR_Error) { + throw Shout_Exception($result); + } + + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + if ($row instanceof PEAR_Error) { + throw Shout_Exception($row); + } + + $contexts = array(); + while ($row && !($row instanceof PEAR_Error)) { + /* Add this new foo to the $_foo list. */ + $contexts[] = $row['context']; + + /* Advance to the new row in the result set. */ + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + } + + $result->free(); + return $contexts; + } + + /** + * Get a list of devices for a given context + * + * @param string $context Context in which to search for devices + * @param string $extension Extension in which to search for devices + * + * @return array Array of devices within this context with their information + * + * @access private + */ + public function getDevices($context, $extension) + { + $sql = 'SELECT id, name, callerid, context, host, permit, nat, ' . + 'secret, disallow, allow FROM %s WHERE mailbox = ?'; + $sql = sprintf($sql, $this->_params['table']); + $args = array($extension . '@' . $context); + + $result = $this->_db->query($sql, $args); + if (is_a($result instanceof PEAR_Error)) { + throw Shout_Exception($result); + } + + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + if ($row instanceof PEAR_Error) { + throw Shout_Exception($row); + } + + $devices = array(); + while ($row && !($row instanceof PEAR_Error)) { + /* Add this new foo to the $_foo list. */ + $devices[] = $row; + + /* Advance to the new row in the result set. */ + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + } + + $result->free(); + + } + + /** + * Get a list of users valid for the contexts + * + * @param string $context Context on which to search + * + * @return array User information indexed by voice mailbox number + */ + public function getExtensions($context) + { + throw new Shout_Exception("Not implemented yet."); + } + + /** + * Save a user to the LDAP tree + * + * @param string $context Context to which the user should be added + * + * @param string $extension Extension to be saved + * + * @param array $userdetails Phone numbers, PIN, options, etc to be saved + * + * @return TRUE on success, PEAR::Error object on error + */ + public function saveExtension($context, $extension, $userdetails) + { + throw new Shout_Exception("Not implemented."); + } + + /** + * Deletes a user from the LDAP tree + * + * @param string $context Context to delete the user from + * @param string $extension Extension of the user to be deleted + * + * @return boolean True on success, PEAR::Error object on error + */ + public function deleteExtension($context, $extension) + { + throw new Shout_Exception("Not implemented."); + } + + /** + * Attempts to open a persistent connection to the SQL server. + * + * @throws Horde_Exception + */ + protected function _connect() + { + if ($this->_connected) { + return; + } + + Horde::assertDriverConfig($this->_params, $this->_params['class'], + array('phptype', 'charset', 'table')); + + if (!isset($this->_params['database'])) { + $this->_params['database'] = ''; + } + if (!isset($this->_params['username'])) { + $this->_params['username'] = ''; + } + if (!isset($this->_params['hostspec'])) { + $this->_params['hostspec'] = ''; + } + + /* Connect to the SQL server using the supplied parameters. */ + $this->_write_db = DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if ($this->_write_db instanceof PEAR_Error) { + throw Horde_Exception($this->_write_db); + } + + // Set DB portability options. + switch ($this->_write_db->phptype) { + case 'mssql': + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + + default: + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + /* Check if we need to set up the read DB connection seperately. */ + if (!empty($this->_params['splitread'])) { + $params = array_merge($this->_params, $this->_params['read']); + $this->_db = DB::connect($params, + array('persistent' => !empty($params['persistent']))); + if ($this->_db instanceof PEAR_Error) { + throw Horde_Exception($this->_db); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + } else { + /* Default to the same DB handle for the writer too. */ + $this->_db = $this->_write_db; + } + + $this->_connected = true; + } + + /** + * Disconnects from the SQL server and cleans up the connection. + */ + protected function _disconnect() + { + if ($this->_connected) { + $this->_connected = false; + $this->_db->disconnect(); + $this->_write_db->disconnect(); + } + } + +} diff --git a/lib/Exception.php b/lib/Exception.php new file mode 100644 index 000000000..301bcf7d8 --- /dev/null +++ b/lib/Exception.php @@ -0,0 +1,4 @@ +checkContextType($context, 'users')) { - - $url = Horde::applicationUrl('usermgr.php'); + if (Shout::checkRights($permprefix . ':extensions', null, 1)) { + $url = Horde::applicationUrl('extensions.php'); $url = Horde_Util::addParameter($url, 'context', $context); - $tabs->addTab(_("_User Manager"), $url, 'usermgr'); + $tabs->addTab(_("_Extensions"), $url, 'usermgr'); } - if (Shout::checkRights($permprefix . ':dialplan', null, 1) && - $shout->checkContextType($context, 'dialplan')) { - + if (Shout::checkRights($permprefix . ':dialplan', null, 1)) { $url = Horde::applicationUrl('dialplan.php'); $url = Horde_Util::addParameter($url, 'context', $context); - $tabs->addTab(_("_Dial Plan"), $url, 'dialplan'); + $tabs->addTab(_("_Automated Attendant"), $url, 'dialplan'); } - if (Shout::checkRights($permprefix . ':conference', null, 1) && - $shout->checkContextType($context, 'conference')) { - + if (Shout::checkRights($permprefix . ':conference', null, 1)) { $url = Horde::applicationUrl('conference.php'); $url = Horde_Util::addParameter($url, 'context', $context); $tabs->addTab(_("_Conference Rooms"), $url, 'conference'); } - if (Shout::checkRights($permprefix . ':moh', null, 1) && - $shout->checkContextType($context, "moh")) { - + if (Shout::checkRights($permprefix . ':moh', null, 1)) { $url = Horde::applicationUrl('moh.php'); $url = Horde_Util::addParameter($url, 'context', $context); $tabs->addTab(_("_Music on Hold"), $url, 'moh'); } - if ($perms->hasPermission('shout:superadmin', Horde_Auth::getAuth(), PERMS_SHOW|PERMS_READ)) { - $url = Horde::applicationUrl('security.php'); - $url = Horde_Util::addParameter($url, 'context', $context); - $tabs->addTab(_("_Security"), $url, 'security'); - } - return $tabs; } @@ -189,12 +175,4 @@ $ret = ($test & $permmask) == $permmask; print "Shout::checkRights() returning $ret"; return ($test & $permmask) == $permmask; } - - static public function getContextTypes() - { - return array(SHOUT_CONTEXT_CUSTOMERS => _("Customers"), - SHOUT_CONTEXT_EXTENSIONS => _("Dialplan"), - SHOUT_CONTEXT_MOH => _("Music On Hold"), - SHOUT_CONTEXT_CONFERENCE => _("Conference Calls")); - } } diff --git a/lib/base.php b/lib/base.php index b46161d33..4ec1df0c2 100644 --- a/lib/base.php +++ b/lib/base.php @@ -38,7 +38,6 @@ $conf = &$GLOBALS['conf']; // Ensure Shout is properly configured before use $shout_configured = (@is_readable(SHOUT_BASE . '/config/conf.php')); if (!$shout_configured) { - require SHOUT_BASE . '/../lib/Test.php'; Horde_Test::configFilesMissing('Shout', SHOUT_BASE, array('conf.php')); } @@ -60,16 +59,15 @@ $notification->attach('status'); //// UI classes. //require_once 'Horde/UI/Tabs.php'; -$shout = Shout_Driver::singleton(); - -//// Horde libraries. -//require_once 'Horde/Help.php'; +$shout_storage = Shout_Driver::factory('storage'); +$shout_extensions = Shout_Driver::factory('extensions'); +$shout_devices = Shout_Driver::factory('devices'); $context = Horde_Util::getFormData('context'); $section = Horde_Util::getFormData('section'); try { - $contexts = $shout->getContexts(); + $contexts = $shout_storage->getContexts(); } catch (Shout_Exception $e) { $notification->push($e); $contexts = false; @@ -78,12 +76,12 @@ try { if (count($contexts) == 1) { // Default to the user's only context $context = $contexts[0]; -} elseif (!$context) { - try { - // Attempt to locate the user's "home" context - $context = $shout->getHomeContext(); - } catch (Shout_Exception $e) { - $notification->push($e); - } +} elseif (!empty($context) && !in_array($context, $contexts)) { + $notification->push('You do not have permission to access that context.', 'horde.error'); + $context = false; +} elseif (!empty($context)) { + $notification->push("Please select a context to continue.", 'horde.info'); $context = false; -} \ No newline at end of file +} + +$_SESSION['shout']['context'] = $context; \ No newline at end of file diff --git a/templates/extensions/list.inc b/templates/extensions/list.inc index dc0d89d64..cddc8fffa 100644 --- a/templates/extensions/list.inc +++ b/templates/extensions/list.inc @@ -11,7 +11,7 @@ $user) { + foreach ($extensions as $extension => $user) { $rowcolor = $line % 2; $line++; $url = Horde::applicationUrl("index.php");