--- /dev/null
+<?php
+/**
+ * The Horde:: class provides the functionality shared by all Horde
+ * applications.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jon Parise <jon@horde.org>
+ * @package Core
+ */
+class Horde
+{
+ /**
+ * Log instance.
+ *
+ * @var Log
+ */
+ static protected $_logger;
+
+ /**
+ * Has compression been started?
+ *
+ * @var boolean
+ */
+ static protected $_compressStart = false;
+
+ /**
+ * The access keys already used in this page.
+ *
+ * @var array
+ */
+ static protected $_used = array();
+
+ /**
+ * The labels already used in this page.
+ *
+ * @var array
+ */
+ static protected $_labels = array();
+
+ /**
+ * Are accesskeys supported on this system.
+ *
+ * @var boolean
+ */
+ static protected $_noAccessKey;
+
+ /**
+ * Whether the hook has already been loaded.
+ *
+ * @var array
+ */
+ static protected $_hooksLoaded = array();
+
+ /**
+ * Logs a message to the global Horde log backend.
+ *
+ * @param mixed $message Either a string or an object with a
+ * getMessage() method (e.g. PEAR_Error,
+ * Exception).
+ * @param string $file What file was the log function called from
+ * (e.g. __FILE__)?
+ * @param integer $line What line was the log function called from
+ * (e.g. __LINE__)?
+ * @param integer $priority The priority of the message. One of:
+ * <pre>
+ * PEAR_LOG_EMERG
+ * PEAR_LOG_ALERT
+ * PEAR_LOG_CRIT
+ * PEAR_LOG_ERR
+ * PEAR_LOG_WARNING
+ * PEAR_LOG_NOTICE
+ * PEAR_LOG_INFO
+ * PEAR_LOG_DEBUG
+ * </pre>
+ */
+ static public function logMessage($message, $file, $line,
+ $priority = PEAR_LOG_INFO)
+ {
+ $logger = &self::getLogger();
+ if ($logger === false) {
+ return;
+ }
+
+ if ($priority > $GLOBALS['conf']['log']['priority']) {
+ return;
+ }
+
+ if ($message instanceof PEAR_Error) {
+ $userinfo = $message->getUserInfo();
+ $message = $message->getMessage();
+ if (!empty($userinfo)) {
+ if (is_array($userinfo)) {
+ $old_error = error_reporting(0);
+ $userinfo = implode(', ', $userinfo);
+ error_reporting($old_error);
+ }
+ $message .= ': ' . $userinfo;
+ }
+ } elseif (is_object($message) &&
+ is_callable(array($message, 'getMessage'))) {
+ $message = $message->getMessage();
+ }
+
+ $app = isset($GLOBALS['registry']) ? $GLOBALS['registry']->getApp() : 'horde';
+ $message = '[' . $app . '] ' . $message . ' [pid ' . getmypid() . ' on line ' . $line . ' of "' . $file . '"]';
+
+ /* Make sure to log in the system's locale. */
+ $locale = setlocale(LC_TIME, 0);
+ setlocale(LC_TIME, 'C');
+
+ $logger->log($message, $priority);
+
+ /* Restore original locale. */
+ setlocale(LC_TIME, $locale);
+
+ return true;
+ }
+
+ /**
+ * Get an instantiated instance of the configured logger, if enabled.
+ * getLogger() will fatally exit if a Log object can not be
+ * instantiated.
+ *
+ * @return mixed Log object on success, false if disabled.
+ */
+ static public function getLogger()
+ {
+ global $conf;
+
+ if (empty($conf['log']['enabled'])) {
+ $ret = false;
+ return $ret;
+ }
+
+ if (isset(self::$_logger)) {
+ return self::$_logger;
+ }
+
+ // Try to make sure that we can log messages somehow.
+ if (empty($conf['log']) ||
+ empty($conf['log']['type']) ||
+ empty($conf['log']['name']) ||
+ empty($conf['log']['ident']) ||
+ !isset($conf['log']['params'])) {
+ self::fatal(new Horde_Exception('Horde is not correctly configured to log error messages. You must configure at least a text file log in horde/config/conf.php.'), __FILE__, __LINE__, false);
+ }
+
+ self::$_logger = Log::singleton($conf['log']['type'],
+ $conf['log']['name'],
+ $conf['log']['ident'],
+ $conf['log']['params']);
+ if (!is_a(self::$_logger, 'Log')) {
+ self::fatal(new Horde_Exception('An error has occurred. Furthermore, Horde encountered an error attempting to log this error. Please check your Horde logging configuration in horde/config/conf.php.'), __FILE__, __LINE__, false);
+ }
+
+ return self::$_logger;
+ }
+
+ /**
+ * Destroys any existing session on login and make sure to use a new
+ * session ID, to avoid session fixation issues. Should be called before
+ * checking a login.
+ */
+ static public function getCleanSession()
+ {
+ // Make sure to force a completely new session ID and clear all
+ // session data.
+ session_regenerate_id(true);
+ session_unset();
+
+ /* Reset cookie timeouts, if necessary. */
+ if (!empty($GLOBALS['conf']['session']['timeout'])) {
+ $app = $GLOBALS['registry']->getApp();
+ if (Horde_Secret::clearKey($app)) {
+ Horde_Secret::setKey($app);
+ }
+ Horde_Secret::setKey('auth');
+ }
+ }
+
+ /**
+ * Aborts with a fatal error, displaying debug information to the user.
+ *
+ * @param mixed $error Either a string or an object with a getMessage()
+ * method (e.g. PEAR_Error, Exception).
+ * @todo Better Exception handling
+ * @param integer $file The file in which the error occured.
+ * @param integer $line The line on which the error occured.
+ * @param boolean $log Log this message via logMessage()?
+ */
+ static public function fatal($error, $file = null, $line = null,
+ $log = true)
+ {
+ $admin = Horde_Auth::isAdmin();
+ $cli = Horde_Cli::runningFromCLI();
+
+ $errortext = '<h1>' . _("A fatal error has occurred") . '</h1>';
+ if ($error instanceof PEAR_Error) {
+ $info = array_merge(array('file' => 'conf.php', 'variable' => '$conf'),
+ array($error->getUserInfo()));
+
+ switch ($error->getCode()) {
+ case Horde_Util::HORDE_ERROR_DRIVER_CONFIG_MISSING:
+ $message = sprintf(_("No configuration information specified for %s."), $info['name']) . '<br />' .
+ sprintf(_("The file %s should contain some %s settings."),
+ $GLOBALS['registry']->get('fileroot') . '/config/' . $info['file'],
+ sprintf("%s['%s']['params']", $info['variable'], $info['driver']));
+ break;
+
+ case Horde_Util::HORDE_ERROR_DRIVER_CONFIG:
+ $message = sprintf(_("Required \"%s\" not specified in %s configuration."), $info['field'], $info['name']) . '<br />' .
+ sprintf(_("The file %s should contain a %s setting."),
+ $GLOBALS['registry']->get('fileroot') . '/config/' . $info['file'],
+ sprintf("%s['%s']['params']['%s']", $info['variable'], $info['driver'], $info['field']));
+ break;
+
+ default:
+ $message = $error->getMessage();
+ break;
+ }
+
+ $errortext .= '<h3>' . htmlspecialchars($message) . '</h3>';
+ } elseif (is_object($error) && method_exists($error, 'getMessage')) {
+ $errortext .= '<h3>' . htmlspecialchars($error->getMessage()) . '</h3>';
+ } elseif (is_string($error)) {
+ $errortext .= '<h3>' . htmlspecialchars($error) . '</h3>';
+ }
+
+ if (is_null($file) && $error instanceof Exception) {
+ $file = $error->getFile();
+ }
+ if (is_null($line) && $error instanceof Exception) {
+ $line = $error->getLine();
+ }
+
+ if ($admin) {
+ $errortext .= '<p><code>' . sprintf(_("[line %d of %s]"), $line, $file) . '</code></p>';
+ if (is_object($error)) {
+ $errortext .= '<h3>' . _("Details:") . '</h3>';
+ $errortext .= '<h4>' . _("The full error message is logged in Horde's log file, and is shown below only to administrators. Non-administrative users will not see error details.") . '</h4>';
+ if (extension_loaded('xdebug')) {
+ $errortext .= '<br />' . print_r($error, true);
+ } else {
+ $errortext .= '<p><pre>' . htmlspecialchars(print_r($error, true)) . '</pre></p>';
+ }
+ }
+ } elseif ($log) {
+ $errortext .= '<h3>' . _("Details have been logged for the administrator.") . '</h3>';
+ }
+
+ // Log the error via logMessage() if requested.
+ if ($log) {
+ self::logMessage($error, $file, $line, PEAR_LOG_EMERG);
+ }
+
+ if ($cli) {
+ echo strip_tags(str_replace(array('<br />', '<p>', '</p>', '<h1>', '</h1>', '<h3>', '</h3>'), "\n", $errortext));
+ } else {
+ echo <<< HTML
+<html>
+<head><title>Horde :: Fatal Error</title></head>
+<body style="background:#fff; color:#000">$errortext</body>
+</html>
+HTML;
+ }
+ exit(1);
+ }
+
+ /**
+ * Adds the javascript code to the output (if output has already started)
+ * or to the list of script files to include via includeScriptFiles().
+ *
+ * @param string $file The full javascript file name.
+ * @param string $app The application name. Defaults to the current
+ * application.
+ * @param boolean $direct Include the file directly without passing it
+ * through javascript.php
+ * @param boolean $full Output a full URL
+ */
+ static public function addScriptFile($file, $app = null, $direct = false,
+ $full = false)
+ {
+ $hsf = &Horde_Script_Files::singleton();
+ $hsf->add($file, $app, $direct, $full);
+ }
+
+ /**
+ * Includes javascript files that were needed before any headers were sent.
+ */
+ static public function includeScriptFiles()
+ {
+ $hsf = &Horde_Script_Files::singleton();
+ $hsf->includeFiles();
+ }
+
+ /**
+ * Provide a list of script files to be included in the current page.
+ *
+ * @var array
+ */
+ static public function listScriptFiles()
+ {
+ $hsf = &Horde_Script_Files::singleton();
+ return $hsf->listFiles();
+ }
+
+ /**
+ * Disable auto-loading of the horde.js script.
+ * Needs to auto-load by default for BC.
+ *
+ * @todo Remove for Horde 4
+ */
+ static public function disableAutoloadHordeJS()
+ {
+ $hsf = &Horde_Script_Files::singleton();
+ $hsf->disableAutoloadHordeJS();
+ }
+
+ /**
+ * Get a token for protecting a form.
+ *
+ * @param string $slug Slug name.
+ *
+ * @return string Token string.
+ */
+ static public function getRequestToken($slug)
+ {
+ $token = Horde_Token::generateId($slug);
+ $_SESSION['horde_form_secrets'][$token] = time();
+ return $token;
+ }
+
+ /**
+ * Check if a token for a form is valid.
+ *
+ * @param string $slug Slug name.
+ * @param string $token Token to check.
+ *
+ * @throws Horde_Exception
+ */
+ static public function checkRequestToken($slug, $token)
+ {
+ if (empty($_SESSION['horde_form_secrets'][$token])) {
+ throw new Horde_Exception(_("We cannot verify that this request was really sent by you. It could be a malicious request. If you intended to perform this action, you can retry it now."));
+ }
+
+ if (($_SESSION['horde_form_secrets'][$token] + $GLOBALS['conf']['urls']['token_lifetime'] * 60) < time()) {
+ throw new Horde_Exception(sprintf(_("This request cannot be completed because the link you followed or the form you submitted was only valid for %s minutes. Please try again now."), $GLOBALS['conf']['urls']['token_lifetime']));
+ }
+
+ return true;
+ }
+
+ /**
+ * Add a signature + timestamp to a query string and return the signed query
+ * string.
+ *
+ * @param string $queryString The query string to sign.
+ * @param integer $now The timestamp at which to sign. Leave blank
+ * for generating signatures; specify when
+ * testing.
+ *
+ * @return string The signed query string.
+ */
+ static public function signQueryString($queryString, $now = null)
+ {
+ if (!isset($GLOBALS['conf']['secret_key'])) {
+ return $queryString;
+ }
+
+ if (is_null($now)) {
+ $now = time();
+ }
+
+ $queryString .= '&_t=' . $now . '&_h=';
+
+ return $queryString . Horde_Util::uriB64Encode(hash_hmac('sha1', $queryString, $GLOBALS['conf']['secret_key'], true));
+ }
+
+ /**
+ * Verify a signature and timestamp on a query string.
+ *
+ * @param string $data The signed query string.
+ * @param integer $now The current time (can override for testing).
+ *
+ * @return boolean Whether or not the string was valid.
+ */
+ static public function verifySignedQueryString($data, $now = null)
+ {
+ if (is_null($now)) {
+ $now = time();
+ }
+
+ $pos = strrpos($data, '&_h=');
+ if ($pos === false) {
+ return false;
+ }
+ $pos += 4;
+
+ $queryString = substr($data, 0, $pos);
+ $hmac = substr($data, $pos);
+
+ if ($hmac != Horde_Util::uriB64Encode(hash_hmac('sha1', $queryString, $GLOBALS['conf']['secret_key'], true))) {
+ return false;
+ }
+
+ // String was not tampered with; now validate timestamp
+ parse_str($queryString, $values);
+
+ return !($values['_t'] + $GLOBALS['conf']['urls']['hmac_lifetime'] * 60 < $now);
+ }
+
+ /**
+ * Checks if link should be shown and return the necessary code.
+ *
+ * @param string $type Type of link to display
+ * @param string $app The name of the current Horde application.
+ * @param boolean $override Override Horde settings?
+ * @param boolean $referrer Include the current page as the referrer
+ * (url=)?
+ *
+ * @return string The HTML to create the link.
+ */
+ static public function getServiceLink($type, $app, $override = false,
+ $referrer = true)
+ {
+ if (!self::showService($type, $override)) {
+ return false;
+ }
+
+ switch ($type) {
+ case 'help':
+ if ($GLOBALS['browser']->hasFeature('javascript')) {
+ self::addScriptFile('popup.js', 'horde', true);
+ }
+ $url = self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/help/', true);
+ return Horde_Util::addParameter($url, 'module', $app);
+
+ case 'problem':
+ return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/problem.php?return_url=' . urlencode(self::selfUrl(true, true, true)));
+
+ case 'logout':
+ return self::url(Horde_Auth::addLogoutParameters($GLOBALS['registry']->get('webroot', 'horde') . '/login.php', Horde_Auth::REASON_LOGOUT));
+
+ case 'login':
+ return Horde_Auth::getLoginScreen('', $referrer ? self::selfUrl(true) : null);
+
+ case 'options':
+ global $conf;
+ if (($conf['prefs']['driver'] != '') && ($conf['prefs']['driver'] != 'none')) {
+ return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/prefs.php?app=' . $app);
+ }
+ break;
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns a stdClass response object with added notification information.
+ *
+ * @param string $data The 'response' data.
+ * @param Notification_Listener $listener If set, adds notification
+ * information to object.
+ * @param boolean $auto If true, the ajax application
+ * will automatically display the
+ * notification. If false, the
+ * callback handler is responsible
+ * for displaying the notification.
+ */
+ static public function prepareResponse($data = null, $listener = null,
+ $auto = true)
+ {
+ $response = new stdClass();
+ $response->response = $data;
+ if ($listener) {
+ $GLOBALS['notification']->notify(array('listeners' => 'status'));
+ $stack = $listener->getStack();
+ if (!empty($stack)) {
+ $response->msgs = $stack;
+ if (!(bool)$auto) {
+ $response->msgs_noauto = true;
+ }
+ }
+ }
+
+ return $response;
+ }
+
+ /**
+ * Send response data to browser.
+ *
+ * @param mixed $data The data to serialize and send to the browser.
+ * @param string $ct The content-type to send the data with. Either
+ * 'json', 'js-json', 'html', 'plain', and 'xml'.
+ */
+ static public function sendHTTPResponse($data, $ct)
+ {
+ $charset = Horde_Nls::getCharset();
+
+ // Output headers and encoded response.
+ switch ($ct) {
+ case 'json':
+ case 'js-json':
+ /* JSON responses are a structured object which always
+ * includes the response in a member named 'response', and an
+ * additional array of messages in 'msgs' which may be updates
+ * for the server or notification messages.
+ *
+ * Make sure no null bytes sneak into the JSON output stream.
+ * Null bytes cause IE to stop reading from the input stream,
+ * causing malformed JSON data and a failed request. These
+ * bytes don't seem to break any other browser, but might as
+ * well remove them anyway.
+ *
+ * Finally, add prototypejs security delimiters to returned
+ * JSON. */
+ $s_data = '/*-secure-' .
+ Horde_String::convertCharset(str_replace("\00", '', Horde_Serialize::serialize($data, Horde_Serialize::JSON, $charset)), 'UTF-8') .
+ '*/';
+
+ if ($ct == 'json') {
+ header('Content-Type: application/json');
+ echo $s_data;
+ } else {
+ header('Content-Type: text/html; charset=' . $charset);
+ echo htmlspecialchars($s_data);
+ }
+ break;
+
+ case 'html':
+ case 'plain':
+ case 'xml':
+ header('Content-Type: text/' . $ct . '; charset=' . $charset);
+ echo $data;
+ break;
+
+ default:
+ echo $data;
+ }
+
+ exit;
+ }
+
+ /**
+ * Is the current HTTP connection considered secure?
+ * @TODO Move this to the request classes!
+ *
+ * @return boolean
+ */
+ static public function isConnectionSecure()
+ {
+ if ($GLOBALS['browser']->usingSSLConnection()) {
+ return true;
+ }
+
+ if (!empty($GLOBALS['conf']['safe_ips'])) {
+ if (reset($GLOBALS['conf']['safe_ips']) == '*') {
+ return true;
+ }
+
+ /* $_SERVER['HTTP_X_FORWARDED_FOR'] is user data and not
+ * reliable. We don't consult it for safe IPs. We also have to
+ * assume that if it is present, the user is coming through a proxy
+ * server. If so, we don't count any non-SSL connection as safe, no
+ * matter the source IP. */
+ if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $remote_addr = $_SERVER['REMOTE_ADDR'];
+ foreach ($GLOBALS['conf']['safe_ips'] as $safe_ip) {
+ $safe_ip = preg_replace('/(\.0)*$/', '', $safe_ip);
+ if (strpos($remote_addr, $safe_ip) === 0) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Throws an exception if not using a secure connection.
+ *
+ * @throws Horde_Exception
+ */
+ static public function requireSecureConnection()
+ {
+ if (!self::isConnectionSecure()) {
+ throw new Horde_Exception(_("The encryption features require a secure web connection."));
+ }
+ }
+
+ /**
+ * TODO
+ *
+ * @param string $type The type of link.
+ * @param boolean $override Override Horde settings?
+ *
+ * @return boolean True if the link is to be shown.
+ */
+ static public function showService($type, $override = false)
+ {
+ global $conf;
+
+ if (empty($conf['menu']['links'][$type])) {
+ return false;
+ }
+
+ switch ($conf['menu']['links'][$type]) {
+ case 'all':
+ return true;
+
+ case 'never':
+ return $override;
+
+ case 'authenticated':
+ return $override || (bool)Horde_Auth::getAuth();
+
+ default:
+ return $override;
+ }
+ }
+
+ /**
+ * Loads global and vhost specific configuration files.
+ *
+ * @param string $config_file The name of the configuration file.
+ * @param string|array $var_names The name(s) of the variable(s) that
+ * is/are defined in the configuration
+ * file.
+ * @param string $app The application. Defaults to the current
+ * application.
+ * @param boolean $show_output If true, the contents of the requested
+ * config file are simply output instead of
+ * loaded into a variable.
+ *
+ * @return mixed The value of $var_names, in a compact()'ed array if
+ * $var_names is an array.
+ * @throws Horde_Exception
+ */
+ static public function loadConfiguration($config_file, $var_names = null,
+ $app = null, $show_output = false)
+ {
+ global $registry;
+
+ if (is_null($app)) {
+ $app = $registry->getApp();
+ }
+
+ // Track if we've included some version (main or vhosted) of
+ // the config file.
+ $was_included = false;
+
+ // Load global configuration file.
+ $config_dir = (($app == 'horde') && defined('HORDE_BASE'))
+ ? HORDE_BASE . '/config/'
+ : $registry->get('fileroot', $app) . '/config/';
+ $file = $config_dir . $config_file;
+
+ if (file_exists($file)) {
+ /* If we are not exporting variables located in the configuration
+ * file, or we are not capturing the output, then there is no
+ * need to load the configuration file more than once. */
+ ob_start();
+ $success = (is_null($var_names) && !$show_output)
+ ? include_once $file
+ : include $file;
+ $output = ob_get_clean();
+
+ if (!empty($output) && !$show_output) {
+ throw new Horde_Exception(sprintf('Failed to import configuration file "%s": ', $file) . strip_tags($output));
+ }
+
+ if (!$success) {
+ throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $file));
+ }
+
+ $was_included = true;
+ }
+
+ // Load vhost configuration file.
+ if (!empty($conf['vhosts']) || !empty($GLOBALS['conf']['vhosts'])) {
+ $server_name = isset($GLOBALS['conf'])
+ ? $GLOBALS['conf']['server']['name']
+ : $conf['server']['name'];
+ $file = $config_dir . substr($config_file, 0, -4) . '-' . $server_name . '.php';
+
+ if (file_exists($file)) {
+ ob_start();
+ $success = (is_null($var_names) && !$show_output)
+ ? include_once $file
+ : include $file;
+ $output = ob_get_clean();
+
+ if (!empty($output) && !$show_output) {
+ throw new Horde_Exception(sprintf('Failed to import configuration file "%s": ', $file) . strip_tags($output));
+ }
+
+ if (!$success) {
+ throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $file));
+ }
+
+ $was_included = true;
+ }
+ }
+
+ // Return an error if neither main or vhosted versions of the config
+ // file exist.
+ if (!$was_included) {
+ throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $config_dir . $config_file));
+ }
+
+ if (isset($output) && $show_output) {
+ echo $output;
+ }
+
+ if (is_null($var_names)) {
+ return;
+ } elseif (is_array($var_names)) {
+ return compact($var_names);
+ } elseif (isset($$var_names)) {
+ return $$var_names;
+ } else {
+ return array();
+ }
+ }
+
+ /**
+ * Returns the driver parameters for the specified backend.
+ *
+ * @param mixed $backend The backend system (e.g. 'prefs', 'categories',
+ * 'contacts') being used.
+ * The used configuration array will be
+ * $conf[$backend]. If an array gets passed, it will
+ * be $conf[$key1][$key2].
+ * @param string $type The type of driver.
+ *
+ * @return array The connection parameters.
+ */
+ static public function getDriverConfig($backend, $type = 'sql')
+ {
+ global $conf;
+
+ $c = null;
+ if (is_array($backend)) {
+ $c = Horde_Array::getElement($conf, $backend);
+ } elseif (isset($conf[$backend])) {
+ $c = $conf[$backend];
+ }
+
+ if (!is_null($c) && isset($c['params'])) {
+ $c['params']['umask'] = $conf['umask'];
+ if (isset($conf[$type])) {
+ return array_merge($conf[$type], $c['params']);
+ } else {
+ return $c['params'];
+ }
+ }
+
+ return isset($conf[$type]) ? $conf[$type] : array();
+ }
+
+
+ /**
+ * Returns the VFS driver parameters for the specified backend.
+ *
+ * @param string $name The VFS system name (e.g. 'images', 'documents')
+ * being used.
+ *
+ * @return array A hash with the VFS parameters; the VFS driver in 'type'
+ * and the connection parameters in 'params'.
+ * @throws Horde_Exception
+ */
+ static public function getVFSConfig($name)
+ {
+ global $conf;
+
+ if (!isset($conf[$name]['type'])) {
+ throw new Horde_Exception(_("You must configure a VFS backend."));
+ }
+
+ $vfs = ($conf[$name]['type'] == 'horde')
+ ? $conf['vfs']
+ : $conf[$name];
+
+ if ($vfs['type'] == 'sql') {
+ $vfs['params'] = self::getDriverConfig($name, 'sql');
+ }
+
+ return $vfs;
+ }
+
+ /**
+ * Return the driver and parameters for the current mailer configuration.
+ *
+ * @return array Array with driver name and parameter hash.
+ */
+ static public function getMailerConfig()
+ {
+ $mail_driver = $GLOBALS['conf']['mailer']['type'];
+ $mail_params = $GLOBALS['conf']['mailer']['params'];
+ if ($mail_driver == 'smtp' && $mail_params['auth'] &&
+ empty($mail_params['username'])) {
+ $mail_params['username'] = Horde_Auth::getAuth();
+ $mail_params['password'] = Horde_Auth::getCredential('password');
+ }
+
+ return array($mail_driver, $mail_params);
+ }
+
+ /**
+ * Checks if all necessary parameters for a driver configuration
+ * are set and throws a fatal error with a detailed explanation
+ * how to fix this, if something is missing.
+ *
+ * @param array $params The configuration array with all parameters.
+ * @param string $driver The key name (in the configuration array) of
+ * the driver.
+ * @param array $fields An array with mandatory parameter names for
+ * this driver.
+ * @param string $name The clear text name of the driver. If not
+ * specified, the application name will be used.
+ * @param string $file The configuration file that should contain
+ * these settings.
+ * @param string $variable The name of the configuration variable.
+ */
+ static public function assertDriverConfig($params, $driver, $fields,
+ $name = null,
+ $file = 'conf.php',
+ $variable = '$conf')
+ {
+ global $registry;
+
+ // Don't generate a fatal error if we fail during or before
+ // Registry instantiation.
+ if (is_null($name)) {
+ $name = isset($registry) ? $registry->getApp() : '[unknown]';
+ }
+ $fileroot = isset($registry) ? $registry->get('fileroot') : '';
+
+ if (!is_array($params) || !count($params)) {
+ self::fatal(new Horde_Exception(
+ sprintf(_("No configuration information specified for %s."), $name) . "\n\n" .
+ sprintf(_("The file %s should contain some %s settings."),
+ $fileroot . '/config/' . $file,
+ sprintf("%s['%s']['params']", $variable, $driver))),
+ __FILE__, __LINE__);
+ }
+
+ foreach ($fields as $field) {
+ if (!isset($params[$field])) {
+ self::fatal(new Horde_Exception(
+ sprintf(_("Required \"%s\" not specified in %s configuration."), $field, $name) . "\n\n" .
+ sprintf(_("The file %s should contain a %s setting."),
+ $fileroot . '/config/' . $file,
+ sprintf("%s['%s']['params']['%s']", $variable, $driver, $field))),
+ __FILE__, __LINE__);
+ }
+ }
+ }
+
+ /**
+ * Returns a session-id-ified version of $uri.
+ * If a full URL is requested, all parameter separators get converted to
+ * "&", otherwise to "&".
+ *
+ * @param string $uri The URI to be modified.
+ * @param boolean $full Generate a full (http://server/path/)
+ * URL.
+ * @param integer $append_session 0 = only if needed, 1 = always, -1 =
+ * never.
+ * @param boolean $force_ssl Ignore $conf['use_ssl'] and force
+ * creation of a SSL URL?
+ *
+ * @return string The URL with the session id appended (if needed).
+ */
+ static public function url($uri, $full = false, $append_session = 0,
+ $force_ssl = false)
+ {
+ if ($force_ssl) {
+ $full = true;
+ }
+
+ if ($full) {
+ global $conf, $registry, $browser;
+
+ /* Store connection parameters in local variables. */
+ $server_name = $conf['server']['name'];
+ $server_port = $conf['server']['port'];
+
+ $protocol = 'http';
+ if ($conf['use_ssl'] == 1) {
+ $protocol = 'https';
+ } elseif ($conf['use_ssl'] == 2 &&
+ $browser->usingSSLConnection()) {
+ $protocol = 'https';
+ } elseif ($conf['use_ssl'] == 3) {
+ $server_port = '';
+ if ($force_ssl) {
+ $protocol = 'https';
+ }
+ }
+
+ /* If using non-standard ports, add the port to the URL. */
+ if (!empty($server_port) &&
+ ((($protocol == 'http') && ($server_port != 80)) ||
+ (($protocol == 'https') && ($server_port != 443)))) {
+ $server_name .= ':' . $server_port;
+ }
+
+ /* Store the webroot in a local variable. */
+ $webroot = $registry->get('webroot');
+
+ $url = $protocol . '://' . $server_name;
+ if (preg_match('|^([\w+-]{1,20})://|', $webroot)) {
+ /* Don't prepend to webroot if it's already absolute. */
+ $url = '';
+ }
+
+ if (substr($uri, 0, 1) != '/') {
+ /* Simple case for http:// absolute webroots. */
+ if (preg_match('|^([\w+-]{1,20})://|', $uri)) {
+ $url = $uri;
+ } elseif (substr($webroot, -1) == '/') {
+ $url .= $webroot . $uri;
+ } else {
+ $url .= $webroot . '/' . $uri;
+ }
+ } else {
+ $url .= $uri;
+ }
+ } else {
+ $url = $uri;
+
+ if (!empty($_SERVER['HTTP_HOST'])) {
+ // Don't generate absolute URLs if we don't have to.
+ if (preg_match('|^([\w+-]{1,20}://' . preg_quote($_SERVER['HTTP_HOST'], '|') . ')/|', $url, $matches)) {
+ $url = substr($url, strlen($matches[1]));
+ }
+ }
+ }
+
+ if (empty($GLOBALS['conf']['session']['use_only_cookies']) &&
+ (($append_session == 1) ||
+ (($append_session == 0) &&
+ !isset($_COOKIE[session_name()])))) {
+ $url = Horde_Util::addParameter($url, session_name(), session_id());
+ }
+
+ if ($full) {
+ /* We need to run the replace twice, because we only catch every
+ * second match. */
+ return preg_replace(array('/(=?.*?)&(.*?=)/',
+ '/(=?.*?)&(.*?=)/'),
+ '$1&$2', $url);
+ } elseif (preg_match('/=.*&.*=/', $url)) {
+ return $url;
+ } else {
+ return htmlentities($url);
+ }
+ }
+
+ /**
+ * Returns a session-id-ified version of $uri, using the current
+ * application's webroot setting.
+ *
+ * @param string $uri The URI to be modified.
+ * @param boolean $full Generate a full (http://server/path/)
+ * URL.
+ * @param integer $append_session 0 = only if needed, 1 = always, -1 =
+ * never.
+ *
+ * @return string The url with the session id appended.
+ */
+ static public function applicationUrl($uri, $full = false,
+ $append_session = 0)
+ {
+ if ($full) {
+ return self::url($uri, $full, $append_session);
+ }
+
+ if (substr($uri, 0, 1) != '/') {
+ $webroot = $GLOBALS['registry']->get('webroot');
+ if (substr($webroot, -1) != '/') {
+ $webroot .= '/';
+ }
+ $uri = $webroot . $uri;
+ }
+
+ return self::url($uri, $full, $append_session);
+ }
+
+ /**
+ * Returns an external link passed through the dereferrer to strip session
+ * IDs from the referrer.
+ *
+ * @param string $url The external URL to link to.
+ * @param boolean $tag If true, a complete <a> tag is returned, only the
+ * url otherwise.
+ *
+ * @return string The link to the dereferrer script.
+ */
+ static public function externalUrl($url, $tag = false)
+ {
+ if (!isset($_GET[session_name()]) ||
+ Horde_String::substr($url, 0, 1) == '#' ||
+ Horde_String::substr($url, 0, 7) == 'mailto:') {
+ $ext = $url;
+ } else {
+ $ext = self::url($GLOBALS['registry']->get('webroot', 'horde') .
+ '/services/go.php', true, -1);
+
+ /* We must make sure there are no &'s in the URL. */
+ $url = preg_replace(array('/(=?.*?)&(.*?=)/', '/(=?.*?)&(.*?=)/'), '$1&$2', $url);
+ $ext .= '?' . self::signQueryString('url=' . urlencode($url));
+ }
+
+ if ($tag) {
+ $ext = self::link($ext, $url, '', '_blank');
+ }
+
+ return $ext;
+ }
+
+ /**
+ * Returns a URL to be used for downloading, that takes into account any
+ * special browser quirks (i.e. IE's broken filename handling).
+ *
+ * @param string $filename The filename of the download data.
+ * @param array $params Any additional parameters needed.
+ * @param string $url The URL to alter. If none passed in, will use
+ * the file 'view.php' located in the current
+ * module's base directory.
+ *
+ * @return string The download URL.
+ */
+ static public function downloadUrl($filename, $params = array(), $url = null)
+ {
+ global $browser, $registry;
+
+ $horde_url = false;
+
+ if (is_null($url)) {
+ $url = Horde_Util::addParameter(self::url($registry->get('webroot', 'horde') . '/services/download/'), 'module', $registry->getApp());
+ $horde_url = true;
+ }
+
+ /* Add parameters. */
+ if (!is_null($params)) {
+ $url = Horde_Util::addParameter($url, $params);
+ }
+
+ /* If we are using the default Horde download link, add the
+ * filename to the end of the URL. Although not necessary for
+ * many browsers, this should allow every browser to download
+ * correctly. */
+ if ($horde_url) {
+ $url = Horde_Util::addParameter($url, 'fn', '/' . rawurlencode($filename));
+ } elseif ($browser->hasQuirk('break_disposition_filename')) {
+ /* Some browsers will only obtain the filename correctly
+ * if the extension is the last argument in the query
+ * string and rest of the filename appears in the
+ * PATH_INFO element. */
+ $filename = rawurlencode($filename);
+
+ /* Get the webserver ID. */
+ $server = self::webServerID();
+
+ /* Get the name and extension of the file. Apache 2 does
+ * NOT support PATH_INFO information being passed to the
+ * PHP module by default, so disable that
+ * functionality. */
+ if (($server != 'apache2')) {
+ if (($pos = strrpos($filename, '.'))) {
+ $name = '/' . preg_replace('/\./', '%2E', substr($filename, 0, $pos));
+ $ext = substr($filename, $pos);
+ } else {
+ $name = '/' . $filename;
+ $ext = '';
+ }
+
+ /* Enter the PATH_INFO information. */
+ if (($pos = strpos($url, '?'))) {
+ $url = substr($url, 0, $pos) . $name . substr($url, $pos);
+ } else {
+ $url .= $name;
+ }
+ }
+
+ /* Append the extension, if it exists. */
+ if (($server == 'apache2') || !empty($ext)) {
+ $url = Horde_Util::addParameter($url, 'fn_ext', '/' . $filename);
+ }
+ }
+
+ return $url;
+ }
+
+ /**
+ * Returns an anchor tag with the relevant parameters
+ *
+ * @param string $url The full URL to be linked to.
+ * @param string $title The link title/description.
+ * @param string $class The CSS class of the link.
+ * @param string $target The window target to point to.
+ * @param string $onclick JavaScript action for the 'onclick' event.
+ * @param string $title2 The link title (tooltip) (deprecated - just
+ * use $title).
+ * @param string $accesskey The access key to use.
+ * @param array $attributes Any other name/value pairs to add to the <a>
+ * tag.
+ * @param boolean $escape Whether to escape special characters in the
+ * title attribute.
+ *
+ * @return string The full <a href> tag.
+ */
+ static public function link($url = '', $title = '', $class = '',
+ $target = '', $onclick = '', $title2 = '',
+ $accesskey = '', $attributes = array(),
+ $escape = true)
+ {
+ if (!empty($title2)) {
+ $title = $title2;
+ }
+
+ $ret = '<a';
+ if (!empty($url)) {
+ $ret .= " href=\"$url\"";
+ }
+ if (!empty($onclick)) {
+ $ret .= " onclick=\"$onclick\"";
+ }
+ if (!empty($class)) {
+ $ret .= " class=\"$class\"";
+ }
+ if (!empty($target)) {
+ $ret .= " target=\"$target\"";
+ }
+ if (!empty($title)) {
+ if ($escape) {
+ $charset = Horde_Nls::getCharset();
+ $old_error = error_reporting(0);
+ $title = str_replace(
+ array("\r", "\n"), '',
+ htmlspecialchars(
+ nl2br(htmlspecialchars($title, ENT_QUOTES, $charset)),
+ ENT_QUOTES, $charset));
+ error_reporting($old_error);
+ }
+ $ret .= ' title="' . $title . '"';
+ }
+ if (!empty($accesskey)) {
+ $ret .= ' accesskey="' . htmlspecialchars($accesskey) . '"';
+ }
+
+ foreach ($attributes as $name => $value) {
+ $ret .= ' ' . htmlspecialchars($name) . '="'
+ . htmlspecialchars($value) . '"';
+ }
+
+ return "$ret>";
+ }
+
+ /**
+ * Uses DOM Tooltips to display the 'title' attribute for
+ * link() calls.
+ *
+ * @param string $url The full URL to be linked to
+ * @param string $status The JavaScript mouse-over string
+ * @param string $class The CSS class of the link
+ * @param string $target The window target to point to.
+ * @param string $onclick JavaScript action for the 'onclick' event.
+ * @param string $title The link title (tooltip).
+ * @param string $accesskey The access key to use.
+ * @param array $attributes Any other name/value pairs to add to the <a>
+ * tag.
+ *
+ * @return string The full <a href> tag.
+ */
+ static public function linkTooltip($url, $status = '', $class = '',
+ $target = '', $onclick = '',
+ $title = '', $accesskey = '',
+ $attributes = array())
+ {
+ if (!empty($title)) {
+ $charset = Horde_Nls::getCharset();
+ $old_error = error_reporting(0);
+ $title = '<pre>' . preg_replace(array('/\n/', '/((?<!<br)\s{1,}(?<!\/>))/em', '/<br \/><br \/>/', '/<br \/>/'), array('', 'str_repeat(" ", strlen("$1"))', '<br /> <br />', '<br />'), nl2br(htmlspecialchars(htmlspecialchars($title, ENT_QUOTES, $charset), ENT_QUOTES, $charset))) . '</pre>';
+ error_reporting($old_error);
+ }
+
+ return self::link($url, $title, $class, $target, $onclick, null, $accesskey, $attributes, false);
+ }
+
+ /**
+ * Returns an anchor sequence with the relevant parameters for a widget
+ * with accesskey and text.
+ *
+ * @param string $url The full URL to be linked to.
+ * @param string $title The link title/description.
+ * @param string $class The CSS class of the link
+ * @param string $target The window target to point to.
+ * @param string $onclick JavaScript action for the 'onclick' event.
+ * @param string $title2 The link title (tooltip) (deprecated - just use
+ * $title).
+ * @param boolean $nocheck Don't check if the access key already has been
+ * used. Defaults to false (= check).
+ *
+ * @return string The full <a href>Title</a> sequence.
+ */
+ static public function widget($url, $title = '', $class = 'widget',
+ $target = '', $onclick = '', $title2 = '',
+ $nocheck = false)
+ {
+ if (!empty($title2)) {
+ $title = $title2;
+ }
+
+ $ak = self::getAccessKey($title, $nocheck);
+
+ return self::link($url, '', $class, $target, $onclick, '', $ak) . self::highlightAccessKey($title, $ak) . '</a>';
+ }
+
+ /**
+ * Returns a session-id-ified version of $SCRIPT_NAME resp. $PHP_SELF.
+ *
+ * @param boolean $script_params Include script parameters like
+ * QUERY_STRING and PATH_INFO?
+ * @param boolean $nocache Include a nocache parameter in the URL?
+ * @param boolean $full Return a full URL?
+ * @param boolean $force_ssl Ignore $conf['use_ssl'] and force creation
+ * of a SSL URL?
+ *
+ * @return string The requested URI.
+ */
+ static public function selfUrl($script_params = false, $nocache = true,
+ $full = false, $force_ssl = false)
+ {
+ if (!strncmp(PHP_SAPI, 'cgi', 3)) {
+ // When using CGI PHP, SCRIPT_NAME may contain the path to
+ // the PHP binary instead of the script being run; use
+ // PHP_SELF instead.
+ $url = $_SERVER['PHP_SELF'];
+ } else {
+ $url = isset($_SERVER['SCRIPT_NAME']) ?
+ $_SERVER['SCRIPT_NAME'] :
+ $_SERVER['PHP_SELF'];
+ }
+
+ if ($script_params) {
+ if ($pathInfo = Horde_Util::getPathInfo()) {
+ $url .= $pathInfo;
+ }
+ if (!empty($_SERVER['QUERY_STRING'])) {
+ $url .= '?' . $_SERVER['QUERY_STRING'];
+ }
+ }
+
+ $url = self::url($url, $full, 0, $force_ssl);
+
+ return $nocache
+ ? Horde_Util::nocacheUrl($url, !$full)
+ : $url;
+ }
+
+ /**
+ * Constructs a correctly-pathed link to an image.
+ *
+ * @param string $src The image file.
+ * @param string $alt Text describing the image.
+ * @param mixed $attr Any additional attributes for the image tag. Can be
+ * a pre-built string or an array of key/value pairs
+ * that will be assembled and html-encoded.
+ * @param string $dir The root graphics directory.
+ *
+ * @return string The full image tag.
+ */
+ static public function img($src, $alt = '', $attr = '', $dir = null)
+ {
+ $charset = Horde_Nls::getCharset();
+
+ /* If browser does not support images, simply return the ALT text. */
+ if (!$GLOBALS['browser']->hasFeature('images')) {
+ $old_error = error_reporting(0);
+ $res = htmlspecialchars($alt, ENT_COMPAT, $charset);
+ error_reporting($old_error);
+ return $res;
+ }
+
+ /* If no directory has been specified, get it from the registry. */
+ if (is_null($dir)) {
+ $dir = $GLOBALS['registry']->getImageDir();
+ }
+
+ /* If a directory has been provided, prepend it to the image source. */
+ if (!empty($dir)) {
+ $src = $dir . '/' . $src;
+ }
+
+ /* Build all of the tag attributes. */
+ $attributes = array('alt' => $alt);
+ if (is_array($attr)) {
+ $attributes = array_merge($attributes, $attr);
+ }
+ if (empty($attributes['title'])) {
+ $attributes['title'] = '';
+ }
+
+ $img = '<img';
+ $old_error = error_reporting(0);
+ foreach ($attributes as $attribute => $value) {
+ $img .= ' ' . $attribute . '="' . htmlspecialchars($value, ENT_COMPAT, $charset) . '"';
+ }
+ error_reporting($old_error);
+
+ /* If the user supplied a pre-built string of attributes, add that. */
+ if (is_string($attr) && !empty($attr)) {
+ $img .= ' ' . $attr;
+ }
+
+ /* Return the closed image tag. */
+ return $img . ' src="' . self::base64ImgData($src) . '" />';
+ }
+
+ /**
+ * Same as Horde::img(), but returns a full source url for the image.
+ * Useful for when the image may be part of embedded Horde content on an
+ * external site. Basically a stop-gap measure until Horde_View etc...
+ *
+ * @see Horde::img()
+ */
+ static public function fullSrcImg($src, $options = array())
+ {
+ $charset = Horde_Nls::getCharset();
+
+ /* If browser does not support images, simply return the ALT text. */
+ if (!$GLOBALS['browser']->hasFeature('images')) {
+ $old_error = error_reporting(0);
+ $res = htmlspecialchars($alt, ENT_COMPAT, $charset);
+ error_reporting($old_error);
+ return $res;
+ }
+
+ /* If no directory has been specified, get it from the registry. */
+ $dir = empty($options['dir']) ? $GLOBALS['registry']->getImageDir() : $options['dir'];
+
+ /* If we can send as data, no need to get the full path */
+ $src = self::base64ImgData($dir . '/' . $src);
+ if (substr($src, 0, 10) != 'data:image') {
+ $src = self::url($src, true, -1);
+ }
+
+ /* Build all of the tag attributes. */
+ $attributes = !empty($options['attr']) ? $options['attr'] : array();
+
+ $img = '<img';
+ $old_error = error_reporting(0);
+ foreach ($attributes as $attribute => $value) {
+ $img .= ' ' . $attribute . '="' . htmlspecialchars($value, ENT_COMPAT, $charset) . '"';
+ }
+ error_reporting($old_error);
+
+ /* If the user supplied a pre-built string of attributes, add that. */
+ if (!empty($options['attr']) && is_string($options['attr'])) {
+ $img .= ' ' . $options['attr'];
+ }
+
+ /* Return the closed image tag. */
+ return $img . ' src="' . $src . '" />';
+ }
+
+ /**
+ * Generate RFC 2397-compliant image data strings.
+ *
+ * @param string $file Filename containing image data.
+ *
+ * @return string The string to use in the image 'src' attribute; either
+ * the image data if the browser supports, or the filepath
+ * if not.
+ */
+ public function base64ImgData($file)
+ {
+ $dataurl = $GLOBALS['browser']->hasFeature('dataurl');
+ if (!$dataurl) {
+ return $file;
+ }
+
+ /* Only encode image files if they are below the dataurl limit. */
+ $filename = realpath($GLOBALS['registry']->get('fileroot', 'horde')) . preg_replace('/^' . preg_quote($GLOBALS['registry']->get('webroot', 'horde'), '/') . '/', '', $file);
+
+ /* Delete approx. 50 chars from the limit to account for the various
+ * data/base64 header text. Multiply by 0.75 to determine the
+ * base64 encoded size. */
+ return (($dataurl === true) ||
+ (filesize($filename) <= (($dataurl * 0.75) - 50)))
+ ? 'data:image/' . substr($file, strrpos($file, '.') + 1) . ';base64,' . base64_encode(file_get_contents($filename))
+ : $file;
+ }
+
+ /**
+ * Determines the location of the system temporary directory. If a specific
+ * setting cannot be found, it defaults to /tmp.
+ *
+ * @return string A directory name that can be used for temp files.
+ * Returns false if one could not be found.
+ */
+ static public function getTempDir()
+ {
+ global $conf;
+
+ /* If one has been specifically set, then use that */
+ if (!empty($conf['tmpdir'])) {
+ $tmp = $conf['tmpdir'];
+ }
+
+ /* Next, try Horde_Util::getTempDir(). */
+ if (empty($tmp)) {
+ $tmp = Horde_Util::getTempDir();
+ }
+
+ /* If it is still empty, we have failed, so return false;
+ * otherwise return the directory determined. */
+ return empty($tmp)
+ ? false
+ : $tmp;
+ }
+
+ /**
+ * Creates a temporary filename for the lifetime of the script, and
+ * (optionally) registers it to be deleted at request shutdown.
+ *
+ * @param string $prefix Prefix to make the temporary name more
+ * recognizable.
+ * @param boolean $delete Delete the file at the end of the request?
+ * @param string $dir Directory to create the temporary file in.
+ * @param boolean $secure If deleting file, should we securely delete the
+ * file?
+ *
+ * @return string Returns the full path-name to the temporary file or
+ * false if a temporary file could not be created.
+ */
+ static public function getTempFile($prefix = 'Horde', $delete = true,
+ $dir = '', $secure = false)
+ {
+ if (empty($dir) || !is_dir($dir)) {
+ $dir = self::getTempDir();
+ }
+
+ return Horde_Util::getTempFile($prefix, $delete, $dir, $secure);
+ }
+
+ /**
+ * Starts output compression, if requested.
+ */
+ static public function compressOutput()
+ {
+ if (self::$_compressStart) {
+ return;
+ }
+
+ /* Compress output if requested and possible. */
+ if ($GLOBALS['conf']['compress_pages'] &&
+ !$GLOBALS['browser']->hasQuirk('buggy_compression') &&
+ !(bool)ini_get('zlib.output_compression') &&
+ !(bool)ini_get('zend_accelerator.compress_all') &&
+ ini_get('output_handler') != 'ob_gzhandler') {
+ if (ob_get_level()) {
+ ob_end_clean();
+ }
+ ob_start('ob_gzhandler');
+ }
+
+ self::$_compressStart = true;
+ }
+
+ /**
+ * Determines if output compression can be used.
+ *
+ * @return boolean True if output compression can be used, false if not.
+ */
+ static public function allowOutputCompression()
+ {
+ $browser = &Horde_Browser::singleton();
+ return !$browser->hasQuirk('buggy_compression') &&
+ (ini_get('zlib.output_compression') == '') &&
+ (ini_get('zend_accelerator.compress_all') == '') &&
+ (ini_get('output_handler') != 'ob_gzhandler');
+ }
+
+ /**
+ * Returns the Web server being used.
+ * PHP string list built from the PHP 'configure' script.
+ *
+ * @return string A web server identification string.
+ * @see php_sapi_name()
+ */
+ static public function webServerID()
+ {
+ switch (PHP_SAPI) {
+ case 'apache':
+ return 'apache1';
+
+ case 'apache2filter':
+ case 'apache2handler':
+ return 'apache2';
+
+ default:
+ return PHP_SAPI;
+ }
+ }
+
+ /**
+ * Returns the <link> tags for the CSS stylesheets.
+ *
+ * @param string|array $app The Horde application(s).
+ * @param mixed $theme The theme to use; specify an empty value to
+ * retrieve the theme from user preferences, and
+ * false for no theme.
+ * @param boolean $inherit Inherit Horde-wide CSS?
+ *
+ * @return string <link> tags for CSS stylesheets.
+ */
+ static public function stylesheetLink($apps = null, $theme = '',
+ $inherit = true)
+ {
+ $css = self::getStylesheets($apps, $theme, $inherit);
+
+ $html = '';
+ foreach ($css as $css_link) {
+ $html .= '<link href="' . $css_link['u'] . '" rel="stylesheet" type="text/css" />' . "\n";
+ }
+
+ return $html;
+ }
+
+ /**
+ * Return the list of base stylesheets to display.
+ *
+ * @param string|array $app The Horde application(s).
+ * @param mixed $theme The theme to use; specify an empty value to
+ * retrieve the theme from user preferences, and
+ * false for no theme.
+ * @param boolean $inherit Inherit Horde-wide CSS?
+ *
+ * @return array
+ */
+ static public function getStylesheets($apps = null, $theme = '',
+ $inherit = true)
+ {
+ if ($theme === '' && isset($GLOBALS['prefs'])) {
+ $theme = $GLOBALS['prefs']->getValue('theme');
+ }
+
+ $css = array();
+ $rtl = isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]);
+
+ if (!is_array($apps)) {
+ $apps = is_null($apps) ? array() : array($apps);
+ }
+ if ($inherit) {
+ $key = array_search('horde', $apps);
+ if ($key !== false) {
+ unset($apps[$key]);
+ }
+ array_unshift($apps, 'horde');
+ }
+
+ /* Collect browser specific stylesheets if needed. */
+ $browser_css = array();
+
+ switch ($GLOBALS['browser']->getBrowser()) {
+ case 'msie':
+ $ie_major = $GLOBALS['browser']->getMajor();
+ if ($ie_major == 7) {
+ $browser_css[] = 'ie7.css';
+ } elseif ($ie_major < 7) {
+ $browser_css[] = 'ie6_or_less.css';
+ if ($GLOBALS['browser']->getPlatform() == 'mac') {
+ $browser_css[] = 'ie5mac.css';
+ }
+ }
+ break;
+
+ case 'opera':
+ $browser_css[] = 'opera.css';
+ break;
+
+ case 'mozilla':
+ if ($GLOBALS['browser']->getMajor() >= 5 &&
+ preg_match('/rv:(.*)\)/', $GLOBALS['browser']->getAgentString(), $revision) &&
+ $revision[1] <= 1.4) {
+ $browser_css[] = 'moz14.css';
+ }
+ break;
+
+ case 'webkit':
+ $browser_css[] = 'webkit.css';
+ break;
+ }
+
+ foreach ($apps as $app) {
+ $themes_fs = $GLOBALS['registry']->get('themesfs', $app);
+ $themes_uri = self::url($GLOBALS['registry']->get('themesuri', $app), false, -1);
+ $css[] = array('u' => $themes_uri . '/screen.css', 'f' => $themes_fs . '/screen.css');
+ if (!empty($theme) &&
+ file_exists($themes_fs . '/' . $theme . '/screen.css')) {
+ $css[] = array('u' => $themes_uri . '/' . $theme . '/screen.css', 'f' => $themes_fs . '/' . $theme . '/screen.css');
+ }
+
+ if ($rtl) {
+ $css[] = array('u' => $themes_uri . '/rtl.css', 'f' => $themes_fs . '/rtl.css');
+ if (!empty($theme) &&
+ file_exists($themes_fs . '/' . $theme . '/rtl.css')) {
+ $css[] = array('u' => $themes_uri . '/' . $theme . '/rtl.css', 'f' => $themes_fs . '/' . $theme . '/rtl.css');
+ }
+ }
+ foreach ($browser_css as $browser) {
+ if (file_exists($themes_fs . '/' . $browser)) {
+ $css[] = array('u' => $themes_uri . '/' . $browser, 'f' => $themes_fs . '/' . $browser);
+ }
+ if (!empty($theme) &&
+ file_exists($themes_fs . '/' . $theme . '/' . $browser)) {
+ $css[] = array('u' => $themes_uri . '/' . $theme . '/' . $browser, 'f' => $themes_fs . '/' . $theme . '/' . $browser);
+ }
+ }
+ }
+
+ return $css;
+ }
+
+ /**
+ * Sets a custom session handler up, if there is one.
+ * If the global variable 'session_cache_limiter' is defined, its value
+ * will override the cache limiter setting found in the configuration
+ * file.
+ *
+ * The custom session handler object will be contained in the global
+ * 'horde_sessionhandler' variable.
+ */
+ static public function setupSessionHandler()
+ {
+ global $conf;
+
+ ini_set('url_rewriter.tags', 0);
+ if (!empty($conf['session']['use_only_cookies'])) {
+ ini_set('session.use_only_cookies', 1);
+ if (!empty($conf['cookie']['domain']) &&
+ strpos($conf['server']['name'], '.') === false) {
+ self::fatal('Session cookies will not work without a FQDN and with a non-empty cookie domain. Either use a fully qualified domain name like "http://www.example.com" instead of "http://example" only, or set the cookie domain in the Horde configuration to an empty value, or enable non-cookie (url-based) sessions in the Horde configuration.', __FILE__, __LINE__);
+ }
+ }
+
+ session_set_cookie_params($conf['session']['timeout'],
+ $conf['cookie']['path'], $conf['cookie']['domain'], $conf['use_ssl'] == 1 ? 1 : 0);
+ session_cache_limiter(Horde_Util::nonInputVar('session_cache_limiter', $conf['session']['cache_limiter']));
+ session_name(urlencode($conf['session']['name']));
+
+ $type = !empty($conf['sessionhandler']['type']) ? $conf['sessionhandler']['type'] : 'none';
+ if ($type == 'external') {
+ $calls = $conf['sessionhandler']['params'];
+ session_set_save_handler($calls['open'],
+ $calls['close'],
+ $calls['read'],
+ $calls['write'],
+ $calls['destroy'],
+ $calls['gc']);
+ } elseif ($type != 'none') {
+ try {
+ $sh = &Horde_SessionHandler::singleton($conf['sessionhandler']['type'], array_merge(self::getDriverConfig('sessionhandler', $conf['sessionhandler']['type']), array('memcache' => !empty($conf['sessionhandler']['memcache']))));
+ ini_set('session.save_handler', 'user');
+ session_set_save_handler(array(&$sh, 'open'),
+ array(&$sh, 'close'),
+ array(&$sh, 'read'),
+ array(&$sh, 'write'),
+ array(&$sh, 'destroy'),
+ array(&$sh, 'gc'));
+ $GLOBALS['horde_sessionhandler'] = $sh;
+ } catch (Horde_Exception $e) {
+ self::fatal(new Horde_Exception('Horde is unable to correctly start the custom session handler.'), __FILE__, __LINE__, false);
+ }
+ }
+ }
+
+ /**
+ * Returns an un-used access key from the label given.
+ *
+ * @param string $label The label to choose an access key from.
+ * @param boolean $nocheck Don't check if the access key already has been
+ * used?
+ *
+ * @return string A single lower case character access key or empty
+ * string if none can be found
+ */
+ static public function getAccessKey($label, $nocheck = false,
+ $shutdown = false)
+ {
+ /* Shutdown call for translators? */
+ if ($shutdown) {
+ if (!count(self::$_labels)) {
+ return;
+ }
+ $script = basename($_SERVER['PHP_SELF']);
+ $labels = array_keys(self::$_labels);
+ sort($labels);
+ $used = array_keys(self::$_used);
+ sort($used);
+ $remaining = str_replace($used, array(), 'abcdefghijklmnopqrstuvwxyz');
+ self::logMessage('Access key information for ' . $script, __FILE__, __LINE__);
+ self::logMessage('Used labels: ' . implode(',', $labels), __FILE__, __LINE__);
+ self::logMessage('Used keys: ' . implode('', $used), __FILE__, __LINE__);
+ self::logMessage('Free keys: ' . $remaining, __FILE__, __LINE__);
+ return;
+ }
+
+ /* Use access keys at all? */
+ if (!isset(self::$_noAccessKey)) {
+ self::$_noAccessKey = !$GLOBALS['browser']->hasFeature('accesskey') || !$GLOBALS['prefs']->getValue('widget_accesskey');
+ }
+
+ if (self::$_noAccessKey || !preg_match('/_([A-Za-z])/', $label, $match)) {
+ return '';
+ }
+ $key = $match[1];
+
+ /* Has this key already been used? */
+ if (isset(self::$_used[strtolower($key)]) &&
+ !($nocheck && isset(self::$_labels[$label]))) {
+ return '';
+ }
+
+ /* Save key and label. */
+ self::$_used[strtolower($key)] = true;
+ self::$_labels[$label] = true;
+
+ return $key;
+ }
+
+ /**
+ * Strips an access key from a label.
+ * For multibyte charset strings the access key gets removed completely,
+ * otherwise only the underscore gets removed.
+ *
+ * @param string $label The label containing an access key.
+ *
+ * @return string The label with the access key being stripped.
+ */
+ static public function stripAccessKey($label)
+ {
+ if (!isset($GLOBALS['nls'])) {
+ self::loadConfiguration('nls.php', null, 'horde');
+ }
+ $multibyte = isset($GLOBALS['nls']['multibyte'][Horde_Nls::getCharset(true)]);
+
+ return preg_replace('/_([A-Za-z])/',
+ $multibyte && preg_match('/[\x80-\xff]/', $label) ? '' : '\1',
+ $label);
+ }
+
+ /**
+ * Highlights an access key in a label.
+ *
+ * @param string $label The label to highlight the access key in.
+ * @param string $accessKey The access key to highlight.
+ *
+ * @return string The HTML version of the label with the access key
+ * highlighted.
+ */
+ static public function highlightAccessKey($label, $accessKey)
+ {
+ $stripped_label = self::stripAccessKey($label);
+
+ if (empty($accessKey)) {
+ return $stripped_label;
+ }
+
+ if (isset($GLOBALS['nls']['multibyte'][Horde_Nls::getCharset(true)])) {
+ /* Prefix parenthesis with the UTF-8 representation of the LRO
+ * (Left-to-Right-Override) Unicode codepoint U+202D. */
+ $prefix = Horde_Nls::getCharset() == 'UTF-8' ? "\xe2\x80\xad" : '';
+ return $stripped_label . $prefix . '(<span class="accessKey">'
+ . strtoupper($accessKey) . '</span>' . ')';
+ } else {
+ return str_replace('_' . $accessKey, '<span class="accessKey">' . $accessKey . '</span>', $label);
+ }
+ }
+
+ /**
+ * Returns the appropriate "accesskey" and "title" attributes for an HTML
+ * tag and the given label.
+ *
+ * @param string $label The title of an HTML element
+ * @param boolean $nocheck Don't check if the access key already has been
+ * used?
+ *
+ * @return string The title, and if appropriate, the accesskey attributes
+ * for the element.
+ */
+ static public function getAccessKeyAndTitle($label, $nocheck = false)
+ {
+ $ak = self::getAccessKey($label, $nocheck);
+ $attributes = 'title="' . self::stripAccessKey($label);
+ if (!empty($ak)) {
+ $attributes .= sprintf(_(" (Accesskey %s)"), $ak);
+ $attributes .= '" accesskey="' . $ak;
+ }
+
+ return $attributes . '"';
+ }
+
+ /**
+ * Returns a label element including an access key for usage in conjuction
+ * with a form field. User preferences regarding access keys are respected.
+ *
+ * @param string $for The form field's id attribute.
+ * @param string $label The label text.
+ * @param string $ak The access key to use. If null a new access key
+ * will be generated.
+ *
+ * @return string The html code for the label element.
+ */
+ static public function label($for, $label, $ak = null)
+ {
+ if (is_null($ak)) {
+ $ak = self::getAccessKey($label, 1);
+ }
+ $label = self::highlightAccessKey($label, $ak);
+
+ return sprintf('<label for="%s"%s>%s</label>',
+ $for,
+ !empty($ak) ? ' accesskey="' . $ak . '"' : '',
+ $label);
+ }
+
+ /**
+ * Redirects to the main Horde login page on authentication failure.
+ */
+ static public function authenticationFailureRedirect()
+ {
+ if (Horde_Cli::runningFromCLI()) {
+ $cli = &Horde_Cli::singleton();
+ $cli->fatal(_("You are not authenticated."));
+ }
+
+ $url = $GLOBALS['registry']->get('webroot', 'horde') . '/login.php';
+ $url = Horde_Util::addParameter($url, array('url' => self::selfUrl(true), 'nosidebar' => 1), null, false);
+ $url = Horde_Auth::addLogoutParameters($url);
+ header('Location: ' . self::url($url, true));
+ exit;
+ }
+
+ /**
+ * Provides a standardised function to call a Horde hook, checking whether
+ * a hook config file exists and whether the function itself exists. If
+ * these two conditions are not satisfied it will return the specified
+ * value (by default a PEAR error).
+ *
+ * @param string $hook The function to call.
+ * @param array $args An array of any arguments to pass to the hook
+ * function.
+ * @param string $app If specified look for hooks in the config directory
+ * of this app.
+ * @param mixed $error What to return if $app/config/hooks.php or $hook
+ * does not exist. If this is the string 'PEAR_Error'
+ * a PEAR error object is returned instead, detailing
+ * the failure.
+ *
+ * @return mixed Either the results of the hook or PEAR error on failure.
+ * @todo Throw Horde_Exception
+ */
+ static public function callHook($hook, $args = array(), $app = 'horde',
+ $error = 'PEAR_Error')
+ {
+ if (!isset(self::$_hooksLoaded[$app])) {
+ try {
+ self::loadConfiguration('hooks.php', null, $app);
+ self::$_hooksLoaded[$app] = true;
+ } catch (Horde_Exception $e) {
+ self::logMessage($e, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ self::$_hooksLoaded[$app] = false;
+ }
+ }
+
+ if (function_exists($hook)) {
+ $result = call_user_func_array($hook, $args);
+ if ($result instanceof PEAR_Error) {
+ self::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
+ }
+ return $result;
+ }
+
+ if (is_string($error) && strcmp($error, 'PEAR_Error') == 0) {
+ $error = PEAR::raiseError(sprintf('Hook %s in application %s not called.', $hook, $app));
+ self::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ }
+
+ return $error;
+ }
+
+ /**
+ * Returns the specified permission for the current user.
+ *
+ * @param string $permission A permission, currently only 'max_blocks'.
+ *
+ * @return mixed The value of the specified permission.
+ */
+ static public function hasPermission($permission)
+ {
+ global $perms;
+
+ if (!$perms->exists('horde:' . $permission)) {
+ return true;
+ }
+
+ $allowed = $perms->getPermissions('horde:' . $permission);
+ if (is_array($allowed)) {
+ switch ($permission) {
+ case 'max_blocks':
+ $allowed = max($allowed);
+ break;
+ }
+ }
+
+ return $allowed;
+ }
+
+ /**
+ * Utility function to send redirect headers to browser, handling any
+ * browser quirks.
+ *
+ * @param string $url The URL to redirect to.
+ */
+ static public function redirect($url)
+ {
+ if ($GLOBALS['browser']->isBrowser('msie') &&
+ ($GLOBALS['conf']['use_ssl'] == 3) &&
+ (strlen($url) < 160)) {
+ header('Refresh: 0; URL=' . $url);
+ } else {
+ header('Location: ' . $url);
+ }
+ exit;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Config:: package provides a framework for managing the
+ * configuration of Horde applications, writing conf.php files from
+ * conf.xml source files, generating user interfaces, etc.
+ *
+ * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Core
+ */
+class Horde_Config
+{
+ /**
+ * The name of the configured application.
+ *
+ * @var string
+ */
+ protected $_app;
+
+ /**
+ * The XML tree of the configuration file traversed to an
+ * associative array.
+ *
+ * @var array
+ */
+ protected $_xmlConfigTree = null;
+
+ /**
+ * The content of the generated configuration file.
+ *
+ * @var string
+ */
+ protected $_phpConfig;
+
+ /**
+ * The content of the old configuration file.
+ *
+ * @var string
+ */
+ protected $_oldConfig;
+
+ /**
+ * The manual configuration in front of the generated configuration.
+ *
+ * @var string
+ */
+ protected $_preConfig;
+
+ /**
+ * The manual configuration after the generated configuration.
+ *
+ * @var string
+ */
+ protected $_postConfig;
+
+ /**
+ * The current $conf array of the configured application.
+ *
+ * @var array
+ */
+ protected $_currentConfig = array();
+
+ /**
+ * The version tag of the conf.xml file which will be copied into the
+ * conf.php file.
+ *
+ * @var string
+ */
+ protected $_versionTag = '';
+
+ /**
+ * The line marking the begin of the generated configuration.
+ *
+ * @var string
+ */
+ protected $_configBegin = "/* CONFIG START. DO NOT CHANGE ANYTHING IN OR AFTER THIS LINE. */\n";
+
+ /**
+ * The line marking the end of the generated configuration.
+ *
+ * @var string
+ */
+ protected $_configEnd = "/* CONFIG END. DO NOT CHANGE ANYTHING IN OR BEFORE THIS LINE. */\n";
+
+ /**
+ * Constructor.
+ *
+ * @param string $app The name of the application to be configured.
+ */
+ public function __construct($app)
+ {
+ $this->_app = $app;
+ }
+
+ /**
+ * Reads the application's conf.xml file and builds an associative array
+ * from its XML tree.
+ *
+ * @param array $custom_conf Any settings that shall be included in the
+ * generated configuration.
+ *
+ * @return array An associative array representing the configuration
+ * tree.
+ */
+ public function readXMLConfig($custom_conf = null)
+ {
+ if (!is_null($this->_xmlConfigTree) && !$custom_conf) {
+ return $this->_xmlConfigTree;
+ }
+
+ $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
+
+ if ($custom_conf) {
+ $this->_currentConfig = $custom_conf;
+ } else {
+ /* Fetch the current conf.php contents. */
+ @eval($this->getPHPConfig());
+ if (isset($conf)) {
+ $this->_currentConfig = $conf;
+ }
+ }
+
+ /* Load the DOM object. */
+ include_once 'Horde/DOM.php';
+ $doc = Horde_DOM_Document::factory(array('filename' => $path . '/conf.xml'));
+
+ /* Check if there is a CVS version tag and store it. */
+ $node = $doc->first_child();
+ while (!empty($node)) {
+ if ($node->type == XML_COMMENT_NODE) {
+ // @TODO: Old CVS tag
+ if (preg_match('/\$.*?conf\.xml,v .*? .*\$/', $node->node_value(), $match) ||
+ // New Git tag
+ preg_match('/\$Id:\s*[0-9a-f]+\s*\$/', $node->node_value(), $match)) {
+ $this->_versionTag = $match[0] . "\n";
+ break;
+ }
+ }
+ $node = $node->next_sibling();
+ }
+
+ /* Parse the config file. */
+ $this->_xmlConfigTree = array();
+ $root = $doc->root();
+ if ($root->has_child_nodes()) {
+ $this->_parseLevel($this->_xmlConfigTree, $root->child_nodes(), '');
+ }
+
+ return $this->_xmlConfigTree;
+ }
+
+ /**
+ * Returns the file content of the current configuration file.
+ *
+ * @return string The unparsed configuration file content.
+ */
+ public function getPHPConfig()
+ {
+ if (!is_null($this->_oldConfig)) {
+ return $this->_oldConfig;
+ }
+
+ $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
+ if (file_exists($path . '/conf.php')) {
+ $this->_oldConfig = file_get_contents($path . '/conf.php');
+ if (!empty($this->_oldConfig)) {
+ $this->_oldConfig = preg_replace('/<\?php\n?/', '', $this->_oldConfig);
+ $pos = strpos($this->_oldConfig, $this->_configBegin);
+ if ($pos !== false) {
+ $this->_preConfig = substr($this->_oldConfig, 0, $pos);
+ $this->_oldConfig = substr($this->_oldConfig, $pos);
+ }
+ $pos = strpos($this->_oldConfig, $this->_configEnd);
+ if ($pos !== false) {
+ $this->_postConfig = substr($this->_oldConfig, $pos + strlen($this->_configEnd));
+ $this->_oldConfig = substr($this->_oldConfig, 0, $pos);
+ }
+ }
+ } else {
+ $this->_oldConfig = '';
+ }
+
+ return $this->_oldConfig;
+ }
+
+ /**
+ * Generates the content of the application's configuration file.
+ *
+ * @param Horde_Variables $formvars The processed configuration form
+ * data.
+ * @param array $custom_conf Any settings that shall be included
+ * in the generated configuration.
+ *
+ * @return string The content of the generated configuration file.
+ */
+ public function generatePHPConfig($formvars, $custom_conf = null)
+ {
+ $this->readXMLConfig($custom_conf);
+ $this->getPHPConfig();
+
+ $this->_phpConfig = "<?php\n" . $this->_preConfig . $this->_configBegin;
+ if (!empty($this->_versionTag)) {
+ $this->_phpConfig .= '// ' . $this->_versionTag;
+ }
+ $this->_generatePHPConfig($this->_xmlConfigTree, '', $formvars);
+ $this->_phpConfig .= $this->_configEnd . $this->_postConfig;
+
+ return $this->_phpConfig;
+ }
+
+ /**
+ * Generates the configuration file items for a part of the configuration
+ * tree.
+ *
+ * @param array $section An associative array containing the
+ * part of the traversed XML
+ * configuration tree that should be
+ * processed.
+ * @param string $prefix A configuration prefix determining
+ * the current position inside the
+ * configuration file. This prefix will
+ * be translated to keys of the $conf
+ * array in the generated configuration
+ * file.
+ * @param Horde_Variables $formvars The processed configuration form
+ * data.
+ */
+ protected function _generatePHPConfig($section, $prefix, $formvars)
+ {
+ if (!is_array($section)) {
+ return;
+ }
+
+ foreach ($section as $name => $configitem) {
+ $prefixedname = empty($prefix)
+ ? $name
+ : $prefix . '|' . $name;
+ $configname = str_replace('|', '__', $prefixedname);
+ $quote = (!isset($configitem['quote']) || $configitem['quote'] !== false);
+
+ if ($configitem == 'placeholder') {
+ $this->_phpConfig .= '$conf[\'' . str_replace('|', '\'][\'', $prefix) . "'] = array();\n";
+ } elseif (isset($configitem['switch'])) {
+ $val = $formvars->getExists($configname, $wasset);
+ if (!$wasset) {
+ $val = isset($configitem['default']) ? $configitem['default'] : null;
+ }
+ if (isset($configitem['switch'][$val])) {
+ $value = $val;
+ if ($quote && $value != 'true' && $value != 'false') {
+ $value = "'" . $value . "'";
+ }
+ $this->_generatePHPConfig($configitem['switch'][$val]['fields'], $prefix, $formvars);
+ }
+ } elseif (isset($configitem['_type'])) {
+ $val = $formvars->getExists($configname, $wasset);
+ if (!$wasset) {
+ $val = isset($configitem['default']) ? $configitem['default'] : null;
+ }
+
+ $type = $configitem['_type'];
+ switch ($type) {
+ case 'multienum':
+ if (is_array($val)) {
+ $encvals = array();
+ foreach ($val as $v) {
+ $encvals[] = $this->_quote($v);
+ }
+ $arrayval = "'" . implode('\', \'', $encvals) . "'";
+ if ($arrayval == "''") {
+ $arrayval = '';
+ }
+ } else {
+ $arrayval = '';
+ }
+ $value = 'array(' . $arrayval . ')';
+ break;
+
+ case 'boolean':
+ if (is_bool($val)) {
+ $value = $val ? 'true' : 'false';
+ } else {
+ $value = ($val == 'on') ? 'true' : 'false';
+ }
+ break;
+
+ case 'stringlist':
+ $values = explode(',', $val);
+ if (!is_array($values)) {
+ $value = "array('" . $this->_quote(trim($values)) . "')";
+ } else {
+ $encvals = array();
+ foreach ($values as $v) {
+ $encvals[] = $this->_quote(trim($v));
+ }
+ $arrayval = "'" . implode('\', \'', $encvals) . "'";
+ if ($arrayval == "''") {
+ $arrayval = '';
+ }
+ $value = 'array(' . $arrayval . ')';
+ }
+ break;
+
+ case 'int':
+ if ($val !== '') {
+ $value = (int)$val;
+ }
+ break;
+
+ case 'octal':
+ $value = sprintf('0%o', octdec($val));
+ break;
+
+ case 'header':
+ case 'description':
+ break;
+
+ default:
+ if ($val != '') {
+ $value = $val;
+ if ($quote && $value != 'true' && $value != 'false') {
+ $value = "'" . $this->_quote($value) . "'";
+ }
+ }
+ break;
+ }
+ } else {
+ $this->_generatePHPConfig($configitem, $prefixedname, $formvars);
+ }
+
+ if (isset($value)) {
+ $this->_phpConfig .= '$conf[\'' . str_replace('__', '\'][\'', $configname) . '\'] = ' . $value . ";\n";
+ }
+ unset($value);
+ }
+ }
+
+ /**
+ * Parses one level of the configuration XML tree into the associative
+ * array containing the traversed configuration tree.
+ *
+ * @param array &$conf The already existing array where the processed
+ * XML tree portion should be appended to.
+ * @param array $children An array containing the XML nodes of the level
+ * that should be parsed.
+ * @param string $ctx A string representing the current position
+ * (context prefix) inside the configuration XML
+ * file.
+ */
+ protected function _parseLevel(&$conf, $children, $ctx)
+ {
+ reset($children);
+ while (list(,$node) = each($children)) {
+ if ($node->type != XML_ELEMENT_NODE) {
+ continue;
+ }
+ $name = $node->get_attribute('name');
+ $desc = Horde_Text_Filter::filter($node->get_attribute('desc'), 'linkurls', array('callback' => 'Horde::externalUrl'));
+ $required = !($node->get_attribute('required') == 'false');
+ $quote = !($node->get_attribute('quote') == 'false');
+
+ $curctx = empty($ctx)
+ ? $name
+ : $ctx . '|' . $name;
+
+ switch ($node->tagname) {
+ case 'configdescription':
+ if (empty($name)) {
+ $name = hash('md5', uniqid(mt_rand(), true));
+ }
+
+ $conf[$name] = array(
+ '_type' => 'description',
+ 'desc' => Horde_Text_Filter::filter($this->_default($curctx, $this->_getNodeOnlyText($node)), 'linkurls', array('callback' => 'Horde::externalUrl'))
+ );
+ break;
+
+ case 'configheader':
+ if (empty($name)) {
+ $name = hash('md5', uniqid(mt_rand(), true));
+ }
+
+ $conf[$name] = array(
+ '_type' => 'header',
+ 'desc' => $this->_default($curctx, $this->_getNodeOnlyText($node))
+ );
+ break;
+
+ case 'configswitch':
+ $values = $this->_getSwitchValues($node, $ctx);
+ list($default, $isDefault) = $quote
+ ? $this->__default($curctx, $this->_getNodeOnlyText($node))
+ : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
+
+ if ($default === '') {
+ $default = key($values);
+ }
+
+ if (is_bool($default)) {
+ $default = $default ? 'true' : 'false';
+ }
+
+ $conf[$name] = array(
+ 'desc' => $desc,
+ 'switch' => $values,
+ 'default' => $default,
+ 'is_default' => $isDefault
+ );
+ break;
+
+ case 'configenum':
+ $values = $this->_getEnumValues($node);
+ list($default, $isDefault) = $quote
+ ? $this->__default($curctx, $this->_getNodeOnlyText($node))
+ : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
+
+ if ($default === '') {
+ $default = key($values);
+ }
+
+ if (is_bool($default)) {
+ $default = $default ? 'true' : 'false';
+ }
+
+ $conf[$name] = array(
+ '_type' => 'enum',
+ 'required' => $required,
+ 'quote' => $quote,
+ 'values' => $values,
+ 'desc' => $desc,
+ 'default' => $default,
+ 'is_default' => $isDefault
+ );
+ break;
+
+ case 'configlist':
+ list($default, $isDefault) = $this->__default($curctx, null);
+
+ if (is_null($default)) {
+ $default = $this->_getNodeOnlyText($node);
+ } elseif (is_array($default)) {
+ $default = implode(', ', $default);
+ }
+
+ $conf[$name] = array(
+ '_type' => 'stringlist',
+ 'required' => $required,
+ 'desc' => $desc,
+ 'default' => $default,
+ 'is_default' => $isDefault
+ );
+ break;
+
+ case 'configmultienum':
+ $values = $this->_getEnumValues($node);
+ list($default, $isDefault) = $this->__default($curctx, explode(',', $this->_getNodeOnlyText($node)));
+
+ $conf[$name] = array(
+ '_type' => 'multienum',
+ 'required' => $required,
+ 'values' => $values,
+ 'desc' => $desc,
+ 'default' => Horde_Array::valuesToKeys($default),
+ 'is_default' => $isDefault
+ );
+ break;
+
+ case 'configpassword':
+ $conf[$name] = array(
+ '_type' => 'password',
+ 'required' => $required,
+ 'desc' => $desc,
+ 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
+ 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
+ );
+ break;
+
+ case 'configstring':
+ $conf[$name] = array(
+ '_type' => 'text',
+ 'required' => $required,
+ 'desc' => $desc,
+ 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
+ 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
+ );
+
+ if ($conf[$name]['default'] === false) {
+ $conf[$name]['default'] = 'false';
+ } elseif ($conf[$name]['default'] === true) {
+ $conf[$name]['default'] = 'true';
+ }
+ break;
+
+ case 'configboolean':
+ $default = $this->_getNodeOnlyText($node);
+ $default = !(empty($default) || $default === 'false');
+
+ $conf[$name] = array(
+ '_type' => 'boolean',
+ 'required' => $required,
+ 'desc' => $desc,
+ 'default' => $this->_default($curctx, $default),
+ 'is_default' => $this->_isDefault($curctx, $default)
+ );
+ break;
+
+ case 'configinteger':
+ $values = $this->_getEnumValues($node);
+
+ $conf[$name] = array(
+ '_type' => 'int',
+ 'required' => $required,
+ 'values' => $values,
+ 'desc' => $desc,
+ 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
+ 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
+ );
+
+ if ($node->get_attribute('octal') == 'true' &&
+ $conf[$name]['default'] != '') {
+ $conf[$name]['_type'] = 'octal';
+ $conf[$name]['default'] = sprintf('0%o', $this->_default($curctx, octdec($this->_getNodeOnlyText($node))));
+ }
+ break;
+
+ case 'configphp':
+ $conf[$name] = array(
+ '_type' => 'php',
+ 'required' => $required,
+ 'quote' => false,
+ 'desc' => $desc,
+ 'default' => $this->_defaultRaw($curctx, $this->_getNodeOnlyText($node)),
+ 'is_default' => $this->_isDefaultRaw($curctx, $this->_getNodeOnlyText($node))
+ );
+ break;
+
+ case 'configsecret':
+ $conf[$name] = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => $desc,
+ 'default' => $this->_default($curctx, sha1(uniqid(mt_rand(), true))),
+ 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
+ );
+ break;
+
+ case 'configsql':
+ $conf[$node->get_attribute('switchname')] = $this->_configSQL($ctx, $node);
+ break;
+
+ case 'configvfs':
+ $conf[$node->get_attribute('switchname')] = $this->_configVFS($ctx, $node);
+ break;
+
+ case 'configsection':
+ $conf[$name] = array();
+ $cur = &$conf[$name];
+ if ($node->has_child_nodes()) {
+ $this->_parseLevel($cur, $node->child_nodes(), $curctx);
+ }
+ break;
+
+ case 'configtab':
+ $key = hash('md5', uniqid(mt_rand(), true));
+
+ $conf[$key] = array(
+ 'tab' => $name,
+ 'desc' => $desc
+ );
+
+ if ($node->has_child_nodes()) {
+ $this->_parseLevel($conf, $node->child_nodes(), $ctx);
+ }
+ break;
+
+ case 'configplaceholder':
+ $conf[hash('md5', uniqid(mt_rand(), true))] = 'placeholder';
+ break;
+
+ default:
+ $conf[$name] = array();
+ $cur = &$conf[$name];
+ if ($node->has_child_nodes()) {
+ $this->_parseLevel($cur, $node->child_nodes(), $curctx);
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns the configuration tree for an SQL backend configuration to
+ * replace a <configsql> tag.
+ * Subnodes will be parsed and added to both the Horde defaults and the
+ * Custom configuration parts.
+ *
+ * @param string $ctx The context of the <configsql> tag.
+ * @param DomNode $node The DomNode representation of the <configsql>
+ * tag.
+ * @param string $switchname If DomNode is not set, the value of the
+ * tag's switchname attribute.
+ *
+ * @return array An associative array with the SQL configuration tree.
+ */
+ protected function _configSQL($ctx, $node = null,
+ $switchname = 'driverconfig')
+ {
+ $persistent = array(
+ '_type' => 'boolean',
+ 'required' => false,
+ 'desc' => 'Request persistent connections?',
+ 'default' => $this->_default($ctx . '|persistent', false)
+ );
+
+ $hostspec = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Database server/host',
+ 'default' => $this->_default($ctx . '|hostspec', '')
+ );
+
+ $username = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Username to connect to the database as',
+ 'default' => $this->_default($ctx . '|username', '')
+ );
+
+ $password = array(
+ '_type' => 'text',
+ 'required' => false,
+ 'desc' => 'Password to connect with',
+ 'default' => $this->_default($ctx . '|password', '')
+ );
+
+ $database = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Database name to use',
+ 'default' => $this->_default($ctx . '|database', '')
+ );
+
+ $socket = array(
+ '_type' => 'text',
+ 'required' => false,
+ 'desc' => 'Location of UNIX socket',
+ 'default' => $this->_default($ctx . '|socket', '')
+ );
+
+ $port = array(
+ '_type' => 'int',
+ 'required' => false,
+ 'desc' => 'Port the DB is running on, if non-standard',
+ 'default' => $this->_default($ctx . '|port', null)
+ );
+
+ $protocol = array(
+ 'desc' => 'How should we connect to the database?',
+ 'default' => $this->_default($ctx . '|protocol', 'unix'),
+ 'switch' => array(
+ 'unix' => array(
+ 'desc' => 'UNIX Sockets',
+ 'fields' => array(
+ 'socket' => $socket
+ )
+ ),
+ 'tcp' => array(
+ 'desc' => 'TCP/IP',
+ 'fields' => array(
+ 'hostspec' => $hostspec,
+ 'port' => $port
+ )
+ )
+ )
+ );
+
+ $mysql_protocol = $protocol;
+ $mysql_protocol['switch']['tcp']['fields']['port']['default'] = $this->_default($ctx . '|port', 3306);
+
+ $charset = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Internally used charset',
+ 'default' => $this->_default($ctx . '|charset', 'utf-8')
+ );
+
+ $ssl = array(
+ '_type' => 'boolean',
+ 'required' => false,
+ 'desc' => 'Use SSL to connect to the server?',
+ 'default' => $this->_default($ctx . '|ssl', false)
+ );
+
+ $ca = array(
+ '_type' => 'text',
+ 'required' => false,
+ 'desc' => 'Certification Authority to use for SSL connections',
+ 'default' => $this->_default($ctx . '|ca', '')
+ );
+
+ $oci8_fields = array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password
+ );
+ if (function_exists('oci_connect')) {
+ $oci8_fields['database'] = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Database name or Easy Connect parameter',
+ 'default' => $this->_default($ctx . '|database', 'horde')
+ );
+ } else {
+ $oci8_fields['hostspec'] = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Database name or Easy Connect parameter',
+ 'default' => $this->_default($ctx . '|hostspec', 'horde')
+ );
+ }
+ $oci8_fields['charset'] = $charset;
+
+ $read_hostspec = array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Read database server/host',
+ 'default' => $this->_default($ctx . '|read|hostspec', '')
+ );
+
+ $read_port = array(
+ '_type' => 'int',
+ 'required' => false,
+ 'desc' => 'Port the read DB is running on, if non-standard',
+ 'default' => $this->_default($ctx . '|read|port', null)
+ );
+
+ $splitread = array(
+ '_type' => 'boolean',
+ 'required' => false,
+ 'desc' => 'Split reads to a different server?',
+ 'default' => $this->_default($ctx . '|splitread', 'false'),
+ 'switch' => array(
+ 'false' => array(
+ 'desc' => 'Disabled',
+ 'fields' => array()
+ ),
+ 'true' => array(
+ 'desc' => 'Enabled',
+ 'fields' => array(
+ 'read' => array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password,
+ 'protocol' => $mysql_protocol,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ )
+ )
+ )
+ );
+
+ $custom_fields = array(
+ 'required' => true,
+ 'desc' => 'What database backend should we use?',
+ 'default' => $this->_default($ctx . '|phptype', 'false'),
+ 'switch' => array(
+ 'false' => array(
+ 'desc' => '[None]',
+ 'fields' => array()
+ ),
+ 'dbase' => array(
+ 'desc' => 'dBase',
+ 'fields' => array(
+ 'database' => array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Absolute path to the database file',
+ 'default' => $this->_default($ctx . '|database', '')
+ ),
+ 'mode' => array(
+ '_type' => 'enum',
+ 'desc' => 'The mode to open the file with',
+ 'values' => array(
+ 0 => 'Read Only',
+ 2 => 'Read Write'),
+ 'default' => $this->_default($ctx . '|mode', 2)
+ ),
+ 'charset' => $charset
+ )
+ ),
+ 'ibase' => array(
+ 'desc' => 'Firebird/InterBase',
+ 'fields' => array(
+ 'dbsyntax' => array(
+ '_type' => 'enum',
+ 'desc' => 'The database syntax variant to use',
+ 'required' => false,
+ 'values' => array(
+ 'ibase' => 'InterBase',
+ 'firebird' => 'Firebird'
+ ),
+ 'default' => $this->_default($ctx . '|dbsyntax', 'firebird')
+ ),
+ 'persistent' => $persistent,
+ 'hostspec' => $hostspec,
+ 'username' => $username,
+ 'password' => $password,
+ 'database' => $database,
+ 'buffers' => array(
+ '_type' => 'int',
+ 'desc' => 'The number of database buffers to allocate',
+ 'required' => false,
+ 'default' => $this->_default($ctx . '|buffers', null)
+ ),
+ 'dialect' => array(
+ '_type' => 'int',
+ 'desc' => 'The default SQL dialect for any statement executed within a connection.',
+ 'required' => false,
+ 'default' => $this->_default($ctx . '|dialect', null)
+ ),
+ 'role' => array(
+ '_type' => 'text',
+ 'desc' => 'Role',
+ 'required' => false,
+ 'default' => $this->_default($ctx . '|role', null)),
+ 'charset' => $charset
+ )
+ ),
+ 'fbsql' => array(
+ 'desc' => 'Frontbase',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'hostspec' => $hostspec,
+ 'username' => $username,
+ 'password' => $password,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ ),
+ 'ifx' => array(
+ 'desc' => 'Informix',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ ),
+ 'msql' => array(
+ 'desc' => 'mSQL',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'hostspec' => $hostspec,
+ 'username' => $username,
+ 'password' => $password,
+ 'port' => $port,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ ),
+ 'mssql' => array(
+ 'desc' => 'MS SQL Server',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'hostspec' => $hostspec,
+ 'username' => $username,
+ 'password' => $password,
+ 'port' => $port,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ ),
+ 'mysql' => array(
+ 'desc' => 'MySQL',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password,
+ 'protocol' => $mysql_protocol,
+ 'database' => $database,
+ 'charset' => $charset,
+ 'ssl' => $ssl,
+ 'ca' => $ca,
+ 'splitread' => $splitread
+ )
+ ),
+ 'mysqli' => array(
+ 'desc' => 'MySQL (mysqli)',
+ 'fields' => array(
+ 'username' => $username,
+ 'password' => $password,
+ 'protocol' => $mysql_protocol,
+ 'database' => $database,
+ 'charset' => $charset,
+ 'splitread' => $splitread,
+ 'ssl' => $ssl,
+ 'ca' => $ca
+ )),
+ 'oci8' => array(
+ 'desc' => 'Oracle',
+ 'fields' => $oci8_fields
+ ),
+ 'odbc' => array(
+ 'desc' => 'ODBC',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password,
+ 'hostspec' => array(
+ '_type' => 'text',
+ 'desc' => 'DSN',
+ 'default' => $this->_default($ctx . '|hostspec', '')
+ ),
+ 'dbsyntax' => array(
+ '_type' => 'enum',
+ 'desc' => 'The database syntax variant to use',
+ 'required' => false,
+ 'values' => array(
+ 'sql92' => 'SQL92',
+ 'access' => 'Access',
+ 'db2' => 'DB2',
+ 'solid' => 'Solid',
+ 'navision' => 'Navision',
+ 'mssql' => 'MS SQL Server',
+ 'sybase' => 'Sybase',
+ 'mysql' => 'MySQL',
+ 'mysqli' => 'MySQL (mysqli)',
+ ),
+ 'default' => $this->_default($ctx . '|dbsyntax', 'sql92')
+ ),
+ 'cursor' => array(
+ '_type' => 'enum',
+ 'desc' => 'Cursor type',
+ 'quote' => false,
+ 'required' => false,
+ 'values' => array(
+ 'null' => 'None',
+ 'SQL_CUR_DEFAULT' => 'Default',
+ 'SQL_CUR_USE_DRIVER' => 'Use Driver',
+ 'SQL_CUR_USE_ODBC' => 'Use ODBC',
+ 'SQL_CUR_USE_IF_NEEDED' => 'Use If Needed'
+ ),
+ 'default' => $this->_default($ctx . '|cursor', null)
+ ),
+ 'charset' => $charset
+ )
+ ),
+ 'pgsql' => array(
+ 'desc' => 'PostgreSQL',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'username' => $username,
+ 'password' => $password,
+ 'protocol' => $protocol,
+ 'database' => $database,
+ 'charset' => $charset
+ )
+ ),
+ 'sqlite' => array(
+ 'desc' => 'SQLite',
+ 'fields' => array(
+ 'database' => array(
+ '_type' => 'text',
+ 'required' => true,
+ 'desc' => 'Absolute path to the database file',
+ 'default' => $this->_default($ctx . '|database', '')
+ ),
+ 'mode' => array(
+ '_type' => 'text',
+ 'desc' => 'The mode to open the file with',
+ 'default' => $this->_default($ctx . '|mode', '0644')
+ ),
+ 'charset' => $charset
+ )
+ ),
+ 'sybase' => array(
+ 'desc' => 'Sybase',
+ 'fields' => array(
+ 'persistent' => $persistent,
+ 'hostspec' => $hostspec,
+ 'username' => $username,
+ 'password' => $password,
+ 'database' => $database,
+ 'appname' => array(
+ '_type' => 'text',
+ 'desc' => 'Application Name',
+ 'required' => false,
+ 'default' => $this->_default($ctx . '|appname', '')
+ ),
+ 'charset' => $charset
+ )
+ )
+ )
+ );
+
+ if (isset($node) && $node->get_attribute('baseconfig') == 'true') {
+ return $custom_fields;
+ }
+
+ list($default, $isDefault) = $this->__default($ctx . '|' . (isset($node) ? $node->get_attribute('switchname') : $switchname), 'horde');
+ $config = array(
+ 'desc' => 'Driver configuration',
+ 'default' => $default,
+ 'is_default' => $isDefault,
+ 'switch' => array(
+ 'horde' => array(
+ 'desc' => 'Horde defaults',
+ 'fields' => array()
+ ),
+ 'custom' => array(
+ 'desc' => 'Custom parameters',
+ 'fields' => array(
+ 'phptype' => $custom_fields
+ )
+ )
+ )
+ );
+
+ if (isset($node) && $node->has_child_nodes()) {
+ $cur = array();
+ $this->_parseLevel($cur, $node->child_nodes(), $ctx);
+ $config['switch']['horde']['fields'] = array_merge($config['switch']['horde']['fields'], $cur);
+ $config['switch']['custom']['fields'] = array_merge($config['switch']['custom']['fields'], $cur);
+ }
+
+ return $config;
+ }
+
+ /**
+ * Returns the configuration tree for a VFS backend configuration to
+ * replace a <configvfs> tag.
+ * Subnodes will be parsed and added to both the Horde defaults and the
+ * Custom configuration parts.
+ *
+ * @param string $ctx The context of the <configvfs> tag.
+ * @param DomNode $node The DomNode representation of the <configvfs>
+ * tag.
+ *
+ * @return array An associative array with the VFS configuration tree.
+ */
+ protected function _configVFS($ctx, $node)
+ {
+ $sql = $this->_configSQL($ctx . '|params');
+ $default = $node->get_attribute('default');
+ $default = empty($default) ? 'none' : $default;
+ list($default, $isDefault) = $this->__default($ctx . '|' . $node->get_attribute('switchname'), $default);
+
+ $config = array(
+ 'desc' => 'What VFS driver should we use?',
+ 'default' => $default,
+ 'is_default' => $isDefault,
+ 'switch' => array(
+ 'none' => array(
+ 'desc' => 'None',
+ 'fields' => array()
+ ),
+ 'file' => array(
+ 'desc' => 'Files on the local system',
+ 'fields' => array(
+ 'params' => array(
+ 'vfsroot' => array(
+ '_type' => 'text',
+ 'desc' => 'Where on the real filesystem should Horde use as root of the virtual filesystem?',
+ 'default' => $this->_default($ctx . '|params|vfsroot', '/tmp')
+ )
+ )
+ )
+ ),
+ 'sql' => array(
+ 'desc' => 'SQL database',
+ 'fields' => array(
+ 'params' => array(
+ 'driverconfig' => $sql
+ )
+ )
+ )
+ )
+ );
+
+ if (isset($node) && $node->get_attribute('baseconfig') != 'true') {
+ $config['switch']['horde'] = array(
+ 'desc' => 'Horde defaults',
+ 'fields' => array()
+ );
+ }
+ $cases = $this->_getSwitchValues($node, $ctx . '|params');
+ foreach ($cases as $case => $fields) {
+ if (isset($config['switch'][$case])) {
+ $config['switch'][$case]['fields']['params'] = array_merge($config['switch'][$case]['fields']['params'], $fields['fields']);
+ }
+ }
+
+ return $config;
+ }
+
+ /**
+ * Returns a certain value from the current configuration array or
+ * a default value, if not found.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return mixed Either the value of the configuration array's requested
+ * key or the default value if the key wasn't found.
+ */
+ protected function _default($ctx, $default)
+ {
+ list ($ptr,) = $this->__default($ctx, $default);
+ return $ptr;
+ }
+
+ /**
+ * Returns whether a certain value from the current configuration array
+ * exists or a default value will be used.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return boolean Whether the default value will be used.
+ */
+ protected function _isDefault($ctx, $default)
+ {
+ list (,$isDefault) = $this->__default($ctx, $default);
+ return $isDefault;
+ }
+
+ /**
+ * Returns a certain value from the current configuration array or a
+ * default value, if not found, and which of the values have been
+ * returned.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return array First element: either the value of the configuration
+ * array's requested key or the default value if the key
+ * wasn't found.
+ * Second element: whether the returned value was the
+ * default value.
+ */
+ protected function __default($ctx, $default)
+ {
+ $ctx = explode('|', $ctx);
+ $ptr = $this->_currentConfig;
+
+ for ($i = 0; $i < count($ctx); ++$i) {
+ if (!isset($ptr[$ctx[$i]])) {
+ return array($default, true);
+ }
+
+ $ptr = $ptr[$ctx[$i]];
+ }
+
+ if (is_string($ptr)) {
+ $ptr = Horde_String::convertCharset($ptr, 'iso-8859-1');
+ }
+
+ return array($ptr, false);
+ }
+
+ /**
+ * Returns a certain value from the current configuration file or
+ * a default value, if not found.
+ * It does NOT return the actual value, but the PHP expression as used
+ * in the configuration file.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return mixed Either the value of the configuration file's requested
+ * key or the default value if the key wasn't found.
+ */
+ protected function _defaultRaw($ctx, $default)
+ {
+ list ($ptr,) = $this->__defaultRaw($ctx, $default);
+ return $ptr;
+ }
+
+ /**
+ * Returns whether a certain value from the current configuration array
+ * exists or a default value will be used.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return boolean Whether the default value will be used.
+ */
+ protected function _isDefaultRaw($ctx, $default)
+ {
+ list (,$isDefault) = $this->__defaultRaw($ctx, $default);
+ return $isDefault;
+ }
+
+ /**
+ * Returns a certain value from the current configuration file or
+ * a default value, if not found, and which of the values have been
+ * returned.
+ *
+ * It does NOT return the actual value, but the PHP expression as used
+ * in the configuration file.
+ *
+ * @param string $ctx A string representing the key of the
+ * configuration array to return.
+ * @param mixed $default The default value to return if the key wasn't
+ * found.
+ *
+ * @return array First element: either the value of the configuration
+ * array's requested key or the default value if the key
+ * wasn't found.
+ * Second element: whether the returned value was the
+ * default value.
+ */
+ protected function __defaultRaw($ctx, $default)
+ {
+ $ctx = explode('|', $ctx);
+ $pattern = '/^\$conf\[\'' . implode("'\]\['", $ctx) . '\'\] = (.*);\r?$/m';
+
+ return preg_match($pattern, $this->getPHPConfig(), $matches)
+ ? array($matches[1], false)
+ : array($default, true);
+ }
+
+ /**
+ * Returns the content of all text node children of the specified node.
+ *
+ * @param DomNode $node A DomNode object whose text node children to
+ * return.
+ *
+ * @return string The concatenated values of all text nodes.
+ */
+ protected function _getNodeOnlyText($node)
+ {
+ $text = '';
+
+ if (!$node->has_child_nodes()) {
+ return $node->get_content();
+ }
+
+ foreach ($node->child_nodes() as $tnode) {
+ if ($tnode->type == XML_TEXT_NODE) {
+ $text .= $tnode->content;
+ }
+ }
+
+ return trim($text);
+ }
+
+ /**
+ * Returns an associative array containing all possible values of the
+ * specified <configenum> tag.
+ *
+ * The keys contain the actual enum values while the values contain their
+ * corresponding descriptions.
+ *
+ * @param DomNode $node The DomNode representation of the <configenum>
+ * tag whose values should be returned.
+ *
+ * @return array An associative array with all possible enum values.
+ */
+ protected function _getEnumValues($node)
+ {
+ $values = array();
+
+ if (!$node->has_child_nodes()) {
+ return $values;
+ }
+
+ foreach ($node->child_nodes() as $vnode) {
+ if ($vnode->type == XML_ELEMENT_NODE &&
+ $vnode->tagname == 'values') {
+ if (!$vnode->has_child_nodes()) {
+ return array();
+ }
+
+ foreach ($vnode->child_nodes() as $value) {
+ if ($value->type == XML_ELEMENT_NODE) {
+ if ($value->tagname == 'configspecial') {
+ return $this->_handleSpecials($value);
+ } elseif ($value->tagname == 'value') {
+ $text = $value->get_content();
+ $desc = $value->get_attribute('desc');
+ $values[$text] = empty($desc) ? $text : $desc;
+ }
+ }
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns a multidimensional associative array representing the specified
+ * <configswitch> tag.
+ *
+ * @param DomNode &$node The DomNode representation of the <configswitch>
+ * tag to process.
+ *
+ * @return array An associative array representing the node.
+ */
+ protected function _getSwitchValues(&$node, $curctx)
+ {
+ $values = array();
+
+ if (!$node->has_child_nodes()) {
+ return $values;
+ }
+
+ foreach ($node->child_nodes() as $case) {
+ if ($case->type == XML_ELEMENT_NODE) {
+ $name = $case->get_attribute('name');
+ $values[$name] = array(
+ 'desc' => $case->get_attribute('desc'),
+ 'fields' => array()
+ );
+ if ($case->has_child_nodes()) {
+ $this->_parseLevel($values[$name]['fields'], $case->child_nodes(), $curctx);
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Returns an associative array containing the possible values of a
+ * <configspecial> tag as used inside of enum configurations.
+ *
+ * @param DomNode $node The DomNode representation of the <configspecial>
+ * tag.
+ *
+ * @return array An associative array with the possible values.
+ */
+ protected function _handleSpecials($node)
+ {
+ switch ($node->get_attribute('name')) {
+ case 'list-horde-apps':
+ $apps = Horde_Array::valuesToKeys($GLOBALS['registry']->listApps(array('hidden', 'notoolbar', 'active')));
+ asort($apps);
+ return $apps;
+
+ case 'list-horde-languages':
+ return array_map(create_function('$val', 'return preg_replace(array("/&#x([0-9a-f]{4});/ie", "/(&[^;]+;)/e"), array("Horde_String::convertCharset(pack(\"H*\", \"$1\"), \"ucs-2\", \"' . Horde_Nls::getCharset() . '\")", "Horde_String::convertCharset(html_entity_decode(\"$1\", ENT_COMPAT, \"iso-8859-1\"), \"iso-8859-1\", \"' . Horde_Nls::getCharset() . '\")"), $val);'), $GLOBALS['nls']['languages']);
+
+ case 'list-blocks':
+ $collection = Horde_Block_Collection::singleton('portal');
+ return $collection->getBlocksList();
+
+ case 'list-client-fields':
+ global $registry;
+ $f = array();
+ if ($GLOBALS['registry']->hasMethod('clients/getClientSource')) {
+ $addressbook = $GLOBALS['registry']->call('clients/getClientSource');
+ $fields = $GLOBALS['registry']->call('clients/clientFields', array($addressbook));
+ if ($fields instanceof PEAR_Error) {
+ $fields = $GLOBALS['registry']->call('clients/fields', array($addressbook));
+ }
+ if (!$fields instanceof PEAR_Error) {
+ foreach ($fields as $field) {
+ $f[$field['name']] = $field['label'];
+ }
+ }
+ }
+ return $f;
+
+ case 'list-contact-sources':
+ $res = $GLOBALS['registry']->call('contacts/sources');
+ return $res;
+ }
+
+ return array();
+ }
+
+ /**
+ * Returns the specified string with escaped single quotes
+ *
+ * @param string $string A string to escape.
+ *
+ * @return string The specified string with single quotes being escaped.
+ */
+ protected function _quote($string)
+ {
+ return str_replace("'", "\'", $string);
+ }
+
+}
+
+/**
+ * A Horde_Form:: form that implements a user interface for the config
+ * system.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @package Core
+ */
+class ConfigForm extends Horde_Form
+{
+ /**
+ * Don't use form tokens for the configuration form - while
+ * generating configuration info, things like the Token system
+ * might not work correctly. This saves some headaches.
+ *
+ * @var boolean
+ */
+ protected $_useFormToken = false;
+
+ /**
+ * Contains the Horde_Config object that this form represents.
+ *
+ * @var Horde_Config
+ */
+ protected $_xmlConfig;
+
+ /**
+ * Contains the Horde_Variables object of this form.
+ *
+ * @var Horde_Variables
+ */
+ protected $_vars;
+
+ /**
+ * Constructor.
+ *
+ * @param Horde_Variables &$vars The variables object of this form.
+ * @param string $app The name of the application that this
+ * configuration form is for.
+ */
+ public function __construct(&$vars, $app)
+ {
+ parent::__construct($vars);
+
+ $this->_xmlConfig = new Horde_Config($app);
+ $this->_vars = &$vars;
+ $config = $this->_xmlConfig->readXMLConfig();
+ $this->addHidden('', 'app', 'text', true);
+ $this->_buildVariables($config);
+ }
+
+ /**
+ * Builds the form based on the specified level of the configuration tree.
+ *
+ * @param array $config The portion of the configuration tree for that
+ * the form fields should be created.
+ * @param string $prefix A string representing the current position
+ * inside the configuration tree.
+ */
+ protected function _buildVariables($config, $prefix = '')
+ {
+ if (!is_array($config)) {
+ return;
+ }
+
+ foreach ($config as $name => $configitem) {
+ $prefixedname = empty($prefix) ? $name : $prefix . '|' . $name;
+ $varname = str_replace('|', '__', $prefixedname);
+ if ($configitem == 'placeholder') {
+ continue;
+ } elseif (isset($configitem['tab'])) {
+ $this->setSection($configitem['tab'], $configitem['desc']);
+ } elseif (isset($configitem['switch'])) {
+ $selected = $this->_vars->getExists($varname, $wasset);
+ $var_params = array();
+ $select_option = true;
+ if (is_bool($configitem['default'])) {
+ $configitem['default'] = $configitem['default'] ? 'true' : 'false';
+ }
+ foreach ($configitem['switch'] as $option => $case) {
+ $var_params[$option] = $case['desc'];
+ if ($option == $configitem['default']) {
+ $select_option = false;
+ if (!$wasset) {
+ $selected = $option;
+ }
+ }
+ }
+
+ $name = '$conf[' . implode('][', explode('|', $prefixedname)) . ']';
+ $desc = $configitem['desc'];
+
+ $v = &$this->addVariable($name, $varname, 'enum', true, false, $desc, array($var_params, $select_option));
+ if (array_key_exists('default', $configitem)) {
+ $v->setDefault($configitem['default']);
+ }
+ if (!empty($configitem['is_default'])) {
+ $v->_new = true;
+ }
+ $v_action = Horde_Form_Action::factory('reload');
+ $v->setAction($v_action);
+ if (isset($selected) && isset($configitem['switch'][$selected])) {
+ $this->_buildVariables($configitem['switch'][$selected]['fields'], $prefix);
+ }
+ } elseif (isset($configitem['_type'])) {
+ $required = (isset($configitem['required'])) ? $configitem['required'] : true;
+ $type = $configitem['_type'];
+
+ // FIXME: multienum fields can well be required, meaning that
+ // you need to select at least one entry. Changing this before
+ // Horde 4.0 would break a lot of configuration files though.
+ if ($type == 'multienum' || $type == 'header' ||
+ $type == 'description') {
+ $required = false;
+ }
+
+ $var_params = ($type == 'multienum' || $type == 'enum')
+ ? array($configitem['values'])
+ : array();
+
+ if ($type == 'header' || $type == 'description') {
+ $name = $configitem['desc'];
+ $desc = null;
+ } else {
+ $name = '$conf[' . implode('][', explode('|', $prefixedname)) . ']';
+ $desc = $configitem['desc'];
+ if ($type == 'php') {
+ $type = 'text';
+ $desc .= "\nEnter a valid PHP expression.";
+ }
+ }
+
+ $v = &$this->addVariable($name, $varname, $type, $required, false, $desc, $var_params);
+ if (isset($configitem['default'])) {
+ $v->setDefault($configitem['default']);
+ }
+ if (!empty($configitem['is_default'])) {
+ $v->_new = true;
+ }
+ } else {
+ $this->_buildVariables($configitem, $prefixedname);
+ }
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Horde_ErrorHandler: simple error_handler implementation for
+ * handling PHP errors, generating backtraces for them, etc.
+ *
+ * @TODO Split dump() off into a Horde_Log backend, and make this more
+ * general-purpose. Also make it configurable whether or not to honor
+ * suppression of errors with @.
+ *
+ * @category Horde
+ * @package Core
+ */
+class Horde_ErrorHandler
+{
+ /**
+ * Mapping of error codes to error code names.
+ *
+ * @var array
+ */
+ public static $errorTypes = array(
+ 1 => 'ERROR',
+ 2 => 'WARNING',
+ 4 => 'PARSE',
+ 8 => 'NOTICE',
+ 16 => 'CORE_ERROR',
+ 32 => 'CORE_WARNING',
+ 64 => 'COMPILE_ERROR',
+ 128 => 'COMPILE_WARNING',
+ 256 => 'USER_ERROR',
+ 512 => 'USER_WARNING',
+ 1024 => 'USER_NOTICE',
+ 2047 => 'ALL',
+ 2048 => 'STRICT',
+ 4096 => 'RECOVERABLE_ERROR',
+ );
+
+ /**
+ * error_reporting mask
+ *
+ * @var integer
+ */
+ protected static $_mask = E_ALL;
+
+ /**
+ * Array of errors that have been caught.
+ *
+ * @var array
+ */
+ protected static $_errors = array();
+
+ /**
+ * Configurable function to run on shutdown.
+ *
+ * @var callable
+ */
+ protected static $_shutdownFunc;
+
+ /**
+ * Set the error handler and shutdown functions.
+ *
+ * @param TODO
+ */
+ public static function register($shutdownFunc = null)
+ {
+ set_error_handler(array(__CLASS__, 'handleError'));
+
+ if (is_null($shutdownFunc)) {
+ $shutdownFunc = array(__CLASS__, 'dump');
+ }
+
+ self::$_shutdownFunc = $shutdownFunc;
+ }
+
+ /**
+ * Call the shutdown func, passing in accumulated errors.
+ */
+ public function __destruct()
+ {
+ if (self::$_errors) {
+ call_user_func(self::$_shutdownFunc, self::$_errors);
+ }
+ }
+
+ /**
+ * Process and handle/store an error.
+ *
+ * @param integer $errno TODO
+ * @param string $errstr TODO
+ * @param string $errfile TODO
+ * @param integer $errline TODO
+ */
+ public static function handleError($errno, $errstr, $errfile, $errline)
+ {
+ // Was the error suppressed?
+ if (!error_reporting()) {
+ // @TODO
+ // ...
+ }
+
+ // Check the mask.
+ if ($errno & self::$_mask) {
+ self::$_errors[] = array(
+ 'no' => $errno,
+ 'str' => self::_cleanErrorString($errstr),
+ 'file' => $errfile,
+ 'line' => $errline,
+ 'trace' => self::_errorBacktrace(),
+ );
+ }
+ }
+
+ /**
+ * Include the context of the error in the debug
+ * information. Takes more (and could be much more) memory.
+ *
+ * @param integer $errno TODO
+ * @param string $errstr TODO
+ * @param string $errfile TODO
+ * @param integer $errline TODO
+ * @param TODO $errcontext TODO
+ */
+ public static function handleErrorWithContext($errno, $errstr, $errfile,
+ $errline, $errcontext)
+ {
+ self::$_errors[] = array(
+ 'no' => $errno,
+ 'str' => self::_cleanErrorString($errstr),
+ 'file' => $errfile,
+ 'line' => $errline,
+ 'context' => $errcontext,
+ 'trace' => self::_errorBacktrace(),
+ );
+ }
+
+ /**
+ * Remove function documentation links from an error string.
+ *
+ * @param string $errstr TODO
+ *
+ * @return string TODO
+ */
+ protected static function _cleanErrorString($errstr)
+ {
+ return preg_replace("%\s\[<a href='function\.[\d\w-_]+'>function\.[\d\w-_]+</a>\]%", '', $errstr);
+ }
+
+ /**
+ * Generate an exception-like backtrace from the debug_backtrace()
+ * function for errors.
+ *
+ * @return array TODO
+ */
+ protected static function _errorBacktrace()
+ {
+ // Skip two levels of backtrace
+ $skip = 2;
+
+ $backtrace = debug_backtrace();
+ $trace = array();
+ for ($i = $skip, $i_max = count($backtrace); $i < $i_max; $i++) {
+ $frame = $backtrace[$i];
+ $trace[$i - $skip] = array(
+ 'file' => isset($frame['file']) ? $frame['file'] : null,
+ 'line' => isset($frame['line']) ? $frame['line'] : null,
+ 'function' => isset($frame['function']) ? $frame['function'] : null,
+ 'class' => isset($frame['class']) ? $frame['class'] : null,
+ 'type' => isset($frame['type']) ? $frame['type'] : null,
+ 'args' => isset($frame['args']) ? $frame['args'] : null,
+ );
+ }
+
+ return $trace;
+ }
+
+ /**
+ * On text/html pages, if the user is an administrator, show all
+ * errors that occurred during the request.
+ *
+ * @param array $errors Accumulated errors.
+ */
+ public static function dump($errors)
+ {
+ if (!Horde_Auth::isAdmin()) {
+ return;
+ }
+
+ $dump = false;
+ foreach (headers_list() as $header) {
+ if (strpos($header, 'Content-type: text/html') !== false) {
+ $dump = true;
+ break;
+ }
+ }
+
+ if ($dump) {
+ foreach ($errors as $error) {
+ echo '<p>' . htmlspecialchars($error['file']) . ':' . htmlspecialchars($error['line']) . ': ' . htmlspecialchars($error['str']) . '</p>';
+ }
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Horde base exception class, which includes the ability to take the
+ * output of error_get_last() as $code and mask itself as that error.
+ *
+ * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package Core
+ */
+class Horde_Exception extends Exception
+{
+ /**
+ * Exception constructor
+ *
+ * If $code_or_lasterror is passed the return value of
+ * error_get_last() (or a matching format), the exception will be
+ * rewritten to have its file and line parameters match that of
+ * the array, and any message in the array will be appended to
+ * $message.
+ *
+ * @param mixed $message The exception message, a PEAR_Error
+ * object, or an Exception object.
+ * @param mixed $code_or_lasterror Either a numeric error code, or
+ * an array from error_get_last().
+ */
+ public function __construct($message = null, $code_or_lasterror = null)
+ {
+ if (is_object($message) &&
+ method_exists($message, 'getMessage')) {
+ if (is_null($code_or_lasterror) &&
+ method_exists($message, 'getCode')) {
+ $code_or_lasterror = $message->getCode();
+ }
+ $message = $message->getMessage();
+ }
+
+ if (is_null($code_or_lasterror)) {
+ $code_or_lasterror = 0;
+ }
+
+ if (is_array($code_or_lasterror)) {
+ if ($message) {
+ $message .= $code_or_lasterror['message'];
+ } else {
+ $message = $code_or_lasterror['message'];
+ }
+
+ $this->file = $code_or_lasterror['file'];
+ $this->line = $code_or_lasterror['line'];
+ $code = $code_or_lasterror['type'];
+ } else {
+ $code = $code_or_lasterror;
+ }
+
+ if (is_string($code)) {
+ $code = null;
+ }
+
+ parent::__construct($message, $code);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Help:: class provides an interface to the online help subsystem.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Jon Parise <jon@horde.org>
+ * @package Core
+ */
+class Horde_Help
+{
+ /* Raw help in the string. */
+ const SOURCE_RAW = 0;
+
+ /* Help text is in a file. */
+ const SOURCE_FILE = 1;
+
+ /**
+ * Handle for the XML parser object.
+ *
+ * @var resource
+ */
+ protected $_parser;
+
+ /**
+ * String buffer to hold the XML help source.
+ *
+ * @var string
+ */
+ protected $_buffer = '';
+
+ /**
+ * String containing the ID of the requested help entry.
+ *
+ * @var string
+ */
+ protected $_reqEntry = '';
+
+ /**
+ * String containing the ID of the current help entry.
+ *
+ * @var string
+ */
+ protected $_curEntry = '';
+
+ /**
+ * String containing the formatted output.
+ *
+ * @var string
+ */
+ protected $_output = '';
+
+ /**
+ * Boolean indicating whether we're inside a <help> block.
+ *
+ * @var boolean
+ */
+ protected $_inHelp = false;
+
+ /**
+ * Boolean indicating whether we're inside the requested block.
+ *
+ * @var boolean
+ */
+ protected $_inBlock = false;
+
+ /**
+ * Boolean indicating whether we're inside a <title> block.
+ *
+ * @var boolean
+ */
+ protected $_inTitle = false;
+
+ /**
+ * Boolean indicating whether we're inside a heading block.
+ *
+ * @var boolean
+ */
+ protected $_inHeading = false;
+
+ /**
+ * Hash containing an index of all of the help entries.
+ *
+ * @var array
+ */
+ protected $_entries = array();
+
+ /**
+ * String containing the charset of the XML data source.
+ *
+ * @var string
+ */
+ protected $_charset = 'iso-8859-1';
+
+ /**
+ * Hash of user-defined function handlers for the XML elements.
+ *
+ * @var array
+ */
+ protected $_handlers = array(
+ 'help' => '_helpHandler',
+ 'entry' => '_entryHandler',
+ 'title' => '_titleHandler',
+ 'heading' => '_headingHandler',
+ 'para' => '_paraHandler',
+ 'ref' => '_refHandler',
+ 'eref' => '_erefHandler',
+ 'href' => '_hrefHandler',
+ 'b' => '_bHandler',
+ 'i' => '_iHandler',
+ 'pre' => '_preHandler',
+ 'tip' => '_tipHandler',
+ 'warn' => '_warnHandler'
+ );
+
+ /**
+ * Hash containing an index of all of the search results.
+ *
+ * @var array
+ */
+ protected $_search = array();
+
+ /**
+ * String containing the keyword for the search.
+ *
+ * @var string
+ */
+ protected $_keyword = '';
+
+ /**
+ * Constructor.
+ *
+ * @param integer $source The source of the XML help data, based on the
+ * SOURCE_* constants.
+ * @param string $arg Source-dependent argument for this Help
+ * instance.
+ */
+ public function __construct($source, $arg = null)
+ {
+ if (isset($GLOBALS['nls']['charsets'][$GLOBALS['language']])) {
+ $this->_charset = $GLOBALS['nls']['charsets'][$GLOBALS['language']];
+ }
+
+ /* Populate $this->_buffer based on $source. */
+ switch ($source) {
+ case self::SOURCE_RAW:
+ $this->_buffer = $arg;
+ break;
+
+ case self::SOURCE_FILE:
+ if (file_exists($arg[0]) && filesize($arg[0])) {
+ $this->_buffer = file_get_contents($arg[0]);
+ } elseif (file_exists($arg[1]) && filesize($arg[1])) {
+ $this->_buffer = file_get_contents($arg[1]);
+ } else {
+ $this->_buffer = '';
+ }
+ break;
+
+ default:
+ $this->_buffer = '';
+ break;
+ }
+ }
+
+ /**
+ * Generates the HTML link that will pop up a help window for the
+ * requested topic.
+ *
+ * @param string $module The name of the current Horde module.
+ * @param string $topic The help topic to be displayed.
+ *
+ * @return string The HTML to create the help link.
+ */
+ static public function link($module, $topic)
+ {
+ if (!Horde::showService('help')) {
+ return ' ';
+ }
+
+ if ($GLOBALS['browser']->hasFeature('javascript')) {
+ Horde::addScriptFile('popup.js', 'horde');
+ }
+
+ $url = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/help/', true);
+ $url = Horde_Util::addParameter($url, array('module' => $module,
+ 'topic' => $topic));
+
+ return Horde::link($url, _("Help"), 'helplink', 'hordehelpwin', 'popup(this.href); return false;') .
+ Horde::img('help.png', _("Help"), 'width="16" height="16"', $GLOBALS['registry']->getImageDir('horde')) . '</a>';
+ }
+
+ /**
+ * Looks up the requested entry in the XML help buffer.
+ *
+ * @param string $entry String containing the entry ID.
+ */
+ public function lookup($entry)
+ {
+ $this->_output = '';
+ $this->_reqEntry = Horde_String::upper($entry);
+ $this->_init();
+ xml_parse($this->_parser, $this->_buffer, true);
+ }
+
+ /**
+ * Returns a hash of all of the topics in this help buffer
+ * containing the keyword specified.
+ *
+ * @return array Hash of all of the search results.
+ */
+ public function search($keyword)
+ {
+ $this->_init();
+ $this->_keyword = $keyword;
+ xml_parse($this->_parser, $this->_buffer, true);
+
+ return $this->_search;
+ }
+
+ /**
+ * Returns a hash of all of the topics in this help buffer.
+ *
+ * @return array Hash of all of the topics in this buffer.
+ */
+ public function topics()
+ {
+ $this->_init();
+ xml_parse($this->_parser, $this->_buffer, true);
+
+ return $this->_entries;
+ }
+
+ /**
+ * Display the contents of the formatted output buffer.
+ */
+ public function display()
+ {
+ echo $this->_output;
+ }
+
+ /**
+ * Initializes the XML parser.
+ *
+ * @return boolean Returns true on success, false on failure.
+ */
+ protected function _init()
+ {
+ if (!isset($this->_parser)) {
+ if (!Horde_Util::extensionExists('xml')) {
+ Horde::fatal(PEAR::raiseError('The XML functions are not available. Rebuild PHP with --with-xml.'), __FILE__, __LINE__, false);
+ }
+
+ /* Create a new parser and set its default properties. */
+ $this->_parser = xml_parser_create();
+ xml_set_object($this->_parser, $this);
+ xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
+ xml_set_element_handler($this->_parser, '_startElement', '_endElement');
+ xml_set_character_data_handler($this->_parser, '_defaultHandler');
+ }
+
+ return ($this->_parser != 0);
+ }
+
+ /**
+ * User-defined function callback for start elements.
+ *
+ * @param object $parser Handle to the parser instance.
+ * @param string $name The name of this XML element.
+ * @param array $attrs List of this element's attributes.
+ */
+ protected function _startElement($parser, $name, $attrs)
+ {
+ /* Call the assigned handler for this element, if one is
+ * available. */
+ if (in_array($name, array_keys($this->_handlers))) {
+ call_user_func(array(&$this, $this->_handlers[$name]), true, $attrs);
+ }
+ }
+
+ /**
+ * User-defined function callback for end elements.
+ *
+ * @param object $parser Handle to the parser instance.
+ * @param string $name The name of this XML element.
+ */
+ protected function _endElement($parser, $name)
+ {
+ /* Call the assigned handler for this element, if one is available. */
+ if (in_array($name, array_keys($this->_handlers))) {
+ call_user_func(array(&$this, $this->_handlers[$name]), false);
+ }
+ }
+
+ /**
+ * User-defined function callback for character data.
+ *
+ * @param object $parser Handle to the parser instance.
+ * @param string $data String of character data.
+ */
+ protected function _defaultHandler($parser, $data)
+ {
+ $data = Horde_String::convertCharset($data, version_compare(zend_version(), '2', '<') ? $this->_charset : 'UTF-8');
+ if ($this->_inTitle) {
+ $this->_entries[$this->_curEntry] .= $data;
+ }
+
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= htmlspecialchars($data);
+ }
+
+ if ($this->_keyword) {
+ if (stristr($data, $this->_keyword) !== false) {
+ $this->_search[$this->_curEntry] = $this->_entries[$this->_curEntry];
+ }
+ }
+ }
+
+ /**
+ * XML element handler for the <help> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes (Not used).
+ */
+ protected function _helpHandler($startTag, $attrs = array())
+ {
+ $this->_inHelp = $startTag ? true : false;
+ }
+
+ /**
+ * XML element handler for the <entry> tag.
+ * Attributes: id
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _entryHandler($startTag, $attrs = array())
+ {
+ if (!$startTag) {
+ $this->_inBlock = false;
+ } else {
+ $id = Horde_String::upper($attrs['id']);
+ $this->_curEntry = $id;
+ $this->_entries[$id] = '';
+ $this->_inBlock = ($id == $this->_reqEntry);
+ }
+ }
+
+ /**
+ * XML element handler for the <title> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes (Not used).
+ */
+ protected function _titleHandler($startTag, $attrs = array())
+ {
+ $this->_inTitle = $startTag;
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<h1>' : '</h1>';
+ }
+ }
+
+ /**
+ * XML element handler for the <heading> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes (Not used).
+ */
+ protected function _headingHandler($startTag, $attrs = array())
+ {
+ $this->_inHeading = $startTag;
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<h2>' : '</h2>';
+ }
+ }
+
+ /**
+ * XML element handler for the <para> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes (Not used).
+ */
+ protected function _paraHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<p>' : '</p>';
+ }
+ }
+
+ /**
+ * XML element handler for the <ref> tag.
+ * Required attributes: ENTRY, MODULE
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _refHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ if ($startTag && isset($attrs['module']) && isset($attrs['entry'])) {
+ $url = Horde_Util::addParameter(Horde::selfUrl(),
+ array('show' => 'entry',
+ 'module' => $attrs['module'],
+ 'topic' => $attrs['entry']));
+ $this->_output .= Horde::link($url);
+ } else {
+ $this->_output .= '</a>';
+ }
+ }
+ }
+
+ /**
+ * XML element handler for the <eref> tag.
+ * Required elements: URL
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _erefHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ if ($startTag) {
+ $this->_output .= Horde::link($attrs['url'], null, '', '_blank');
+ } else {
+ $this->_output .= '</a>';
+ }
+ }
+ }
+
+ /**
+ * XML element handler for the <href> tag.
+ * Required elements: url, app.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _hrefHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ if ($startTag) {
+ $url = Horde::url($GLOBALS['registry']->get('webroot', $attrs['app']) . '/' . $attrs['url']);
+ $this->_output .= Horde::link($url, null, '', '_blank');
+ } else {
+ $this->_output .= '</a>';
+ }
+ }
+ }
+
+ /**
+ * XML element handler for the <b> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes (Not used).
+ */
+ protected function _bHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<strong>' : '</strong>';
+ }
+ }
+
+ /**
+ * XML element handler for the <i> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _iHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<em>' : '</em>';
+ }
+ }
+
+ /**
+ * XML element handler for the <pre> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _preHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<pre>' : '</pre>';
+ }
+ }
+
+ /**
+ * XML element handler for the <tip> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _tipHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<em class="helpTip">' : '</em>';
+ }
+ }
+
+ /**
+ * XML element handler for the <warn> tag.
+ *
+ * @param boolean $startTag Boolean indicating whether this instance is a
+ * start tag.
+ * @param array $attrs Additional element attributes.
+ */
+ protected function _warnHandler($startTag, $attrs = array())
+ {
+ if ($this->_inHelp && $this->_inBlock) {
+ $this->_output .= $startTag ? '<em class="helpWarn">' : '</em>';
+ }
+ }
+
+}
+++ /dev/null
-<?php
-/**
- * The Horde:: class provides the functionality shared by all Horde
- * applications.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jon Parise <jon@horde.org>
- * @package Core
- */
-class Horde
-{
- /**
- * Log instance.
- *
- * @var Log
- */
- static protected $_logger;
-
- /**
- * Has compression been started?
- *
- * @var boolean
- */
- static protected $_compressStart = false;
-
- /**
- * The access keys already used in this page.
- *
- * @var array
- */
- static protected $_used = array();
-
- /**
- * The labels already used in this page.
- *
- * @var array
- */
- static protected $_labels = array();
-
- /**
- * Are accesskeys supported on this system.
- *
- * @var boolean
- */
- static protected $_noAccessKey;
-
- /**
- * Whether the hook has already been loaded.
- *
- * @var array
- */
- static protected $_hooksLoaded = array();
-
- /**
- * Logs a message to the global Horde log backend.
- *
- * @param mixed $message Either a string or an object with a
- * getMessage() method (e.g. PEAR_Error,
- * Exception).
- * @param string $file What file was the log function called from
- * (e.g. __FILE__)?
- * @param integer $line What line was the log function called from
- * (e.g. __LINE__)?
- * @param integer $priority The priority of the message. One of:
- * <pre>
- * PEAR_LOG_EMERG
- * PEAR_LOG_ALERT
- * PEAR_LOG_CRIT
- * PEAR_LOG_ERR
- * PEAR_LOG_WARNING
- * PEAR_LOG_NOTICE
- * PEAR_LOG_INFO
- * PEAR_LOG_DEBUG
- * </pre>
- */
- static public function logMessage($message, $file, $line,
- $priority = PEAR_LOG_INFO)
- {
- $logger = &self::getLogger();
- if ($logger === false) {
- return;
- }
-
- if ($priority > $GLOBALS['conf']['log']['priority']) {
- return;
- }
-
- if ($message instanceof PEAR_Error) {
- $userinfo = $message->getUserInfo();
- $message = $message->getMessage();
- if (!empty($userinfo)) {
- if (is_array($userinfo)) {
- $old_error = error_reporting(0);
- $userinfo = implode(', ', $userinfo);
- error_reporting($old_error);
- }
- $message .= ': ' . $userinfo;
- }
- } elseif (is_object($message) &&
- is_callable(array($message, 'getMessage'))) {
- $message = $message->getMessage();
- }
-
- $app = isset($GLOBALS['registry']) ? $GLOBALS['registry']->getApp() : 'horde';
- $message = '[' . $app . '] ' . $message . ' [pid ' . getmypid() . ' on line ' . $line . ' of "' . $file . '"]';
-
- /* Make sure to log in the system's locale. */
- $locale = setlocale(LC_TIME, 0);
- setlocale(LC_TIME, 'C');
-
- $logger->log($message, $priority);
-
- /* Restore original locale. */
- setlocale(LC_TIME, $locale);
-
- return true;
- }
-
- /**
- * Get an instantiated instance of the configured logger, if enabled.
- * getLogger() will fatally exit if a Log object can not be
- * instantiated.
- *
- * @return mixed Log object on success, false if disabled.
- */
- static public function getLogger()
- {
- global $conf;
-
- if (empty($conf['log']['enabled'])) {
- $ret = false;
- return $ret;
- }
-
- if (isset(self::$_logger)) {
- return self::$_logger;
- }
-
- // Try to make sure that we can log messages somehow.
- if (empty($conf['log']) ||
- empty($conf['log']['type']) ||
- empty($conf['log']['name']) ||
- empty($conf['log']['ident']) ||
- !isset($conf['log']['params'])) {
- self::fatal(new Horde_Exception('Horde is not correctly configured to log error messages. You must configure at least a text file log in horde/config/conf.php.'), __FILE__, __LINE__, false);
- }
-
- self::$_logger = Log::singleton($conf['log']['type'],
- $conf['log']['name'],
- $conf['log']['ident'],
- $conf['log']['params']);
- if (!is_a(self::$_logger, 'Log')) {
- self::fatal(new Horde_Exception('An error has occurred. Furthermore, Horde encountered an error attempting to log this error. Please check your Horde logging configuration in horde/config/conf.php.'), __FILE__, __LINE__, false);
- }
-
- return self::$_logger;
- }
-
- /**
- * Destroys any existing session on login and make sure to use a new
- * session ID, to avoid session fixation issues. Should be called before
- * checking a login.
- */
- static public function getCleanSession()
- {
- // Make sure to force a completely new session ID and clear all
- // session data.
- session_regenerate_id(true);
- session_unset();
-
- /* Reset cookie timeouts, if necessary. */
- if (!empty($GLOBALS['conf']['session']['timeout'])) {
- $app = $GLOBALS['registry']->getApp();
- if (Horde_Secret::clearKey($app)) {
- Horde_Secret::setKey($app);
- }
- Horde_Secret::setKey('auth');
- }
- }
-
- /**
- * Aborts with a fatal error, displaying debug information to the user.
- *
- * @param mixed $error Either a string or an object with a getMessage()
- * method (e.g. PEAR_Error, Exception).
- * @todo Better Exception handling
- * @param integer $file The file in which the error occured.
- * @param integer $line The line on which the error occured.
- * @param boolean $log Log this message via logMessage()?
- */
- static public function fatal($error, $file = null, $line = null,
- $log = true)
- {
- $admin = Horde_Auth::isAdmin();
- $cli = Horde_Cli::runningFromCLI();
-
- $errortext = '<h1>' . _("A fatal error has occurred") . '</h1>';
- if ($error instanceof PEAR_Error) {
- $info = array_merge(array('file' => 'conf.php', 'variable' => '$conf'),
- array($error->getUserInfo()));
-
- switch ($error->getCode()) {
- case Horde_Util::HORDE_ERROR_DRIVER_CONFIG_MISSING:
- $message = sprintf(_("No configuration information specified for %s."), $info['name']) . '<br />' .
- sprintf(_("The file %s should contain some %s settings."),
- $GLOBALS['registry']->get('fileroot') . '/config/' . $info['file'],
- sprintf("%s['%s']['params']", $info['variable'], $info['driver']));
- break;
-
- case Horde_Util::HORDE_ERROR_DRIVER_CONFIG:
- $message = sprintf(_("Required \"%s\" not specified in %s configuration."), $info['field'], $info['name']) . '<br />' .
- sprintf(_("The file %s should contain a %s setting."),
- $GLOBALS['registry']->get('fileroot') . '/config/' . $info['file'],
- sprintf("%s['%s']['params']['%s']", $info['variable'], $info['driver'], $info['field']));
- break;
-
- default:
- $message = $error->getMessage();
- break;
- }
-
- $errortext .= '<h3>' . htmlspecialchars($message) . '</h3>';
- } elseif (is_object($error) && method_exists($error, 'getMessage')) {
- $errortext .= '<h3>' . htmlspecialchars($error->getMessage()) . '</h3>';
- } elseif (is_string($error)) {
- $errortext .= '<h3>' . htmlspecialchars($error) . '</h3>';
- }
-
- if (is_null($file) && $error instanceof Exception) {
- $file = $error->getFile();
- }
- if (is_null($line) && $error instanceof Exception) {
- $line = $error->getLine();
- }
-
- if ($admin) {
- $errortext .= '<p><code>' . sprintf(_("[line %d of %s]"), $line, $file) . '</code></p>';
- if (is_object($error)) {
- $errortext .= '<h3>' . _("Details:") . '</h3>';
- $errortext .= '<h4>' . _("The full error message is logged in Horde's log file, and is shown below only to administrators. Non-administrative users will not see error details.") . '</h4>';
- if (extension_loaded('xdebug')) {
- $errortext .= '<br />' . print_r($error, true);
- } else {
- $errortext .= '<p><pre>' . htmlspecialchars(print_r($error, true)) . '</pre></p>';
- }
- }
- } elseif ($log) {
- $errortext .= '<h3>' . _("Details have been logged for the administrator.") . '</h3>';
- }
-
- // Log the error via logMessage() if requested.
- if ($log) {
- self::logMessage($error, $file, $line, PEAR_LOG_EMERG);
- }
-
- if ($cli) {
- echo strip_tags(str_replace(array('<br />', '<p>', '</p>', '<h1>', '</h1>', '<h3>', '</h3>'), "\n", $errortext));
- } else {
- echo <<< HTML
-<html>
-<head><title>Horde :: Fatal Error</title></head>
-<body style="background:#fff; color:#000">$errortext</body>
-</html>
-HTML;
- }
- exit(1);
- }
-
- /**
- * Adds the javascript code to the output (if output has already started)
- * or to the list of script files to include via includeScriptFiles().
- *
- * @param string $file The full javascript file name.
- * @param string $app The application name. Defaults to the current
- * application.
- * @param boolean $direct Include the file directly without passing it
- * through javascript.php
- * @param boolean $full Output a full URL
- */
- static public function addScriptFile($file, $app = null, $direct = false,
- $full = false)
- {
- $hsf = &Horde_Script_Files::singleton();
- $hsf->add($file, $app, $direct, $full);
- }
-
- /**
- * Includes javascript files that were needed before any headers were sent.
- */
- static public function includeScriptFiles()
- {
- $hsf = &Horde_Script_Files::singleton();
- $hsf->includeFiles();
- }
-
- /**
- * Provide a list of script files to be included in the current page.
- *
- * @var array
- */
- static public function listScriptFiles()
- {
- $hsf = &Horde_Script_Files::singleton();
- return $hsf->listFiles();
- }
-
- /**
- * Disable auto-loading of the horde.js script.
- * Needs to auto-load by default for BC.
- *
- * @todo Remove for Horde 4
- */
- static public function disableAutoloadHordeJS()
- {
- $hsf = &Horde_Script_Files::singleton();
- $hsf->disableAutoloadHordeJS();
- }
-
- /**
- * Get a token for protecting a form.
- *
- * @param string $slug Slug name.
- *
- * @return string Token string.
- */
- static public function getRequestToken($slug)
- {
- $token = Horde_Token::generateId($slug);
- $_SESSION['horde_form_secrets'][$token] = time();
- return $token;
- }
-
- /**
- * Check if a token for a form is valid.
- *
- * @param string $slug Slug name.
- * @param string $token Token to check.
- *
- * @throws Horde_Exception
- */
- static public function checkRequestToken($slug, $token)
- {
- if (empty($_SESSION['horde_form_secrets'][$token])) {
- throw new Horde_Exception(_("We cannot verify that this request was really sent by you. It could be a malicious request. If you intended to perform this action, you can retry it now."));
- }
-
- if (($_SESSION['horde_form_secrets'][$token] + $GLOBALS['conf']['urls']['token_lifetime'] * 60) < time()) {
- throw new Horde_Exception(sprintf(_("This request cannot be completed because the link you followed or the form you submitted was only valid for %s minutes. Please try again now."), $GLOBALS['conf']['urls']['token_lifetime']));
- }
-
- return true;
- }
-
- /**
- * Add a signature + timestamp to a query string and return the signed query
- * string.
- *
- * @param string $queryString The query string to sign.
- * @param integer $now The timestamp at which to sign. Leave blank
- * for generating signatures; specify when
- * testing.
- *
- * @return string The signed query string.
- */
- static public function signQueryString($queryString, $now = null)
- {
- if (!isset($GLOBALS['conf']['secret_key'])) {
- return $queryString;
- }
-
- if (is_null($now)) {
- $now = time();
- }
-
- $queryString .= '&_t=' . $now . '&_h=';
-
- return $queryString . Horde_Util::uriB64Encode(hash_hmac('sha1', $queryString, $GLOBALS['conf']['secret_key'], true));
- }
-
- /**
- * Verify a signature and timestamp on a query string.
- *
- * @param string $data The signed query string.
- * @param integer $now The current time (can override for testing).
- *
- * @return boolean Whether or not the string was valid.
- */
- static public function verifySignedQueryString($data, $now = null)
- {
- if (is_null($now)) {
- $now = time();
- }
-
- $pos = strrpos($data, '&_h=');
- if ($pos === false) {
- return false;
- }
- $pos += 4;
-
- $queryString = substr($data, 0, $pos);
- $hmac = substr($data, $pos);
-
- if ($hmac != Horde_Util::uriB64Encode(hash_hmac('sha1', $queryString, $GLOBALS['conf']['secret_key'], true))) {
- return false;
- }
-
- // String was not tampered with; now validate timestamp
- parse_str($queryString, $values);
-
- return !($values['_t'] + $GLOBALS['conf']['urls']['hmac_lifetime'] * 60 < $now);
- }
-
- /**
- * Checks if link should be shown and return the necessary code.
- *
- * @param string $type Type of link to display
- * @param string $app The name of the current Horde application.
- * @param boolean $override Override Horde settings?
- * @param boolean $referrer Include the current page as the referrer
- * (url=)?
- *
- * @return string The HTML to create the link.
- */
- static public function getServiceLink($type, $app, $override = false,
- $referrer = true)
- {
- if (!self::showService($type, $override)) {
- return false;
- }
-
- switch ($type) {
- case 'help':
- if ($GLOBALS['browser']->hasFeature('javascript')) {
- self::addScriptFile('popup.js', 'horde', true);
- }
- $url = self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/help/', true);
- return Horde_Util::addParameter($url, 'module', $app);
-
- case 'problem':
- return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/problem.php?return_url=' . urlencode(self::selfUrl(true, true, true)));
-
- case 'logout':
- return self::url(Horde_Auth::addLogoutParameters($GLOBALS['registry']->get('webroot', 'horde') . '/login.php', Horde_Auth::REASON_LOGOUT));
-
- case 'login':
- return Horde_Auth::getLoginScreen('', $referrer ? self::selfUrl(true) : null);
-
- case 'options':
- global $conf;
- if (($conf['prefs']['driver'] != '') && ($conf['prefs']['driver'] != 'none')) {
- return self::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/prefs.php?app=' . $app);
- }
- break;
- }
-
- return false;
- }
-
- /**
- * Returns a stdClass response object with added notification information.
- *
- * @param string $data The 'response' data.
- * @param Notification_Listener $listener If set, adds notification
- * information to object.
- * @param boolean $auto If true, the ajax application
- * will automatically display the
- * notification. If false, the
- * callback handler is responsible
- * for displaying the notification.
- */
- static public function prepareResponse($data = null, $listener = null,
- $auto = true)
- {
- $response = new stdClass();
- $response->response = $data;
- if ($listener) {
- $GLOBALS['notification']->notify(array('listeners' => 'status'));
- $stack = $listener->getStack();
- if (!empty($stack)) {
- $response->msgs = $stack;
- if (!(bool)$auto) {
- $response->msgs_noauto = true;
- }
- }
- }
-
- return $response;
- }
-
- /**
- * Send response data to browser.
- *
- * @param mixed $data The data to serialize and send to the browser.
- * @param string $ct The content-type to send the data with. Either
- * 'json', 'js-json', 'html', 'plain', and 'xml'.
- */
- static public function sendHTTPResponse($data, $ct)
- {
- $charset = Horde_Nls::getCharset();
-
- // Output headers and encoded response.
- switch ($ct) {
- case 'json':
- case 'js-json':
- /* JSON responses are a structured object which always
- * includes the response in a member named 'response', and an
- * additional array of messages in 'msgs' which may be updates
- * for the server or notification messages.
- *
- * Make sure no null bytes sneak into the JSON output stream.
- * Null bytes cause IE to stop reading from the input stream,
- * causing malformed JSON data and a failed request. These
- * bytes don't seem to break any other browser, but might as
- * well remove them anyway.
- *
- * Finally, add prototypejs security delimiters to returned
- * JSON. */
- $s_data = '/*-secure-' .
- Horde_String::convertCharset(str_replace("\00", '', Horde_Serialize::serialize($data, Horde_Serialize::JSON, $charset)), 'UTF-8') .
- '*/';
-
- if ($ct == 'json') {
- header('Content-Type: application/json');
- echo $s_data;
- } else {
- header('Content-Type: text/html; charset=' . $charset);
- echo htmlspecialchars($s_data);
- }
- break;
-
- case 'html':
- case 'plain':
- case 'xml':
- header('Content-Type: text/' . $ct . '; charset=' . $charset);
- echo $data;
- break;
-
- default:
- echo $data;
- }
-
- exit;
- }
-
- /**
- * Is the current HTTP connection considered secure?
- * @TODO Move this to the request classes!
- *
- * @return boolean
- */
- static public function isConnectionSecure()
- {
- if ($GLOBALS['browser']->usingSSLConnection()) {
- return true;
- }
-
- if (!empty($GLOBALS['conf']['safe_ips'])) {
- if (reset($GLOBALS['conf']['safe_ips']) == '*') {
- return true;
- }
-
- /* $_SERVER['HTTP_X_FORWARDED_FOR'] is user data and not
- * reliable. We don't consult it for safe IPs. We also have to
- * assume that if it is present, the user is coming through a proxy
- * server. If so, we don't count any non-SSL connection as safe, no
- * matter the source IP. */
- if (!isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
- $remote_addr = $_SERVER['REMOTE_ADDR'];
- foreach ($GLOBALS['conf']['safe_ips'] as $safe_ip) {
- $safe_ip = preg_replace('/(\.0)*$/', '', $safe_ip);
- if (strpos($remote_addr, $safe_ip) === 0) {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * Throws an exception if not using a secure connection.
- *
- * @throws Horde_Exception
- */
- static public function requireSecureConnection()
- {
- if (!self::isConnectionSecure()) {
- throw new Horde_Exception(_("The encryption features require a secure web connection."));
- }
- }
-
- /**
- * TODO
- *
- * @param string $type The type of link.
- * @param boolean $override Override Horde settings?
- *
- * @return boolean True if the link is to be shown.
- */
- static public function showService($type, $override = false)
- {
- global $conf;
-
- if (empty($conf['menu']['links'][$type])) {
- return false;
- }
-
- switch ($conf['menu']['links'][$type]) {
- case 'all':
- return true;
-
- case 'never':
- return $override;
-
- case 'authenticated':
- return $override || (bool)Horde_Auth::getAuth();
-
- default:
- return $override;
- }
- }
-
- /**
- * Loads global and vhost specific configuration files.
- *
- * @param string $config_file The name of the configuration file.
- * @param string|array $var_names The name(s) of the variable(s) that
- * is/are defined in the configuration
- * file.
- * @param string $app The application. Defaults to the current
- * application.
- * @param boolean $show_output If true, the contents of the requested
- * config file are simply output instead of
- * loaded into a variable.
- *
- * @return mixed The value of $var_names, in a compact()'ed array if
- * $var_names is an array.
- * @throws Horde_Exception
- */
- static public function loadConfiguration($config_file, $var_names = null,
- $app = null, $show_output = false)
- {
- global $registry;
-
- if (is_null($app)) {
- $app = $registry->getApp();
- }
-
- // Track if we've included some version (main or vhosted) of
- // the config file.
- $was_included = false;
-
- // Load global configuration file.
- $config_dir = (($app == 'horde') && defined('HORDE_BASE'))
- ? HORDE_BASE . '/config/'
- : $registry->get('fileroot', $app) . '/config/';
- $file = $config_dir . $config_file;
-
- if (file_exists($file)) {
- /* If we are not exporting variables located in the configuration
- * file, or we are not capturing the output, then there is no
- * need to load the configuration file more than once. */
- ob_start();
- $success = (is_null($var_names) && !$show_output)
- ? include_once $file
- : include $file;
- $output = ob_get_clean();
-
- if (!empty($output) && !$show_output) {
- throw new Horde_Exception(sprintf('Failed to import configuration file "%s": ', $file) . strip_tags($output));
- }
-
- if (!$success) {
- throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $file));
- }
-
- $was_included = true;
- }
-
- // Load vhost configuration file.
- if (!empty($conf['vhosts']) || !empty($GLOBALS['conf']['vhosts'])) {
- $server_name = isset($GLOBALS['conf'])
- ? $GLOBALS['conf']['server']['name']
- : $conf['server']['name'];
- $file = $config_dir . substr($config_file, 0, -4) . '-' . $server_name . '.php';
-
- if (file_exists($file)) {
- ob_start();
- $success = (is_null($var_names) && !$show_output)
- ? include_once $file
- : include $file;
- $output = ob_get_clean();
-
- if (!empty($output) && !$show_output) {
- throw new Horde_Exception(sprintf('Failed to import configuration file "%s": ', $file) . strip_tags($output));
- }
-
- if (!$success) {
- throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $file));
- }
-
- $was_included = true;
- }
- }
-
- // Return an error if neither main or vhosted versions of the config
- // file exist.
- if (!$was_included) {
- throw new Horde_Exception(sprintf('Failed to import configuration file "%s".', $config_dir . $config_file));
- }
-
- if (isset($output) && $show_output) {
- echo $output;
- }
-
- if (is_null($var_names)) {
- return;
- } elseif (is_array($var_names)) {
- return compact($var_names);
- } elseif (isset($$var_names)) {
- return $$var_names;
- } else {
- return array();
- }
- }
-
- /**
- * Returns the driver parameters for the specified backend.
- *
- * @param mixed $backend The backend system (e.g. 'prefs', 'categories',
- * 'contacts') being used.
- * The used configuration array will be
- * $conf[$backend]. If an array gets passed, it will
- * be $conf[$key1][$key2].
- * @param string $type The type of driver.
- *
- * @return array The connection parameters.
- */
- static public function getDriverConfig($backend, $type = 'sql')
- {
- global $conf;
-
- $c = null;
- if (is_array($backend)) {
- $c = Horde_Array::getElement($conf, $backend);
- } elseif (isset($conf[$backend])) {
- $c = $conf[$backend];
- }
-
- if (!is_null($c) && isset($c['params'])) {
- $c['params']['umask'] = $conf['umask'];
- if (isset($conf[$type])) {
- return array_merge($conf[$type], $c['params']);
- } else {
- return $c['params'];
- }
- }
-
- return isset($conf[$type]) ? $conf[$type] : array();
- }
-
-
- /**
- * Returns the VFS driver parameters for the specified backend.
- *
- * @param string $name The VFS system name (e.g. 'images', 'documents')
- * being used.
- *
- * @return array A hash with the VFS parameters; the VFS driver in 'type'
- * and the connection parameters in 'params'.
- * @throws Horde_Exception
- */
- static public function getVFSConfig($name)
- {
- global $conf;
-
- if (!isset($conf[$name]['type'])) {
- throw new Horde_Exception(_("You must configure a VFS backend."));
- }
-
- $vfs = ($conf[$name]['type'] == 'horde')
- ? $conf['vfs']
- : $conf[$name];
-
- if ($vfs['type'] == 'sql') {
- $vfs['params'] = self::getDriverConfig($name, 'sql');
- }
-
- return $vfs;
- }
-
- /**
- * Return the driver and parameters for the current mailer configuration.
- *
- * @return array Array with driver name and parameter hash.
- */
- static public function getMailerConfig()
- {
- $mail_driver = $GLOBALS['conf']['mailer']['type'];
- $mail_params = $GLOBALS['conf']['mailer']['params'];
- if ($mail_driver == 'smtp' && $mail_params['auth'] &&
- empty($mail_params['username'])) {
- $mail_params['username'] = Horde_Auth::getAuth();
- $mail_params['password'] = Horde_Auth::getCredential('password');
- }
-
- return array($mail_driver, $mail_params);
- }
-
- /**
- * Checks if all necessary parameters for a driver configuration
- * are set and throws a fatal error with a detailed explanation
- * how to fix this, if something is missing.
- *
- * @param array $params The configuration array with all parameters.
- * @param string $driver The key name (in the configuration array) of
- * the driver.
- * @param array $fields An array with mandatory parameter names for
- * this driver.
- * @param string $name The clear text name of the driver. If not
- * specified, the application name will be used.
- * @param string $file The configuration file that should contain
- * these settings.
- * @param string $variable The name of the configuration variable.
- */
- static public function assertDriverConfig($params, $driver, $fields,
- $name = null,
- $file = 'conf.php',
- $variable = '$conf')
- {
- global $registry;
-
- // Don't generate a fatal error if we fail during or before
- // Registry instantiation.
- if (is_null($name)) {
- $name = isset($registry) ? $registry->getApp() : '[unknown]';
- }
- $fileroot = isset($registry) ? $registry->get('fileroot') : '';
-
- if (!is_array($params) || !count($params)) {
- self::fatal(new Horde_Exception(
- sprintf(_("No configuration information specified for %s."), $name) . "\n\n" .
- sprintf(_("The file %s should contain some %s settings."),
- $fileroot . '/config/' . $file,
- sprintf("%s['%s']['params']", $variable, $driver))),
- __FILE__, __LINE__);
- }
-
- foreach ($fields as $field) {
- if (!isset($params[$field])) {
- self::fatal(new Horde_Exception(
- sprintf(_("Required \"%s\" not specified in %s configuration."), $field, $name) . "\n\n" .
- sprintf(_("The file %s should contain a %s setting."),
- $fileroot . '/config/' . $file,
- sprintf("%s['%s']['params']['%s']", $variable, $driver, $field))),
- __FILE__, __LINE__);
- }
- }
- }
-
- /**
- * Returns a session-id-ified version of $uri.
- * If a full URL is requested, all parameter separators get converted to
- * "&", otherwise to "&".
- *
- * @param string $uri The URI to be modified.
- * @param boolean $full Generate a full (http://server/path/)
- * URL.
- * @param integer $append_session 0 = only if needed, 1 = always, -1 =
- * never.
- * @param boolean $force_ssl Ignore $conf['use_ssl'] and force
- * creation of a SSL URL?
- *
- * @return string The URL with the session id appended (if needed).
- */
- static public function url($uri, $full = false, $append_session = 0,
- $force_ssl = false)
- {
- if ($force_ssl) {
- $full = true;
- }
-
- if ($full) {
- global $conf, $registry, $browser;
-
- /* Store connection parameters in local variables. */
- $server_name = $conf['server']['name'];
- $server_port = $conf['server']['port'];
-
- $protocol = 'http';
- if ($conf['use_ssl'] == 1) {
- $protocol = 'https';
- } elseif ($conf['use_ssl'] == 2 &&
- $browser->usingSSLConnection()) {
- $protocol = 'https';
- } elseif ($conf['use_ssl'] == 3) {
- $server_port = '';
- if ($force_ssl) {
- $protocol = 'https';
- }
- }
-
- /* If using non-standard ports, add the port to the URL. */
- if (!empty($server_port) &&
- ((($protocol == 'http') && ($server_port != 80)) ||
- (($protocol == 'https') && ($server_port != 443)))) {
- $server_name .= ':' . $server_port;
- }
-
- /* Store the webroot in a local variable. */
- $webroot = $registry->get('webroot');
-
- $url = $protocol . '://' . $server_name;
- if (preg_match('|^([\w+-]{1,20})://|', $webroot)) {
- /* Don't prepend to webroot if it's already absolute. */
- $url = '';
- }
-
- if (substr($uri, 0, 1) != '/') {
- /* Simple case for http:// absolute webroots. */
- if (preg_match('|^([\w+-]{1,20})://|', $uri)) {
- $url = $uri;
- } elseif (substr($webroot, -1) == '/') {
- $url .= $webroot . $uri;
- } else {
- $url .= $webroot . '/' . $uri;
- }
- } else {
- $url .= $uri;
- }
- } else {
- $url = $uri;
-
- if (!empty($_SERVER['HTTP_HOST'])) {
- // Don't generate absolute URLs if we don't have to.
- if (preg_match('|^([\w+-]{1,20}://' . preg_quote($_SERVER['HTTP_HOST'], '|') . ')/|', $url, $matches)) {
- $url = substr($url, strlen($matches[1]));
- }
- }
- }
-
- if (empty($GLOBALS['conf']['session']['use_only_cookies']) &&
- (($append_session == 1) ||
- (($append_session == 0) &&
- !isset($_COOKIE[session_name()])))) {
- $url = Horde_Util::addParameter($url, session_name(), session_id());
- }
-
- if ($full) {
- /* We need to run the replace twice, because we only catch every
- * second match. */
- return preg_replace(array('/(=?.*?)&(.*?=)/',
- '/(=?.*?)&(.*?=)/'),
- '$1&$2', $url);
- } elseif (preg_match('/=.*&.*=/', $url)) {
- return $url;
- } else {
- return htmlentities($url);
- }
- }
-
- /**
- * Returns a session-id-ified version of $uri, using the current
- * application's webroot setting.
- *
- * @param string $uri The URI to be modified.
- * @param boolean $full Generate a full (http://server/path/)
- * URL.
- * @param integer $append_session 0 = only if needed, 1 = always, -1 =
- * never.
- *
- * @return string The url with the session id appended.
- */
- static public function applicationUrl($uri, $full = false,
- $append_session = 0)
- {
- if ($full) {
- return self::url($uri, $full, $append_session);
- }
-
- if (substr($uri, 0, 1) != '/') {
- $webroot = $GLOBALS['registry']->get('webroot');
- if (substr($webroot, -1) != '/') {
- $webroot .= '/';
- }
- $uri = $webroot . $uri;
- }
-
- return self::url($uri, $full, $append_session);
- }
-
- /**
- * Returns an external link passed through the dereferrer to strip session
- * IDs from the referrer.
- *
- * @param string $url The external URL to link to.
- * @param boolean $tag If true, a complete <a> tag is returned, only the
- * url otherwise.
- *
- * @return string The link to the dereferrer script.
- */
- static public function externalUrl($url, $tag = false)
- {
- if (!isset($_GET[session_name()]) ||
- Horde_String::substr($url, 0, 1) == '#' ||
- Horde_String::substr($url, 0, 7) == 'mailto:') {
- $ext = $url;
- } else {
- $ext = self::url($GLOBALS['registry']->get('webroot', 'horde') .
- '/services/go.php', true, -1);
-
- /* We must make sure there are no &'s in the URL. */
- $url = preg_replace(array('/(=?.*?)&(.*?=)/', '/(=?.*?)&(.*?=)/'), '$1&$2', $url);
- $ext .= '?' . self::signQueryString('url=' . urlencode($url));
- }
-
- if ($tag) {
- $ext = self::link($ext, $url, '', '_blank');
- }
-
- return $ext;
- }
-
- /**
- * Returns a URL to be used for downloading, that takes into account any
- * special browser quirks (i.e. IE's broken filename handling).
- *
- * @param string $filename The filename of the download data.
- * @param array $params Any additional parameters needed.
- * @param string $url The URL to alter. If none passed in, will use
- * the file 'view.php' located in the current
- * module's base directory.
- *
- * @return string The download URL.
- */
- static public function downloadUrl($filename, $params = array(), $url = null)
- {
- global $browser, $registry;
-
- $horde_url = false;
-
- if (is_null($url)) {
- $url = Horde_Util::addParameter(self::url($registry->get('webroot', 'horde') . '/services/download/'), 'module', $registry->getApp());
- $horde_url = true;
- }
-
- /* Add parameters. */
- if (!is_null($params)) {
- $url = Horde_Util::addParameter($url, $params);
- }
-
- /* If we are using the default Horde download link, add the
- * filename to the end of the URL. Although not necessary for
- * many browsers, this should allow every browser to download
- * correctly. */
- if ($horde_url) {
- $url = Horde_Util::addParameter($url, 'fn', '/' . rawurlencode($filename));
- } elseif ($browser->hasQuirk('break_disposition_filename')) {
- /* Some browsers will only obtain the filename correctly
- * if the extension is the last argument in the query
- * string and rest of the filename appears in the
- * PATH_INFO element. */
- $filename = rawurlencode($filename);
-
- /* Get the webserver ID. */
- $server = self::webServerID();
-
- /* Get the name and extension of the file. Apache 2 does
- * NOT support PATH_INFO information being passed to the
- * PHP module by default, so disable that
- * functionality. */
- if (($server != 'apache2')) {
- if (($pos = strrpos($filename, '.'))) {
- $name = '/' . preg_replace('/\./', '%2E', substr($filename, 0, $pos));
- $ext = substr($filename, $pos);
- } else {
- $name = '/' . $filename;
- $ext = '';
- }
-
- /* Enter the PATH_INFO information. */
- if (($pos = strpos($url, '?'))) {
- $url = substr($url, 0, $pos) . $name . substr($url, $pos);
- } else {
- $url .= $name;
- }
- }
-
- /* Append the extension, if it exists. */
- if (($server == 'apache2') || !empty($ext)) {
- $url = Horde_Util::addParameter($url, 'fn_ext', '/' . $filename);
- }
- }
-
- return $url;
- }
-
- /**
- * Returns an anchor tag with the relevant parameters
- *
- * @param string $url The full URL to be linked to.
- * @param string $title The link title/description.
- * @param string $class The CSS class of the link.
- * @param string $target The window target to point to.
- * @param string $onclick JavaScript action for the 'onclick' event.
- * @param string $title2 The link title (tooltip) (deprecated - just
- * use $title).
- * @param string $accesskey The access key to use.
- * @param array $attributes Any other name/value pairs to add to the <a>
- * tag.
- * @param boolean $escape Whether to escape special characters in the
- * title attribute.
- *
- * @return string The full <a href> tag.
- */
- static public function link($url = '', $title = '', $class = '',
- $target = '', $onclick = '', $title2 = '',
- $accesskey = '', $attributes = array(),
- $escape = true)
- {
- if (!empty($title2)) {
- $title = $title2;
- }
-
- $ret = '<a';
- if (!empty($url)) {
- $ret .= " href=\"$url\"";
- }
- if (!empty($onclick)) {
- $ret .= " onclick=\"$onclick\"";
- }
- if (!empty($class)) {
- $ret .= " class=\"$class\"";
- }
- if (!empty($target)) {
- $ret .= " target=\"$target\"";
- }
- if (!empty($title)) {
- if ($escape) {
- $charset = Horde_Nls::getCharset();
- $old_error = error_reporting(0);
- $title = str_replace(
- array("\r", "\n"), '',
- htmlspecialchars(
- nl2br(htmlspecialchars($title, ENT_QUOTES, $charset)),
- ENT_QUOTES, $charset));
- error_reporting($old_error);
- }
- $ret .= ' title="' . $title . '"';
- }
- if (!empty($accesskey)) {
- $ret .= ' accesskey="' . htmlspecialchars($accesskey) . '"';
- }
-
- foreach ($attributes as $name => $value) {
- $ret .= ' ' . htmlspecialchars($name) . '="'
- . htmlspecialchars($value) . '"';
- }
-
- return "$ret>";
- }
-
- /**
- * Uses DOM Tooltips to display the 'title' attribute for
- * link() calls.
- *
- * @param string $url The full URL to be linked to
- * @param string $status The JavaScript mouse-over string
- * @param string $class The CSS class of the link
- * @param string $target The window target to point to.
- * @param string $onclick JavaScript action for the 'onclick' event.
- * @param string $title The link title (tooltip).
- * @param string $accesskey The access key to use.
- * @param array $attributes Any other name/value pairs to add to the <a>
- * tag.
- *
- * @return string The full <a href> tag.
- */
- static public function linkTooltip($url, $status = '', $class = '',
- $target = '', $onclick = '',
- $title = '', $accesskey = '',
- $attributes = array())
- {
- if (!empty($title)) {
- $charset = Horde_Nls::getCharset();
- $old_error = error_reporting(0);
- $title = '<pre>' . preg_replace(array('/\n/', '/((?<!<br)\s{1,}(?<!\/>))/em', '/<br \/><br \/>/', '/<br \/>/'), array('', 'str_repeat(" ", strlen("$1"))', '<br /> <br />', '<br />'), nl2br(htmlspecialchars(htmlspecialchars($title, ENT_QUOTES, $charset), ENT_QUOTES, $charset))) . '</pre>';
- error_reporting($old_error);
- }
-
- return self::link($url, $title, $class, $target, $onclick, null, $accesskey, $attributes, false);
- }
-
- /**
- * Returns an anchor sequence with the relevant parameters for a widget
- * with accesskey and text.
- *
- * @param string $url The full URL to be linked to.
- * @param string $title The link title/description.
- * @param string $class The CSS class of the link
- * @param string $target The window target to point to.
- * @param string $onclick JavaScript action for the 'onclick' event.
- * @param string $title2 The link title (tooltip) (deprecated - just use
- * $title).
- * @param boolean $nocheck Don't check if the access key already has been
- * used. Defaults to false (= check).
- *
- * @return string The full <a href>Title</a> sequence.
- */
- static public function widget($url, $title = '', $class = 'widget',
- $target = '', $onclick = '', $title2 = '',
- $nocheck = false)
- {
- if (!empty($title2)) {
- $title = $title2;
- }
-
- $ak = self::getAccessKey($title, $nocheck);
-
- return self::link($url, '', $class, $target, $onclick, '', $ak) . self::highlightAccessKey($title, $ak) . '</a>';
- }
-
- /**
- * Returns a session-id-ified version of $SCRIPT_NAME resp. $PHP_SELF.
- *
- * @param boolean $script_params Include script parameters like
- * QUERY_STRING and PATH_INFO?
- * @param boolean $nocache Include a nocache parameter in the URL?
- * @param boolean $full Return a full URL?
- * @param boolean $force_ssl Ignore $conf['use_ssl'] and force creation
- * of a SSL URL?
- *
- * @return string The requested URI.
- */
- static public function selfUrl($script_params = false, $nocache = true,
- $full = false, $force_ssl = false)
- {
- if (!strncmp(PHP_SAPI, 'cgi', 3)) {
- // When using CGI PHP, SCRIPT_NAME may contain the path to
- // the PHP binary instead of the script being run; use
- // PHP_SELF instead.
- $url = $_SERVER['PHP_SELF'];
- } else {
- $url = isset($_SERVER['SCRIPT_NAME']) ?
- $_SERVER['SCRIPT_NAME'] :
- $_SERVER['PHP_SELF'];
- }
-
- if ($script_params) {
- if ($pathInfo = Horde_Util::getPathInfo()) {
- $url .= $pathInfo;
- }
- if (!empty($_SERVER['QUERY_STRING'])) {
- $url .= '?' . $_SERVER['QUERY_STRING'];
- }
- }
-
- $url = self::url($url, $full, 0, $force_ssl);
-
- return $nocache
- ? Horde_Util::nocacheUrl($url, !$full)
- : $url;
- }
-
- /**
- * Constructs a correctly-pathed link to an image.
- *
- * @param string $src The image file.
- * @param string $alt Text describing the image.
- * @param mixed $attr Any additional attributes for the image tag. Can be
- * a pre-built string or an array of key/value pairs
- * that will be assembled and html-encoded.
- * @param string $dir The root graphics directory.
- *
- * @return string The full image tag.
- */
- static public function img($src, $alt = '', $attr = '', $dir = null)
- {
- $charset = Horde_Nls::getCharset();
-
- /* If browser does not support images, simply return the ALT text. */
- if (!$GLOBALS['browser']->hasFeature('images')) {
- $old_error = error_reporting(0);
- $res = htmlspecialchars($alt, ENT_COMPAT, $charset);
- error_reporting($old_error);
- return $res;
- }
-
- /* If no directory has been specified, get it from the registry. */
- if (is_null($dir)) {
- $dir = $GLOBALS['registry']->getImageDir();
- }
-
- /* If a directory has been provided, prepend it to the image source. */
- if (!empty($dir)) {
- $src = $dir . '/' . $src;
- }
-
- /* Build all of the tag attributes. */
- $attributes = array('alt' => $alt);
- if (is_array($attr)) {
- $attributes = array_merge($attributes, $attr);
- }
- if (empty($attributes['title'])) {
- $attributes['title'] = '';
- }
-
- $img = '<img';
- $old_error = error_reporting(0);
- foreach ($attributes as $attribute => $value) {
- $img .= ' ' . $attribute . '="' . htmlspecialchars($value, ENT_COMPAT, $charset) . '"';
- }
- error_reporting($old_error);
-
- /* If the user supplied a pre-built string of attributes, add that. */
- if (is_string($attr) && !empty($attr)) {
- $img .= ' ' . $attr;
- }
-
- /* Return the closed image tag. */
- return $img . ' src="' . self::base64ImgData($src) . '" />';
- }
-
- /**
- * Same as Horde::img(), but returns a full source url for the image.
- * Useful for when the image may be part of embedded Horde content on an
- * external site. Basically a stop-gap measure until Horde_View etc...
- *
- * @see Horde::img()
- */
- static public function fullSrcImg($src, $options = array())
- {
- $charset = Horde_Nls::getCharset();
-
- /* If browser does not support images, simply return the ALT text. */
- if (!$GLOBALS['browser']->hasFeature('images')) {
- $old_error = error_reporting(0);
- $res = htmlspecialchars($alt, ENT_COMPAT, $charset);
- error_reporting($old_error);
- return $res;
- }
-
- /* If no directory has been specified, get it from the registry. */
- $dir = empty($options['dir']) ? $GLOBALS['registry']->getImageDir() : $options['dir'];
-
- /* If we can send as data, no need to get the full path */
- $src = self::base64ImgData($dir . '/' . $src);
- if (substr($src, 0, 10) != 'data:image') {
- $src = self::url($src, true, -1);
- }
-
- /* Build all of the tag attributes. */
- $attributes = !empty($options['attr']) ? $options['attr'] : array();
-
- $img = '<img';
- $old_error = error_reporting(0);
- foreach ($attributes as $attribute => $value) {
- $img .= ' ' . $attribute . '="' . htmlspecialchars($value, ENT_COMPAT, $charset) . '"';
- }
- error_reporting($old_error);
-
- /* If the user supplied a pre-built string of attributes, add that. */
- if (!empty($options['attr']) && is_string($options['attr'])) {
- $img .= ' ' . $options['attr'];
- }
-
- /* Return the closed image tag. */
- return $img . ' src="' . $src . '" />';
- }
-
- /**
- * Generate RFC 2397-compliant image data strings.
- *
- * @param string $file Filename containing image data.
- *
- * @return string The string to use in the image 'src' attribute; either
- * the image data if the browser supports, or the filepath
- * if not.
- */
- public function base64ImgData($file)
- {
- $dataurl = $GLOBALS['browser']->hasFeature('dataurl');
- if (!$dataurl) {
- return $file;
- }
-
- /* Only encode image files if they are below the dataurl limit. */
- $filename = realpath($GLOBALS['registry']->get('fileroot', 'horde')) . preg_replace('/^' . preg_quote($GLOBALS['registry']->get('webroot', 'horde'), '/') . '/', '', $file);
-
- /* Delete approx. 50 chars from the limit to account for the various
- * data/base64 header text. Multiply by 0.75 to determine the
- * base64 encoded size. */
- return (($dataurl === true) ||
- (filesize($filename) <= (($dataurl * 0.75) - 50)))
- ? 'data:image/' . substr($file, strrpos($file, '.') + 1) . ';base64,' . base64_encode(file_get_contents($filename))
- : $file;
- }
-
- /**
- * Determines the location of the system temporary directory. If a specific
- * setting cannot be found, it defaults to /tmp.
- *
- * @return string A directory name that can be used for temp files.
- * Returns false if one could not be found.
- */
- static public function getTempDir()
- {
- global $conf;
-
- /* If one has been specifically set, then use that */
- if (!empty($conf['tmpdir'])) {
- $tmp = $conf['tmpdir'];
- }
-
- /* Next, try Horde_Util::getTempDir(). */
- if (empty($tmp)) {
- $tmp = Horde_Util::getTempDir();
- }
-
- /* If it is still empty, we have failed, so return false;
- * otherwise return the directory determined. */
- return empty($tmp)
- ? false
- : $tmp;
- }
-
- /**
- * Creates a temporary filename for the lifetime of the script, and
- * (optionally) registers it to be deleted at request shutdown.
- *
- * @param string $prefix Prefix to make the temporary name more
- * recognizable.
- * @param boolean $delete Delete the file at the end of the request?
- * @param string $dir Directory to create the temporary file in.
- * @param boolean $secure If deleting file, should we securely delete the
- * file?
- *
- * @return string Returns the full path-name to the temporary file or
- * false if a temporary file could not be created.
- */
- static public function getTempFile($prefix = 'Horde', $delete = true,
- $dir = '', $secure = false)
- {
- if (empty($dir) || !is_dir($dir)) {
- $dir = self::getTempDir();
- }
-
- return Horde_Util::getTempFile($prefix, $delete, $dir, $secure);
- }
-
- /**
- * Starts output compression, if requested.
- */
- static public function compressOutput()
- {
- if (self::$_compressStart) {
- return;
- }
-
- /* Compress output if requested and possible. */
- if ($GLOBALS['conf']['compress_pages'] &&
- !$GLOBALS['browser']->hasQuirk('buggy_compression') &&
- !(bool)ini_get('zlib.output_compression') &&
- !(bool)ini_get('zend_accelerator.compress_all') &&
- ini_get('output_handler') != 'ob_gzhandler') {
- if (ob_get_level()) {
- ob_end_clean();
- }
- ob_start('ob_gzhandler');
- }
-
- self::$_compressStart = true;
- }
-
- /**
- * Determines if output compression can be used.
- *
- * @return boolean True if output compression can be used, false if not.
- */
- static public function allowOutputCompression()
- {
- $browser = &Horde_Browser::singleton();
- return !$browser->hasQuirk('buggy_compression') &&
- (ini_get('zlib.output_compression') == '') &&
- (ini_get('zend_accelerator.compress_all') == '') &&
- (ini_get('output_handler') != 'ob_gzhandler');
- }
-
- /**
- * Returns the Web server being used.
- * PHP string list built from the PHP 'configure' script.
- *
- * @return string A web server identification string.
- * @see php_sapi_name()
- */
- static public function webServerID()
- {
- switch (PHP_SAPI) {
- case 'apache':
- return 'apache1';
-
- case 'apache2filter':
- case 'apache2handler':
- return 'apache2';
-
- default:
- return PHP_SAPI;
- }
- }
-
- /**
- * Returns the <link> tags for the CSS stylesheets.
- *
- * @param string|array $app The Horde application(s).
- * @param mixed $theme The theme to use; specify an empty value to
- * retrieve the theme from user preferences, and
- * false for no theme.
- * @param boolean $inherit Inherit Horde-wide CSS?
- *
- * @return string <link> tags for CSS stylesheets.
- */
- static public function stylesheetLink($apps = null, $theme = '',
- $inherit = true)
- {
- $css = self::getStylesheets($apps, $theme, $inherit);
-
- $html = '';
- foreach ($css as $css_link) {
- $html .= '<link href="' . $css_link['u'] . '" rel="stylesheet" type="text/css" />' . "\n";
- }
-
- return $html;
- }
-
- /**
- * Return the list of base stylesheets to display.
- *
- * @param string|array $app The Horde application(s).
- * @param mixed $theme The theme to use; specify an empty value to
- * retrieve the theme from user preferences, and
- * false for no theme.
- * @param boolean $inherit Inherit Horde-wide CSS?
- *
- * @return array
- */
- static public function getStylesheets($apps = null, $theme = '',
- $inherit = true)
- {
- if ($theme === '' && isset($GLOBALS['prefs'])) {
- $theme = $GLOBALS['prefs']->getValue('theme');
- }
-
- $css = array();
- $rtl = isset($GLOBALS['nls']['rtl'][$GLOBALS['language']]);
-
- if (!is_array($apps)) {
- $apps = is_null($apps) ? array() : array($apps);
- }
- if ($inherit) {
- $key = array_search('horde', $apps);
- if ($key !== false) {
- unset($apps[$key]);
- }
- array_unshift($apps, 'horde');
- }
-
- /* Collect browser specific stylesheets if needed. */
- $browser_css = array();
-
- switch ($GLOBALS['browser']->getBrowser()) {
- case 'msie':
- $ie_major = $GLOBALS['browser']->getMajor();
- if ($ie_major == 7) {
- $browser_css[] = 'ie7.css';
- } elseif ($ie_major < 7) {
- $browser_css[] = 'ie6_or_less.css';
- if ($GLOBALS['browser']->getPlatform() == 'mac') {
- $browser_css[] = 'ie5mac.css';
- }
- }
- break;
-
- case 'opera':
- $browser_css[] = 'opera.css';
- break;
-
- case 'mozilla':
- if ($GLOBALS['browser']->getMajor() >= 5 &&
- preg_match('/rv:(.*)\)/', $GLOBALS['browser']->getAgentString(), $revision) &&
- $revision[1] <= 1.4) {
- $browser_css[] = 'moz14.css';
- }
- break;
-
- case 'webkit':
- $browser_css[] = 'webkit.css';
- break;
- }
-
- foreach ($apps as $app) {
- $themes_fs = $GLOBALS['registry']->get('themesfs', $app);
- $themes_uri = self::url($GLOBALS['registry']->get('themesuri', $app), false, -1);
- $css[] = array('u' => $themes_uri . '/screen.css', 'f' => $themes_fs . '/screen.css');
- if (!empty($theme) &&
- file_exists($themes_fs . '/' . $theme . '/screen.css')) {
- $css[] = array('u' => $themes_uri . '/' . $theme . '/screen.css', 'f' => $themes_fs . '/' . $theme . '/screen.css');
- }
-
- if ($rtl) {
- $css[] = array('u' => $themes_uri . '/rtl.css', 'f' => $themes_fs . '/rtl.css');
- if (!empty($theme) &&
- file_exists($themes_fs . '/' . $theme . '/rtl.css')) {
- $css[] = array('u' => $themes_uri . '/' . $theme . '/rtl.css', 'f' => $themes_fs . '/' . $theme . '/rtl.css');
- }
- }
- foreach ($browser_css as $browser) {
- if (file_exists($themes_fs . '/' . $browser)) {
- $css[] = array('u' => $themes_uri . '/' . $browser, 'f' => $themes_fs . '/' . $browser);
- }
- if (!empty($theme) &&
- file_exists($themes_fs . '/' . $theme . '/' . $browser)) {
- $css[] = array('u' => $themes_uri . '/' . $theme . '/' . $browser, 'f' => $themes_fs . '/' . $theme . '/' . $browser);
- }
- }
- }
-
- return $css;
- }
-
- /**
- * Sets a custom session handler up, if there is one.
- * If the global variable 'session_cache_limiter' is defined, its value
- * will override the cache limiter setting found in the configuration
- * file.
- *
- * The custom session handler object will be contained in the global
- * 'horde_sessionhandler' variable.
- */
- static public function setupSessionHandler()
- {
- global $conf;
-
- ini_set('url_rewriter.tags', 0);
- if (!empty($conf['session']['use_only_cookies'])) {
- ini_set('session.use_only_cookies', 1);
- if (!empty($conf['cookie']['domain']) &&
- strpos($conf['server']['name'], '.') === false) {
- self::fatal('Session cookies will not work without a FQDN and with a non-empty cookie domain. Either use a fully qualified domain name like "http://www.example.com" instead of "http://example" only, or set the cookie domain in the Horde configuration to an empty value, or enable non-cookie (url-based) sessions in the Horde configuration.', __FILE__, __LINE__);
- }
- }
-
- session_set_cookie_params($conf['session']['timeout'],
- $conf['cookie']['path'], $conf['cookie']['domain'], $conf['use_ssl'] == 1 ? 1 : 0);
- session_cache_limiter(Horde_Util::nonInputVar('session_cache_limiter', $conf['session']['cache_limiter']));
- session_name(urlencode($conf['session']['name']));
-
- $type = !empty($conf['sessionhandler']['type']) ? $conf['sessionhandler']['type'] : 'none';
- if ($type == 'external') {
- $calls = $conf['sessionhandler']['params'];
- session_set_save_handler($calls['open'],
- $calls['close'],
- $calls['read'],
- $calls['write'],
- $calls['destroy'],
- $calls['gc']);
- } elseif ($type != 'none') {
- try {
- $sh = &Horde_SessionHandler::singleton($conf['sessionhandler']['type'], array_merge(self::getDriverConfig('sessionhandler', $conf['sessionhandler']['type']), array('memcache' => !empty($conf['sessionhandler']['memcache']))));
- ini_set('session.save_handler', 'user');
- session_set_save_handler(array(&$sh, 'open'),
- array(&$sh, 'close'),
- array(&$sh, 'read'),
- array(&$sh, 'write'),
- array(&$sh, 'destroy'),
- array(&$sh, 'gc'));
- $GLOBALS['horde_sessionhandler'] = $sh;
- } catch (Horde_Exception $e) {
- self::fatal(new Horde_Exception('Horde is unable to correctly start the custom session handler.'), __FILE__, __LINE__, false);
- }
- }
- }
-
- /**
- * Returns an un-used access key from the label given.
- *
- * @param string $label The label to choose an access key from.
- * @param boolean $nocheck Don't check if the access key already has been
- * used?
- *
- * @return string A single lower case character access key or empty
- * string if none can be found
- */
- static public function getAccessKey($label, $nocheck = false,
- $shutdown = false)
- {
- /* Shutdown call for translators? */
- if ($shutdown) {
- if (!count(self::$_labels)) {
- return;
- }
- $script = basename($_SERVER['PHP_SELF']);
- $labels = array_keys(self::$_labels);
- sort($labels);
- $used = array_keys(self::$_used);
- sort($used);
- $remaining = str_replace($used, array(), 'abcdefghijklmnopqrstuvwxyz');
- self::logMessage('Access key information for ' . $script, __FILE__, __LINE__);
- self::logMessage('Used labels: ' . implode(',', $labels), __FILE__, __LINE__);
- self::logMessage('Used keys: ' . implode('', $used), __FILE__, __LINE__);
- self::logMessage('Free keys: ' . $remaining, __FILE__, __LINE__);
- return;
- }
-
- /* Use access keys at all? */
- if (!isset(self::$_noAccessKey)) {
- self::$_noAccessKey = !$GLOBALS['browser']->hasFeature('accesskey') || !$GLOBALS['prefs']->getValue('widget_accesskey');
- }
-
- if (self::$_noAccessKey || !preg_match('/_([A-Za-z])/', $label, $match)) {
- return '';
- }
- $key = $match[1];
-
- /* Has this key already been used? */
- if (isset(self::$_used[strtolower($key)]) &&
- !($nocheck && isset(self::$_labels[$label]))) {
- return '';
- }
-
- /* Save key and label. */
- self::$_used[strtolower($key)] = true;
- self::$_labels[$label] = true;
-
- return $key;
- }
-
- /**
- * Strips an access key from a label.
- * For multibyte charset strings the access key gets removed completely,
- * otherwise only the underscore gets removed.
- *
- * @param string $label The label containing an access key.
- *
- * @return string The label with the access key being stripped.
- */
- static public function stripAccessKey($label)
- {
- if (!isset($GLOBALS['nls'])) {
- self::loadConfiguration('nls.php', null, 'horde');
- }
- $multibyte = isset($GLOBALS['nls']['multibyte'][Horde_Nls::getCharset(true)]);
-
- return preg_replace('/_([A-Za-z])/',
- $multibyte && preg_match('/[\x80-\xff]/', $label) ? '' : '\1',
- $label);
- }
-
- /**
- * Highlights an access key in a label.
- *
- * @param string $label The label to highlight the access key in.
- * @param string $accessKey The access key to highlight.
- *
- * @return string The HTML version of the label with the access key
- * highlighted.
- */
- static public function highlightAccessKey($label, $accessKey)
- {
- $stripped_label = self::stripAccessKey($label);
-
- if (empty($accessKey)) {
- return $stripped_label;
- }
-
- if (isset($GLOBALS['nls']['multibyte'][Horde_Nls::getCharset(true)])) {
- /* Prefix parenthesis with the UTF-8 representation of the LRO
- * (Left-to-Right-Override) Unicode codepoint U+202D. */
- $prefix = Horde_Nls::getCharset() == 'UTF-8' ? "\xe2\x80\xad" : '';
- return $stripped_label . $prefix . '(<span class="accessKey">'
- . strtoupper($accessKey) . '</span>' . ')';
- } else {
- return str_replace('_' . $accessKey, '<span class="accessKey">' . $accessKey . '</span>', $label);
- }
- }
-
- /**
- * Returns the appropriate "accesskey" and "title" attributes for an HTML
- * tag and the given label.
- *
- * @param string $label The title of an HTML element
- * @param boolean $nocheck Don't check if the access key already has been
- * used?
- *
- * @return string The title, and if appropriate, the accesskey attributes
- * for the element.
- */
- static public function getAccessKeyAndTitle($label, $nocheck = false)
- {
- $ak = self::getAccessKey($label, $nocheck);
- $attributes = 'title="' . self::stripAccessKey($label);
- if (!empty($ak)) {
- $attributes .= sprintf(_(" (Accesskey %s)"), $ak);
- $attributes .= '" accesskey="' . $ak;
- }
-
- return $attributes . '"';
- }
-
- /**
- * Returns a label element including an access key for usage in conjuction
- * with a form field. User preferences regarding access keys are respected.
- *
- * @param string $for The form field's id attribute.
- * @param string $label The label text.
- * @param string $ak The access key to use. If null a new access key
- * will be generated.
- *
- * @return string The html code for the label element.
- */
- static public function label($for, $label, $ak = null)
- {
- if (is_null($ak)) {
- $ak = self::getAccessKey($label, 1);
- }
- $label = self::highlightAccessKey($label, $ak);
-
- return sprintf('<label for="%s"%s>%s</label>',
- $for,
- !empty($ak) ? ' accesskey="' . $ak . '"' : '',
- $label);
- }
-
- /**
- * Redirects to the main Horde login page on authentication failure.
- */
- static public function authenticationFailureRedirect()
- {
- if (Horde_Cli::runningFromCLI()) {
- $cli = &Horde_Cli::singleton();
- $cli->fatal(_("You are not authenticated."));
- }
-
- $url = $GLOBALS['registry']->get('webroot', 'horde') . '/login.php';
- $url = Horde_Util::addParameter($url, array('url' => self::selfUrl(true), 'nosidebar' => 1), null, false);
- $url = Horde_Auth::addLogoutParameters($url);
- header('Location: ' . self::url($url, true));
- exit;
- }
-
- /**
- * Provides a standardised function to call a Horde hook, checking whether
- * a hook config file exists and whether the function itself exists. If
- * these two conditions are not satisfied it will return the specified
- * value (by default a PEAR error).
- *
- * @param string $hook The function to call.
- * @param array $args An array of any arguments to pass to the hook
- * function.
- * @param string $app If specified look for hooks in the config directory
- * of this app.
- * @param mixed $error What to return if $app/config/hooks.php or $hook
- * does not exist. If this is the string 'PEAR_Error'
- * a PEAR error object is returned instead, detailing
- * the failure.
- *
- * @return mixed Either the results of the hook or PEAR error on failure.
- * @todo Throw Horde_Exception
- */
- static public function callHook($hook, $args = array(), $app = 'horde',
- $error = 'PEAR_Error')
- {
- if (!isset(self::$_hooksLoaded[$app])) {
- try {
- self::loadConfiguration('hooks.php', null, $app);
- self::$_hooksLoaded[$app] = true;
- } catch (Horde_Exception $e) {
- self::logMessage($e, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- self::$_hooksLoaded[$app] = false;
- }
- }
-
- if (function_exists($hook)) {
- $result = call_user_func_array($hook, $args);
- if ($result instanceof PEAR_Error) {
- self::logMessage($result, __FILE__, __LINE__, PEAR_LOG_ERR);
- }
- return $result;
- }
-
- if (is_string($error) && strcmp($error, 'PEAR_Error') == 0) {
- $error = PEAR::raiseError(sprintf('Hook %s in application %s not called.', $hook, $app));
- self::logMessage($error, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- }
-
- return $error;
- }
-
- /**
- * Returns the specified permission for the current user.
- *
- * @param string $permission A permission, currently only 'max_blocks'.
- *
- * @return mixed The value of the specified permission.
- */
- static public function hasPermission($permission)
- {
- global $perms;
-
- if (!$perms->exists('horde:' . $permission)) {
- return true;
- }
-
- $allowed = $perms->getPermissions('horde:' . $permission);
- if (is_array($allowed)) {
- switch ($permission) {
- case 'max_blocks':
- $allowed = max($allowed);
- break;
- }
- }
-
- return $allowed;
- }
-
- /**
- * Utility function to send redirect headers to browser, handling any
- * browser quirks.
- *
- * @param string $url The URL to redirect to.
- */
- static public function redirect($url)
- {
- if ($GLOBALS['browser']->isBrowser('msie') &&
- ($GLOBALS['conf']['use_ssl'] == 3) &&
- (strlen($url) < 160)) {
- header('Refresh: 0; URL=' . $url);
- } else {
- header('Location: ' . $url);
- }
- exit;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Horde_Config:: package provides a framework for managing the
- * configuration of Horde applications, writing conf.php files from
- * conf.xml source files, generating user interfaces, etc.
- *
- * Copyright 2002-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @package Core
- */
-class Horde_Config
-{
- /**
- * The name of the configured application.
- *
- * @var string
- */
- protected $_app;
-
- /**
- * The XML tree of the configuration file traversed to an
- * associative array.
- *
- * @var array
- */
- protected $_xmlConfigTree = null;
-
- /**
- * The content of the generated configuration file.
- *
- * @var string
- */
- protected $_phpConfig;
-
- /**
- * The content of the old configuration file.
- *
- * @var string
- */
- protected $_oldConfig;
-
- /**
- * The manual configuration in front of the generated configuration.
- *
- * @var string
- */
- protected $_preConfig;
-
- /**
- * The manual configuration after the generated configuration.
- *
- * @var string
- */
- protected $_postConfig;
-
- /**
- * The current $conf array of the configured application.
- *
- * @var array
- */
- protected $_currentConfig = array();
-
- /**
- * The version tag of the conf.xml file which will be copied into the
- * conf.php file.
- *
- * @var string
- */
- protected $_versionTag = '';
-
- /**
- * The line marking the begin of the generated configuration.
- *
- * @var string
- */
- protected $_configBegin = "/* CONFIG START. DO NOT CHANGE ANYTHING IN OR AFTER THIS LINE. */\n";
-
- /**
- * The line marking the end of the generated configuration.
- *
- * @var string
- */
- protected $_configEnd = "/* CONFIG END. DO NOT CHANGE ANYTHING IN OR BEFORE THIS LINE. */\n";
-
- /**
- * Constructor.
- *
- * @param string $app The name of the application to be configured.
- */
- public function __construct($app)
- {
- $this->_app = $app;
- }
-
- /**
- * Reads the application's conf.xml file and builds an associative array
- * from its XML tree.
- *
- * @param array $custom_conf Any settings that shall be included in the
- * generated configuration.
- *
- * @return array An associative array representing the configuration
- * tree.
- */
- public function readXMLConfig($custom_conf = null)
- {
- if (!is_null($this->_xmlConfigTree) && !$custom_conf) {
- return $this->_xmlConfigTree;
- }
-
- $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
-
- if ($custom_conf) {
- $this->_currentConfig = $custom_conf;
- } else {
- /* Fetch the current conf.php contents. */
- @eval($this->getPHPConfig());
- if (isset($conf)) {
- $this->_currentConfig = $conf;
- }
- }
-
- /* Load the DOM object. */
- include_once 'Horde/DOM.php';
- $doc = Horde_DOM_Document::factory(array('filename' => $path . '/conf.xml'));
-
- /* Check if there is a CVS version tag and store it. */
- $node = $doc->first_child();
- while (!empty($node)) {
- if ($node->type == XML_COMMENT_NODE) {
- // @TODO: Old CVS tag
- if (preg_match('/\$.*?conf\.xml,v .*? .*\$/', $node->node_value(), $match) ||
- // New Git tag
- preg_match('/\$Id:\s*[0-9a-f]+\s*\$/', $node->node_value(), $match)) {
- $this->_versionTag = $match[0] . "\n";
- break;
- }
- }
- $node = $node->next_sibling();
- }
-
- /* Parse the config file. */
- $this->_xmlConfigTree = array();
- $root = $doc->root();
- if ($root->has_child_nodes()) {
- $this->_parseLevel($this->_xmlConfigTree, $root->child_nodes(), '');
- }
-
- return $this->_xmlConfigTree;
- }
-
- /**
- * Returns the file content of the current configuration file.
- *
- * @return string The unparsed configuration file content.
- */
- public function getPHPConfig()
- {
- if (!is_null($this->_oldConfig)) {
- return $this->_oldConfig;
- }
-
- $path = $GLOBALS['registry']->get('fileroot', $this->_app) . '/config';
- if (file_exists($path . '/conf.php')) {
- $this->_oldConfig = file_get_contents($path . '/conf.php');
- if (!empty($this->_oldConfig)) {
- $this->_oldConfig = preg_replace('/<\?php\n?/', '', $this->_oldConfig);
- $pos = strpos($this->_oldConfig, $this->_configBegin);
- if ($pos !== false) {
- $this->_preConfig = substr($this->_oldConfig, 0, $pos);
- $this->_oldConfig = substr($this->_oldConfig, $pos);
- }
- $pos = strpos($this->_oldConfig, $this->_configEnd);
- if ($pos !== false) {
- $this->_postConfig = substr($this->_oldConfig, $pos + strlen($this->_configEnd));
- $this->_oldConfig = substr($this->_oldConfig, 0, $pos);
- }
- }
- } else {
- $this->_oldConfig = '';
- }
-
- return $this->_oldConfig;
- }
-
- /**
- * Generates the content of the application's configuration file.
- *
- * @param Horde_Variables $formvars The processed configuration form
- * data.
- * @param array $custom_conf Any settings that shall be included
- * in the generated configuration.
- *
- * @return string The content of the generated configuration file.
- */
- public function generatePHPConfig($formvars, $custom_conf = null)
- {
- $this->readXMLConfig($custom_conf);
- $this->getPHPConfig();
-
- $this->_phpConfig = "<?php\n" . $this->_preConfig . $this->_configBegin;
- if (!empty($this->_versionTag)) {
- $this->_phpConfig .= '// ' . $this->_versionTag;
- }
- $this->_generatePHPConfig($this->_xmlConfigTree, '', $formvars);
- $this->_phpConfig .= $this->_configEnd . $this->_postConfig;
-
- return $this->_phpConfig;
- }
-
- /**
- * Generates the configuration file items for a part of the configuration
- * tree.
- *
- * @param array $section An associative array containing the
- * part of the traversed XML
- * configuration tree that should be
- * processed.
- * @param string $prefix A configuration prefix determining
- * the current position inside the
- * configuration file. This prefix will
- * be translated to keys of the $conf
- * array in the generated configuration
- * file.
- * @param Horde_Variables $formvars The processed configuration form
- * data.
- */
- protected function _generatePHPConfig($section, $prefix, $formvars)
- {
- if (!is_array($section)) {
- return;
- }
-
- foreach ($section as $name => $configitem) {
- $prefixedname = empty($prefix)
- ? $name
- : $prefix . '|' . $name;
- $configname = str_replace('|', '__', $prefixedname);
- $quote = (!isset($configitem['quote']) || $configitem['quote'] !== false);
-
- if ($configitem == 'placeholder') {
- $this->_phpConfig .= '$conf[\'' . str_replace('|', '\'][\'', $prefix) . "'] = array();\n";
- } elseif (isset($configitem['switch'])) {
- $val = $formvars->getExists($configname, $wasset);
- if (!$wasset) {
- $val = isset($configitem['default']) ? $configitem['default'] : null;
- }
- if (isset($configitem['switch'][$val])) {
- $value = $val;
- if ($quote && $value != 'true' && $value != 'false') {
- $value = "'" . $value . "'";
- }
- $this->_generatePHPConfig($configitem['switch'][$val]['fields'], $prefix, $formvars);
- }
- } elseif (isset($configitem['_type'])) {
- $val = $formvars->getExists($configname, $wasset);
- if (!$wasset) {
- $val = isset($configitem['default']) ? $configitem['default'] : null;
- }
-
- $type = $configitem['_type'];
- switch ($type) {
- case 'multienum':
- if (is_array($val)) {
- $encvals = array();
- foreach ($val as $v) {
- $encvals[] = $this->_quote($v);
- }
- $arrayval = "'" . implode('\', \'', $encvals) . "'";
- if ($arrayval == "''") {
- $arrayval = '';
- }
- } else {
- $arrayval = '';
- }
- $value = 'array(' . $arrayval . ')';
- break;
-
- case 'boolean':
- if (is_bool($val)) {
- $value = $val ? 'true' : 'false';
- } else {
- $value = ($val == 'on') ? 'true' : 'false';
- }
- break;
-
- case 'stringlist':
- $values = explode(',', $val);
- if (!is_array($values)) {
- $value = "array('" . $this->_quote(trim($values)) . "')";
- } else {
- $encvals = array();
- foreach ($values as $v) {
- $encvals[] = $this->_quote(trim($v));
- }
- $arrayval = "'" . implode('\', \'', $encvals) . "'";
- if ($arrayval == "''") {
- $arrayval = '';
- }
- $value = 'array(' . $arrayval . ')';
- }
- break;
-
- case 'int':
- if ($val !== '') {
- $value = (int)$val;
- }
- break;
-
- case 'octal':
- $value = sprintf('0%o', octdec($val));
- break;
-
- case 'header':
- case 'description':
- break;
-
- default:
- if ($val != '') {
- $value = $val;
- if ($quote && $value != 'true' && $value != 'false') {
- $value = "'" . $this->_quote($value) . "'";
- }
- }
- break;
- }
- } else {
- $this->_generatePHPConfig($configitem, $prefixedname, $formvars);
- }
-
- if (isset($value)) {
- $this->_phpConfig .= '$conf[\'' . str_replace('__', '\'][\'', $configname) . '\'] = ' . $value . ";\n";
- }
- unset($value);
- }
- }
-
- /**
- * Parses one level of the configuration XML tree into the associative
- * array containing the traversed configuration tree.
- *
- * @param array &$conf The already existing array where the processed
- * XML tree portion should be appended to.
- * @param array $children An array containing the XML nodes of the level
- * that should be parsed.
- * @param string $ctx A string representing the current position
- * (context prefix) inside the configuration XML
- * file.
- */
- protected function _parseLevel(&$conf, $children, $ctx)
- {
- reset($children);
- while (list(,$node) = each($children)) {
- if ($node->type != XML_ELEMENT_NODE) {
- continue;
- }
- $name = $node->get_attribute('name');
- $desc = Horde_Text_Filter::filter($node->get_attribute('desc'), 'linkurls', array('callback' => 'Horde::externalUrl'));
- $required = !($node->get_attribute('required') == 'false');
- $quote = !($node->get_attribute('quote') == 'false');
-
- $curctx = empty($ctx)
- ? $name
- : $ctx . '|' . $name;
-
- switch ($node->tagname) {
- case 'configdescription':
- if (empty($name)) {
- $name = hash('md5', uniqid(mt_rand(), true));
- }
-
- $conf[$name] = array(
- '_type' => 'description',
- 'desc' => Horde_Text_Filter::filter($this->_default($curctx, $this->_getNodeOnlyText($node)), 'linkurls', array('callback' => 'Horde::externalUrl'))
- );
- break;
-
- case 'configheader':
- if (empty($name)) {
- $name = hash('md5', uniqid(mt_rand(), true));
- }
-
- $conf[$name] = array(
- '_type' => 'header',
- 'desc' => $this->_default($curctx, $this->_getNodeOnlyText($node))
- );
- break;
-
- case 'configswitch':
- $values = $this->_getSwitchValues($node, $ctx);
- list($default, $isDefault) = $quote
- ? $this->__default($curctx, $this->_getNodeOnlyText($node))
- : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
-
- if ($default === '') {
- $default = key($values);
- }
-
- if (is_bool($default)) {
- $default = $default ? 'true' : 'false';
- }
-
- $conf[$name] = array(
- 'desc' => $desc,
- 'switch' => $values,
- 'default' => $default,
- 'is_default' => $isDefault
- );
- break;
-
- case 'configenum':
- $values = $this->_getEnumValues($node);
- list($default, $isDefault) = $quote
- ? $this->__default($curctx, $this->_getNodeOnlyText($node))
- : $this->__defaultRaw($curctx, $this->_getNodeOnlyText($node));
-
- if ($default === '') {
- $default = key($values);
- }
-
- if (is_bool($default)) {
- $default = $default ? 'true' : 'false';
- }
-
- $conf[$name] = array(
- '_type' => 'enum',
- 'required' => $required,
- 'quote' => $quote,
- 'values' => $values,
- 'desc' => $desc,
- 'default' => $default,
- 'is_default' => $isDefault
- );
- break;
-
- case 'configlist':
- list($default, $isDefault) = $this->__default($curctx, null);
-
- if (is_null($default)) {
- $default = $this->_getNodeOnlyText($node);
- } elseif (is_array($default)) {
- $default = implode(', ', $default);
- }
-
- $conf[$name] = array(
- '_type' => 'stringlist',
- 'required' => $required,
- 'desc' => $desc,
- 'default' => $default,
- 'is_default' => $isDefault
- );
- break;
-
- case 'configmultienum':
- $values = $this->_getEnumValues($node);
- list($default, $isDefault) = $this->__default($curctx, explode(',', $this->_getNodeOnlyText($node)));
-
- $conf[$name] = array(
- '_type' => 'multienum',
- 'required' => $required,
- 'values' => $values,
- 'desc' => $desc,
- 'default' => Horde_Array::valuesToKeys($default),
- 'is_default' => $isDefault
- );
- break;
-
- case 'configpassword':
- $conf[$name] = array(
- '_type' => 'password',
- 'required' => $required,
- 'desc' => $desc,
- 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
- 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
- );
- break;
-
- case 'configstring':
- $conf[$name] = array(
- '_type' => 'text',
- 'required' => $required,
- 'desc' => $desc,
- 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
- 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
- );
-
- if ($conf[$name]['default'] === false) {
- $conf[$name]['default'] = 'false';
- } elseif ($conf[$name]['default'] === true) {
- $conf[$name]['default'] = 'true';
- }
- break;
-
- case 'configboolean':
- $default = $this->_getNodeOnlyText($node);
- $default = !(empty($default) || $default === 'false');
-
- $conf[$name] = array(
- '_type' => 'boolean',
- 'required' => $required,
- 'desc' => $desc,
- 'default' => $this->_default($curctx, $default),
- 'is_default' => $this->_isDefault($curctx, $default)
- );
- break;
-
- case 'configinteger':
- $values = $this->_getEnumValues($node);
-
- $conf[$name] = array(
- '_type' => 'int',
- 'required' => $required,
- 'values' => $values,
- 'desc' => $desc,
- 'default' => $this->_default($curctx, $this->_getNodeOnlyText($node)),
- 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
- );
-
- if ($node->get_attribute('octal') == 'true' &&
- $conf[$name]['default'] != '') {
- $conf[$name]['_type'] = 'octal';
- $conf[$name]['default'] = sprintf('0%o', $this->_default($curctx, octdec($this->_getNodeOnlyText($node))));
- }
- break;
-
- case 'configphp':
- $conf[$name] = array(
- '_type' => 'php',
- 'required' => $required,
- 'quote' => false,
- 'desc' => $desc,
- 'default' => $this->_defaultRaw($curctx, $this->_getNodeOnlyText($node)),
- 'is_default' => $this->_isDefaultRaw($curctx, $this->_getNodeOnlyText($node))
- );
- break;
-
- case 'configsecret':
- $conf[$name] = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => $desc,
- 'default' => $this->_default($curctx, sha1(uniqid(mt_rand(), true))),
- 'is_default' => $this->_isDefault($curctx, $this->_getNodeOnlyText($node))
- );
- break;
-
- case 'configsql':
- $conf[$node->get_attribute('switchname')] = $this->_configSQL($ctx, $node);
- break;
-
- case 'configvfs':
- $conf[$node->get_attribute('switchname')] = $this->_configVFS($ctx, $node);
- break;
-
- case 'configsection':
- $conf[$name] = array();
- $cur = &$conf[$name];
- if ($node->has_child_nodes()) {
- $this->_parseLevel($cur, $node->child_nodes(), $curctx);
- }
- break;
-
- case 'configtab':
- $key = hash('md5', uniqid(mt_rand(), true));
-
- $conf[$key] = array(
- 'tab' => $name,
- 'desc' => $desc
- );
-
- if ($node->has_child_nodes()) {
- $this->_parseLevel($conf, $node->child_nodes(), $ctx);
- }
- break;
-
- case 'configplaceholder':
- $conf[hash('md5', uniqid(mt_rand(), true))] = 'placeholder';
- break;
-
- default:
- $conf[$name] = array();
- $cur = &$conf[$name];
- if ($node->has_child_nodes()) {
- $this->_parseLevel($cur, $node->child_nodes(), $curctx);
- }
- break;
- }
- }
- }
-
- /**
- * Returns the configuration tree for an SQL backend configuration to
- * replace a <configsql> tag.
- * Subnodes will be parsed and added to both the Horde defaults and the
- * Custom configuration parts.
- *
- * @param string $ctx The context of the <configsql> tag.
- * @param DomNode $node The DomNode representation of the <configsql>
- * tag.
- * @param string $switchname If DomNode is not set, the value of the
- * tag's switchname attribute.
- *
- * @return array An associative array with the SQL configuration tree.
- */
- protected function _configSQL($ctx, $node = null,
- $switchname = 'driverconfig')
- {
- $persistent = array(
- '_type' => 'boolean',
- 'required' => false,
- 'desc' => 'Request persistent connections?',
- 'default' => $this->_default($ctx . '|persistent', false)
- );
-
- $hostspec = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Database server/host',
- 'default' => $this->_default($ctx . '|hostspec', '')
- );
-
- $username = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Username to connect to the database as',
- 'default' => $this->_default($ctx . '|username', '')
- );
-
- $password = array(
- '_type' => 'text',
- 'required' => false,
- 'desc' => 'Password to connect with',
- 'default' => $this->_default($ctx . '|password', '')
- );
-
- $database = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Database name to use',
- 'default' => $this->_default($ctx . '|database', '')
- );
-
- $socket = array(
- '_type' => 'text',
- 'required' => false,
- 'desc' => 'Location of UNIX socket',
- 'default' => $this->_default($ctx . '|socket', '')
- );
-
- $port = array(
- '_type' => 'int',
- 'required' => false,
- 'desc' => 'Port the DB is running on, if non-standard',
- 'default' => $this->_default($ctx . '|port', null)
- );
-
- $protocol = array(
- 'desc' => 'How should we connect to the database?',
- 'default' => $this->_default($ctx . '|protocol', 'unix'),
- 'switch' => array(
- 'unix' => array(
- 'desc' => 'UNIX Sockets',
- 'fields' => array(
- 'socket' => $socket
- )
- ),
- 'tcp' => array(
- 'desc' => 'TCP/IP',
- 'fields' => array(
- 'hostspec' => $hostspec,
- 'port' => $port
- )
- )
- )
- );
-
- $mysql_protocol = $protocol;
- $mysql_protocol['switch']['tcp']['fields']['port']['default'] = $this->_default($ctx . '|port', 3306);
-
- $charset = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Internally used charset',
- 'default' => $this->_default($ctx . '|charset', 'utf-8')
- );
-
- $ssl = array(
- '_type' => 'boolean',
- 'required' => false,
- 'desc' => 'Use SSL to connect to the server?',
- 'default' => $this->_default($ctx . '|ssl', false)
- );
-
- $ca = array(
- '_type' => 'text',
- 'required' => false,
- 'desc' => 'Certification Authority to use for SSL connections',
- 'default' => $this->_default($ctx . '|ca', '')
- );
-
- $oci8_fields = array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password
- );
- if (function_exists('oci_connect')) {
- $oci8_fields['database'] = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Database name or Easy Connect parameter',
- 'default' => $this->_default($ctx . '|database', 'horde')
- );
- } else {
- $oci8_fields['hostspec'] = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Database name or Easy Connect parameter',
- 'default' => $this->_default($ctx . '|hostspec', 'horde')
- );
- }
- $oci8_fields['charset'] = $charset;
-
- $read_hostspec = array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Read database server/host',
- 'default' => $this->_default($ctx . '|read|hostspec', '')
- );
-
- $read_port = array(
- '_type' => 'int',
- 'required' => false,
- 'desc' => 'Port the read DB is running on, if non-standard',
- 'default' => $this->_default($ctx . '|read|port', null)
- );
-
- $splitread = array(
- '_type' => 'boolean',
- 'required' => false,
- 'desc' => 'Split reads to a different server?',
- 'default' => $this->_default($ctx . '|splitread', 'false'),
- 'switch' => array(
- 'false' => array(
- 'desc' => 'Disabled',
- 'fields' => array()
- ),
- 'true' => array(
- 'desc' => 'Enabled',
- 'fields' => array(
- 'read' => array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password,
- 'protocol' => $mysql_protocol,
- 'database' => $database,
- 'charset' => $charset
- )
- )
- )
- )
- );
-
- $custom_fields = array(
- 'required' => true,
- 'desc' => 'What database backend should we use?',
- 'default' => $this->_default($ctx . '|phptype', 'false'),
- 'switch' => array(
- 'false' => array(
- 'desc' => '[None]',
- 'fields' => array()
- ),
- 'dbase' => array(
- 'desc' => 'dBase',
- 'fields' => array(
- 'database' => array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Absolute path to the database file',
- 'default' => $this->_default($ctx . '|database', '')
- ),
- 'mode' => array(
- '_type' => 'enum',
- 'desc' => 'The mode to open the file with',
- 'values' => array(
- 0 => 'Read Only',
- 2 => 'Read Write'),
- 'default' => $this->_default($ctx . '|mode', 2)
- ),
- 'charset' => $charset
- )
- ),
- 'ibase' => array(
- 'desc' => 'Firebird/InterBase',
- 'fields' => array(
- 'dbsyntax' => array(
- '_type' => 'enum',
- 'desc' => 'The database syntax variant to use',
- 'required' => false,
- 'values' => array(
- 'ibase' => 'InterBase',
- 'firebird' => 'Firebird'
- ),
- 'default' => $this->_default($ctx . '|dbsyntax', 'firebird')
- ),
- 'persistent' => $persistent,
- 'hostspec' => $hostspec,
- 'username' => $username,
- 'password' => $password,
- 'database' => $database,
- 'buffers' => array(
- '_type' => 'int',
- 'desc' => 'The number of database buffers to allocate',
- 'required' => false,
- 'default' => $this->_default($ctx . '|buffers', null)
- ),
- 'dialect' => array(
- '_type' => 'int',
- 'desc' => 'The default SQL dialect for any statement executed within a connection.',
- 'required' => false,
- 'default' => $this->_default($ctx . '|dialect', null)
- ),
- 'role' => array(
- '_type' => 'text',
- 'desc' => 'Role',
- 'required' => false,
- 'default' => $this->_default($ctx . '|role', null)),
- 'charset' => $charset
- )
- ),
- 'fbsql' => array(
- 'desc' => 'Frontbase',
- 'fields' => array(
- 'persistent' => $persistent,
- 'hostspec' => $hostspec,
- 'username' => $username,
- 'password' => $password,
- 'database' => $database,
- 'charset' => $charset
- )
- ),
- 'ifx' => array(
- 'desc' => 'Informix',
- 'fields' => array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password,
- 'database' => $database,
- 'charset' => $charset
- )
- ),
- 'msql' => array(
- 'desc' => 'mSQL',
- 'fields' => array(
- 'persistent' => $persistent,
- 'hostspec' => $hostspec,
- 'username' => $username,
- 'password' => $password,
- 'port' => $port,
- 'database' => $database,
- 'charset' => $charset
- )
- ),
- 'mssql' => array(
- 'desc' => 'MS SQL Server',
- 'fields' => array(
- 'persistent' => $persistent,
- 'hostspec' => $hostspec,
- 'username' => $username,
- 'password' => $password,
- 'port' => $port,
- 'database' => $database,
- 'charset' => $charset
- )
- ),
- 'mysql' => array(
- 'desc' => 'MySQL',
- 'fields' => array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password,
- 'protocol' => $mysql_protocol,
- 'database' => $database,
- 'charset' => $charset,
- 'ssl' => $ssl,
- 'ca' => $ca,
- 'splitread' => $splitread
- )
- ),
- 'mysqli' => array(
- 'desc' => 'MySQL (mysqli)',
- 'fields' => array(
- 'username' => $username,
- 'password' => $password,
- 'protocol' => $mysql_protocol,
- 'database' => $database,
- 'charset' => $charset,
- 'splitread' => $splitread,
- 'ssl' => $ssl,
- 'ca' => $ca
- )),
- 'oci8' => array(
- 'desc' => 'Oracle',
- 'fields' => $oci8_fields
- ),
- 'odbc' => array(
- 'desc' => 'ODBC',
- 'fields' => array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password,
- 'hostspec' => array(
- '_type' => 'text',
- 'desc' => 'DSN',
- 'default' => $this->_default($ctx . '|hostspec', '')
- ),
- 'dbsyntax' => array(
- '_type' => 'enum',
- 'desc' => 'The database syntax variant to use',
- 'required' => false,
- 'values' => array(
- 'sql92' => 'SQL92',
- 'access' => 'Access',
- 'db2' => 'DB2',
- 'solid' => 'Solid',
- 'navision' => 'Navision',
- 'mssql' => 'MS SQL Server',
- 'sybase' => 'Sybase',
- 'mysql' => 'MySQL',
- 'mysqli' => 'MySQL (mysqli)',
- ),
- 'default' => $this->_default($ctx . '|dbsyntax', 'sql92')
- ),
- 'cursor' => array(
- '_type' => 'enum',
- 'desc' => 'Cursor type',
- 'quote' => false,
- 'required' => false,
- 'values' => array(
- 'null' => 'None',
- 'SQL_CUR_DEFAULT' => 'Default',
- 'SQL_CUR_USE_DRIVER' => 'Use Driver',
- 'SQL_CUR_USE_ODBC' => 'Use ODBC',
- 'SQL_CUR_USE_IF_NEEDED' => 'Use If Needed'
- ),
- 'default' => $this->_default($ctx . '|cursor', null)
- ),
- 'charset' => $charset
- )
- ),
- 'pgsql' => array(
- 'desc' => 'PostgreSQL',
- 'fields' => array(
- 'persistent' => $persistent,
- 'username' => $username,
- 'password' => $password,
- 'protocol' => $protocol,
- 'database' => $database,
- 'charset' => $charset
- )
- ),
- 'sqlite' => array(
- 'desc' => 'SQLite',
- 'fields' => array(
- 'database' => array(
- '_type' => 'text',
- 'required' => true,
- 'desc' => 'Absolute path to the database file',
- 'default' => $this->_default($ctx . '|database', '')
- ),
- 'mode' => array(
- '_type' => 'text',
- 'desc' => 'The mode to open the file with',
- 'default' => $this->_default($ctx . '|mode', '0644')
- ),
- 'charset' => $charset
- )
- ),
- 'sybase' => array(
- 'desc' => 'Sybase',
- 'fields' => array(
- 'persistent' => $persistent,
- 'hostspec' => $hostspec,
- 'username' => $username,
- 'password' => $password,
- 'database' => $database,
- 'appname' => array(
- '_type' => 'text',
- 'desc' => 'Application Name',
- 'required' => false,
- 'default' => $this->_default($ctx . '|appname', '')
- ),
- 'charset' => $charset
- )
- )
- )
- );
-
- if (isset($node) && $node->get_attribute('baseconfig') == 'true') {
- return $custom_fields;
- }
-
- list($default, $isDefault) = $this->__default($ctx . '|' . (isset($node) ? $node->get_attribute('switchname') : $switchname), 'horde');
- $config = array(
- 'desc' => 'Driver configuration',
- 'default' => $default,
- 'is_default' => $isDefault,
- 'switch' => array(
- 'horde' => array(
- 'desc' => 'Horde defaults',
- 'fields' => array()
- ),
- 'custom' => array(
- 'desc' => 'Custom parameters',
- 'fields' => array(
- 'phptype' => $custom_fields
- )
- )
- )
- );
-
- if (isset($node) && $node->has_child_nodes()) {
- $cur = array();
- $this->_parseLevel($cur, $node->child_nodes(), $ctx);
- $config['switch']['horde']['fields'] = array_merge($config['switch']['horde']['fields'], $cur);
- $config['switch']['custom']['fields'] = array_merge($config['switch']['custom']['fields'], $cur);
- }
-
- return $config;
- }
-
- /**
- * Returns the configuration tree for a VFS backend configuration to
- * replace a <configvfs> tag.
- * Subnodes will be parsed and added to both the Horde defaults and the
- * Custom configuration parts.
- *
- * @param string $ctx The context of the <configvfs> tag.
- * @param DomNode $node The DomNode representation of the <configvfs>
- * tag.
- *
- * @return array An associative array with the VFS configuration tree.
- */
- protected function _configVFS($ctx, $node)
- {
- $sql = $this->_configSQL($ctx . '|params');
- $default = $node->get_attribute('default');
- $default = empty($default) ? 'none' : $default;
- list($default, $isDefault) = $this->__default($ctx . '|' . $node->get_attribute('switchname'), $default);
-
- $config = array(
- 'desc' => 'What VFS driver should we use?',
- 'default' => $default,
- 'is_default' => $isDefault,
- 'switch' => array(
- 'none' => array(
- 'desc' => 'None',
- 'fields' => array()
- ),
- 'file' => array(
- 'desc' => 'Files on the local system',
- 'fields' => array(
- 'params' => array(
- 'vfsroot' => array(
- '_type' => 'text',
- 'desc' => 'Where on the real filesystem should Horde use as root of the virtual filesystem?',
- 'default' => $this->_default($ctx . '|params|vfsroot', '/tmp')
- )
- )
- )
- ),
- 'sql' => array(
- 'desc' => 'SQL database',
- 'fields' => array(
- 'params' => array(
- 'driverconfig' => $sql
- )
- )
- )
- )
- );
-
- if (isset($node) && $node->get_attribute('baseconfig') != 'true') {
- $config['switch']['horde'] = array(
- 'desc' => 'Horde defaults',
- 'fields' => array()
- );
- }
- $cases = $this->_getSwitchValues($node, $ctx . '|params');
- foreach ($cases as $case => $fields) {
- if (isset($config['switch'][$case])) {
- $config['switch'][$case]['fields']['params'] = array_merge($config['switch'][$case]['fields']['params'], $fields['fields']);
- }
- }
-
- return $config;
- }
-
- /**
- * Returns a certain value from the current configuration array or
- * a default value, if not found.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return mixed Either the value of the configuration array's requested
- * key or the default value if the key wasn't found.
- */
- protected function _default($ctx, $default)
- {
- list ($ptr,) = $this->__default($ctx, $default);
- return $ptr;
- }
-
- /**
- * Returns whether a certain value from the current configuration array
- * exists or a default value will be used.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return boolean Whether the default value will be used.
- */
- protected function _isDefault($ctx, $default)
- {
- list (,$isDefault) = $this->__default($ctx, $default);
- return $isDefault;
- }
-
- /**
- * Returns a certain value from the current configuration array or a
- * default value, if not found, and which of the values have been
- * returned.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return array First element: either the value of the configuration
- * array's requested key or the default value if the key
- * wasn't found.
- * Second element: whether the returned value was the
- * default value.
- */
- protected function __default($ctx, $default)
- {
- $ctx = explode('|', $ctx);
- $ptr = $this->_currentConfig;
-
- for ($i = 0; $i < count($ctx); ++$i) {
- if (!isset($ptr[$ctx[$i]])) {
- return array($default, true);
- }
-
- $ptr = $ptr[$ctx[$i]];
- }
-
- if (is_string($ptr)) {
- $ptr = Horde_String::convertCharset($ptr, 'iso-8859-1');
- }
-
- return array($ptr, false);
- }
-
- /**
- * Returns a certain value from the current configuration file or
- * a default value, if not found.
- * It does NOT return the actual value, but the PHP expression as used
- * in the configuration file.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return mixed Either the value of the configuration file's requested
- * key or the default value if the key wasn't found.
- */
- protected function _defaultRaw($ctx, $default)
- {
- list ($ptr,) = $this->__defaultRaw($ctx, $default);
- return $ptr;
- }
-
- /**
- * Returns whether a certain value from the current configuration array
- * exists or a default value will be used.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return boolean Whether the default value will be used.
- */
- protected function _isDefaultRaw($ctx, $default)
- {
- list (,$isDefault) = $this->__defaultRaw($ctx, $default);
- return $isDefault;
- }
-
- /**
- * Returns a certain value from the current configuration file or
- * a default value, if not found, and which of the values have been
- * returned.
- *
- * It does NOT return the actual value, but the PHP expression as used
- * in the configuration file.
- *
- * @param string $ctx A string representing the key of the
- * configuration array to return.
- * @param mixed $default The default value to return if the key wasn't
- * found.
- *
- * @return array First element: either the value of the configuration
- * array's requested key or the default value if the key
- * wasn't found.
- * Second element: whether the returned value was the
- * default value.
- */
- protected function __defaultRaw($ctx, $default)
- {
- $ctx = explode('|', $ctx);
- $pattern = '/^\$conf\[\'' . implode("'\]\['", $ctx) . '\'\] = (.*);\r?$/m';
-
- return preg_match($pattern, $this->getPHPConfig(), $matches)
- ? array($matches[1], false)
- : array($default, true);
- }
-
- /**
- * Returns the content of all text node children of the specified node.
- *
- * @param DomNode $node A DomNode object whose text node children to
- * return.
- *
- * @return string The concatenated values of all text nodes.
- */
- protected function _getNodeOnlyText($node)
- {
- $text = '';
-
- if (!$node->has_child_nodes()) {
- return $node->get_content();
- }
-
- foreach ($node->child_nodes() as $tnode) {
- if ($tnode->type == XML_TEXT_NODE) {
- $text .= $tnode->content;
- }
- }
-
- return trim($text);
- }
-
- /**
- * Returns an associative array containing all possible values of the
- * specified <configenum> tag.
- *
- * The keys contain the actual enum values while the values contain their
- * corresponding descriptions.
- *
- * @param DomNode $node The DomNode representation of the <configenum>
- * tag whose values should be returned.
- *
- * @return array An associative array with all possible enum values.
- */
- protected function _getEnumValues($node)
- {
- $values = array();
-
- if (!$node->has_child_nodes()) {
- return $values;
- }
-
- foreach ($node->child_nodes() as $vnode) {
- if ($vnode->type == XML_ELEMENT_NODE &&
- $vnode->tagname == 'values') {
- if (!$vnode->has_child_nodes()) {
- return array();
- }
-
- foreach ($vnode->child_nodes() as $value) {
- if ($value->type == XML_ELEMENT_NODE) {
- if ($value->tagname == 'configspecial') {
- return $this->_handleSpecials($value);
- } elseif ($value->tagname == 'value') {
- $text = $value->get_content();
- $desc = $value->get_attribute('desc');
- $values[$text] = empty($desc) ? $text : $desc;
- }
- }
- }
- }
- }
-
- return $values;
- }
-
- /**
- * Returns a multidimensional associative array representing the specified
- * <configswitch> tag.
- *
- * @param DomNode &$node The DomNode representation of the <configswitch>
- * tag to process.
- *
- * @return array An associative array representing the node.
- */
- protected function _getSwitchValues(&$node, $curctx)
- {
- $values = array();
-
- if (!$node->has_child_nodes()) {
- return $values;
- }
-
- foreach ($node->child_nodes() as $case) {
- if ($case->type == XML_ELEMENT_NODE) {
- $name = $case->get_attribute('name');
- $values[$name] = array(
- 'desc' => $case->get_attribute('desc'),
- 'fields' => array()
- );
- if ($case->has_child_nodes()) {
- $this->_parseLevel($values[$name]['fields'], $case->child_nodes(), $curctx);
- }
- }
- }
-
- return $values;
- }
-
- /**
- * Returns an associative array containing the possible values of a
- * <configspecial> tag as used inside of enum configurations.
- *
- * @param DomNode $node The DomNode representation of the <configspecial>
- * tag.
- *
- * @return array An associative array with the possible values.
- */
- protected function _handleSpecials($node)
- {
- switch ($node->get_attribute('name')) {
- case 'list-horde-apps':
- $apps = Horde_Array::valuesToKeys($GLOBALS['registry']->listApps(array('hidden', 'notoolbar', 'active')));
- asort($apps);
- return $apps;
-
- case 'list-horde-languages':
- return array_map(create_function('$val', 'return preg_replace(array("/&#x([0-9a-f]{4});/ie", "/(&[^;]+;)/e"), array("Horde_String::convertCharset(pack(\"H*\", \"$1\"), \"ucs-2\", \"' . Horde_Nls::getCharset() . '\")", "Horde_String::convertCharset(html_entity_decode(\"$1\", ENT_COMPAT, \"iso-8859-1\"), \"iso-8859-1\", \"' . Horde_Nls::getCharset() . '\")"), $val);'), $GLOBALS['nls']['languages']);
-
- case 'list-blocks':
- $collection = Horde_Block_Collection::singleton('portal');
- return $collection->getBlocksList();
-
- case 'list-client-fields':
- global $registry;
- $f = array();
- if ($GLOBALS['registry']->hasMethod('clients/getClientSource')) {
- $addressbook = $GLOBALS['registry']->call('clients/getClientSource');
- $fields = $GLOBALS['registry']->call('clients/clientFields', array($addressbook));
- if ($fields instanceof PEAR_Error) {
- $fields = $GLOBALS['registry']->call('clients/fields', array($addressbook));
- }
- if (!$fields instanceof PEAR_Error) {
- foreach ($fields as $field) {
- $f[$field['name']] = $field['label'];
- }
- }
- }
- return $f;
-
- case 'list-contact-sources':
- $res = $GLOBALS['registry']->call('contacts/sources');
- return $res;
- }
-
- return array();
- }
-
- /**
- * Returns the specified string with escaped single quotes
- *
- * @param string $string A string to escape.
- *
- * @return string The specified string with single quotes being escaped.
- */
- protected function _quote($string)
- {
- return str_replace("'", "\'", $string);
- }
-
-}
-
-/**
- * A Horde_Form:: form that implements a user interface for the config
- * system.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @package Core
- */
-class ConfigForm extends Horde_Form
-{
- /**
- * Don't use form tokens for the configuration form - while
- * generating configuration info, things like the Token system
- * might not work correctly. This saves some headaches.
- *
- * @var boolean
- */
- protected $_useFormToken = false;
-
- /**
- * Contains the Horde_Config object that this form represents.
- *
- * @var Horde_Config
- */
- protected $_xmlConfig;
-
- /**
- * Contains the Horde_Variables object of this form.
- *
- * @var Horde_Variables
- */
- protected $_vars;
-
- /**
- * Constructor.
- *
- * @param Horde_Variables &$vars The variables object of this form.
- * @param string $app The name of the application that this
- * configuration form is for.
- */
- public function __construct(&$vars, $app)
- {
- parent::__construct($vars);
-
- $this->_xmlConfig = new Horde_Config($app);
- $this->_vars = &$vars;
- $config = $this->_xmlConfig->readXMLConfig();
- $this->addHidden('', 'app', 'text', true);
- $this->_buildVariables($config);
- }
-
- /**
- * Builds the form based on the specified level of the configuration tree.
- *
- * @param array $config The portion of the configuration tree for that
- * the form fields should be created.
- * @param string $prefix A string representing the current position
- * inside the configuration tree.
- */
- protected function _buildVariables($config, $prefix = '')
- {
- if (!is_array($config)) {
- return;
- }
-
- foreach ($config as $name => $configitem) {
- $prefixedname = empty($prefix) ? $name : $prefix . '|' . $name;
- $varname = str_replace('|', '__', $prefixedname);
- if ($configitem == 'placeholder') {
- continue;
- } elseif (isset($configitem['tab'])) {
- $this->setSection($configitem['tab'], $configitem['desc']);
- } elseif (isset($configitem['switch'])) {
- $selected = $this->_vars->getExists($varname, $wasset);
- $var_params = array();
- $select_option = true;
- if (is_bool($configitem['default'])) {
- $configitem['default'] = $configitem['default'] ? 'true' : 'false';
- }
- foreach ($configitem['switch'] as $option => $case) {
- $var_params[$option] = $case['desc'];
- if ($option == $configitem['default']) {
- $select_option = false;
- if (!$wasset) {
- $selected = $option;
- }
- }
- }
-
- $name = '$conf[' . implode('][', explode('|', $prefixedname)) . ']';
- $desc = $configitem['desc'];
-
- $v = &$this->addVariable($name, $varname, 'enum', true, false, $desc, array($var_params, $select_option));
- if (array_key_exists('default', $configitem)) {
- $v->setDefault($configitem['default']);
- }
- if (!empty($configitem['is_default'])) {
- $v->_new = true;
- }
- $v_action = Horde_Form_Action::factory('reload');
- $v->setAction($v_action);
- if (isset($selected) && isset($configitem['switch'][$selected])) {
- $this->_buildVariables($configitem['switch'][$selected]['fields'], $prefix);
- }
- } elseif (isset($configitem['_type'])) {
- $required = (isset($configitem['required'])) ? $configitem['required'] : true;
- $type = $configitem['_type'];
-
- // FIXME: multienum fields can well be required, meaning that
- // you need to select at least one entry. Changing this before
- // Horde 4.0 would break a lot of configuration files though.
- if ($type == 'multienum' || $type == 'header' ||
- $type == 'description') {
- $required = false;
- }
-
- $var_params = ($type == 'multienum' || $type == 'enum')
- ? array($configitem['values'])
- : array();
-
- if ($type == 'header' || $type == 'description') {
- $name = $configitem['desc'];
- $desc = null;
- } else {
- $name = '$conf[' . implode('][', explode('|', $prefixedname)) . ']';
- $desc = $configitem['desc'];
- if ($type == 'php') {
- $type = 'text';
- $desc .= "\nEnter a valid PHP expression.";
- }
- }
-
- $v = &$this->addVariable($name, $varname, $type, $required, false, $desc, $var_params);
- if (isset($configitem['default'])) {
- $v->setDefault($configitem['default']);
- }
- if (!empty($configitem['is_default'])) {
- $v->_new = true;
- }
- } else {
- $this->_buildVariables($configitem, $prefixedname);
- }
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde_ErrorHandler: simple error_handler implementation for
- * handling PHP errors, generating backtraces for them, etc.
- *
- * @TODO Split dump() off into a Horde_Log backend, and make this more
- * general-purpose. Also make it configurable whether or not to honor
- * suppression of errors with @.
- *
- * @category Horde
- * @package Core
- */
-class Horde_ErrorHandler
-{
- /**
- * Mapping of error codes to error code names.
- *
- * @var array
- */
- public static $errorTypes = array(
- 1 => 'ERROR',
- 2 => 'WARNING',
- 4 => 'PARSE',
- 8 => 'NOTICE',
- 16 => 'CORE_ERROR',
- 32 => 'CORE_WARNING',
- 64 => 'COMPILE_ERROR',
- 128 => 'COMPILE_WARNING',
- 256 => 'USER_ERROR',
- 512 => 'USER_WARNING',
- 1024 => 'USER_NOTICE',
- 2047 => 'ALL',
- 2048 => 'STRICT',
- 4096 => 'RECOVERABLE_ERROR',
- );
-
- /**
- * error_reporting mask
- *
- * @var integer
- */
- protected static $_mask = E_ALL;
-
- /**
- * Array of errors that have been caught.
- *
- * @var array
- */
- protected static $_errors = array();
-
- /**
- * Configurable function to run on shutdown.
- *
- * @var callable
- */
- protected static $_shutdownFunc;
-
- /**
- * Set the error handler and shutdown functions.
- *
- * @param TODO
- */
- public static function register($shutdownFunc = null)
- {
- set_error_handler(array(__CLASS__, 'handleError'));
-
- if (is_null($shutdownFunc)) {
- $shutdownFunc = array(__CLASS__, 'dump');
- }
-
- self::$_shutdownFunc = $shutdownFunc;
- }
-
- /**
- * Call the shutdown func, passing in accumulated errors.
- */
- public function __destruct()
- {
- if (self::$_errors) {
- call_user_func(self::$_shutdownFunc, self::$_errors);
- }
- }
-
- /**
- * Process and handle/store an error.
- *
- * @param integer $errno TODO
- * @param string $errstr TODO
- * @param string $errfile TODO
- * @param integer $errline TODO
- */
- public static function handleError($errno, $errstr, $errfile, $errline)
- {
- // Was the error suppressed?
- if (!error_reporting()) {
- // @TODO
- // ...
- }
-
- // Check the mask.
- if ($errno & self::$_mask) {
- self::$_errors[] = array(
- 'no' => $errno,
- 'str' => self::_cleanErrorString($errstr),
- 'file' => $errfile,
- 'line' => $errline,
- 'trace' => self::_errorBacktrace(),
- );
- }
- }
-
- /**
- * Include the context of the error in the debug
- * information. Takes more (and could be much more) memory.
- *
- * @param integer $errno TODO
- * @param string $errstr TODO
- * @param string $errfile TODO
- * @param integer $errline TODO
- * @param TODO $errcontext TODO
- */
- public static function handleErrorWithContext($errno, $errstr, $errfile,
- $errline, $errcontext)
- {
- self::$_errors[] = array(
- 'no' => $errno,
- 'str' => self::_cleanErrorString($errstr),
- 'file' => $errfile,
- 'line' => $errline,
- 'context' => $errcontext,
- 'trace' => self::_errorBacktrace(),
- );
- }
-
- /**
- * Remove function documentation links from an error string.
- *
- * @param string $errstr TODO
- *
- * @return string TODO
- */
- protected static function _cleanErrorString($errstr)
- {
- return preg_replace("%\s\[<a href='function\.[\d\w-_]+'>function\.[\d\w-_]+</a>\]%", '', $errstr);
- }
-
- /**
- * Generate an exception-like backtrace from the debug_backtrace()
- * function for errors.
- *
- * @return array TODO
- */
- protected static function _errorBacktrace()
- {
- // Skip two levels of backtrace
- $skip = 2;
-
- $backtrace = debug_backtrace();
- $trace = array();
- for ($i = $skip, $i_max = count($backtrace); $i < $i_max; $i++) {
- $frame = $backtrace[$i];
- $trace[$i - $skip] = array(
- 'file' => isset($frame['file']) ? $frame['file'] : null,
- 'line' => isset($frame['line']) ? $frame['line'] : null,
- 'function' => isset($frame['function']) ? $frame['function'] : null,
- 'class' => isset($frame['class']) ? $frame['class'] : null,
- 'type' => isset($frame['type']) ? $frame['type'] : null,
- 'args' => isset($frame['args']) ? $frame['args'] : null,
- );
- }
-
- return $trace;
- }
-
- /**
- * On text/html pages, if the user is an administrator, show all
- * errors that occurred during the request.
- *
- * @param array $errors Accumulated errors.
- */
- public static function dump($errors)
- {
- if (!Horde_Auth::isAdmin()) {
- return;
- }
-
- $dump = false;
- foreach (headers_list() as $header) {
- if (strpos($header, 'Content-type: text/html') !== false) {
- $dump = true;
- break;
- }
- }
-
- if ($dump) {
- foreach ($errors as $error) {
- echo '<p>' . htmlspecialchars($error['file']) . ':' . htmlspecialchars($error['line']) . ': ' . htmlspecialchars($error['str']) . '</p>';
- }
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Horde base exception class, which includes the ability to take the
- * output of error_get_last() as $code and mask itself as that error.
- *
- * Copyright 2008-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @category Horde
- * @package Core
- */
-class Horde_Exception extends Exception
-{
- /**
- * Exception constructor
- *
- * If $code_or_lasterror is passed the return value of
- * error_get_last() (or a matching format), the exception will be
- * rewritten to have its file and line parameters match that of
- * the array, and any message in the array will be appended to
- * $message.
- *
- * @param mixed $message The exception message, a PEAR_Error
- * object, or an Exception object.
- * @param mixed $code_or_lasterror Either a numeric error code, or
- * an array from error_get_last().
- */
- public function __construct($message = null, $code_or_lasterror = null)
- {
- if (is_object($message) &&
- method_exists($message, 'getMessage')) {
- if (is_null($code_or_lasterror) &&
- method_exists($message, 'getCode')) {
- $code_or_lasterror = $message->getCode();
- }
- $message = $message->getMessage();
- }
-
- if (is_null($code_or_lasterror)) {
- $code_or_lasterror = 0;
- }
-
- if (is_array($code_or_lasterror)) {
- if ($message) {
- $message .= $code_or_lasterror['message'];
- } else {
- $message = $code_or_lasterror['message'];
- }
-
- $this->file = $code_or_lasterror['file'];
- $this->line = $code_or_lasterror['line'];
- $code = $code_or_lasterror['type'];
- } else {
- $code = $code_or_lasterror;
- }
-
- if (is_string($code)) {
- $code = null;
- }
-
- parent::__construct($message, $code);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Horde_Help:: class provides an interface to the online help subsystem.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Jon Parise <jon@horde.org>
- * @package Core
- */
-class Horde_Help
-{
- /* Raw help in the string. */
- const SOURCE_RAW = 0;
-
- /* Help text is in a file. */
- const SOURCE_FILE = 1;
-
- /**
- * Handle for the XML parser object.
- *
- * @var resource
- */
- protected $_parser;
-
- /**
- * String buffer to hold the XML help source.
- *
- * @var string
- */
- protected $_buffer = '';
-
- /**
- * String containing the ID of the requested help entry.
- *
- * @var string
- */
- protected $_reqEntry = '';
-
- /**
- * String containing the ID of the current help entry.
- *
- * @var string
- */
- protected $_curEntry = '';
-
- /**
- * String containing the formatted output.
- *
- * @var string
- */
- protected $_output = '';
-
- /**
- * Boolean indicating whether we're inside a <help> block.
- *
- * @var boolean
- */
- protected $_inHelp = false;
-
- /**
- * Boolean indicating whether we're inside the requested block.
- *
- * @var boolean
- */
- protected $_inBlock = false;
-
- /**
- * Boolean indicating whether we're inside a <title> block.
- *
- * @var boolean
- */
- protected $_inTitle = false;
-
- /**
- * Boolean indicating whether we're inside a heading block.
- *
- * @var boolean
- */
- protected $_inHeading = false;
-
- /**
- * Hash containing an index of all of the help entries.
- *
- * @var array
- */
- protected $_entries = array();
-
- /**
- * String containing the charset of the XML data source.
- *
- * @var string
- */
- protected $_charset = 'iso-8859-1';
-
- /**
- * Hash of user-defined function handlers for the XML elements.
- *
- * @var array
- */
- protected $_handlers = array(
- 'help' => '_helpHandler',
- 'entry' => '_entryHandler',
- 'title' => '_titleHandler',
- 'heading' => '_headingHandler',
- 'para' => '_paraHandler',
- 'ref' => '_refHandler',
- 'eref' => '_erefHandler',
- 'href' => '_hrefHandler',
- 'b' => '_bHandler',
- 'i' => '_iHandler',
- 'pre' => '_preHandler',
- 'tip' => '_tipHandler',
- 'warn' => '_warnHandler'
- );
-
- /**
- * Hash containing an index of all of the search results.
- *
- * @var array
- */
- protected $_search = array();
-
- /**
- * String containing the keyword for the search.
- *
- * @var string
- */
- protected $_keyword = '';
-
- /**
- * Constructor.
- *
- * @param integer $source The source of the XML help data, based on the
- * SOURCE_* constants.
- * @param string $arg Source-dependent argument for this Help
- * instance.
- */
- public function __construct($source, $arg = null)
- {
- if (isset($GLOBALS['nls']['charsets'][$GLOBALS['language']])) {
- $this->_charset = $GLOBALS['nls']['charsets'][$GLOBALS['language']];
- }
-
- /* Populate $this->_buffer based on $source. */
- switch ($source) {
- case self::SOURCE_RAW:
- $this->_buffer = $arg;
- break;
-
- case self::SOURCE_FILE:
- if (file_exists($arg[0]) && filesize($arg[0])) {
- $this->_buffer = file_get_contents($arg[0]);
- } elseif (file_exists($arg[1]) && filesize($arg[1])) {
- $this->_buffer = file_get_contents($arg[1]);
- } else {
- $this->_buffer = '';
- }
- break;
-
- default:
- $this->_buffer = '';
- break;
- }
- }
-
- /**
- * Generates the HTML link that will pop up a help window for the
- * requested topic.
- *
- * @param string $module The name of the current Horde module.
- * @param string $topic The help topic to be displayed.
- *
- * @return string The HTML to create the help link.
- */
- static public function link($module, $topic)
- {
- if (!Horde::showService('help')) {
- return ' ';
- }
-
- if ($GLOBALS['browser']->hasFeature('javascript')) {
- Horde::addScriptFile('popup.js', 'horde');
- }
-
- $url = Horde::url($GLOBALS['registry']->get('webroot', 'horde') . '/services/help/', true);
- $url = Horde_Util::addParameter($url, array('module' => $module,
- 'topic' => $topic));
-
- return Horde::link($url, _("Help"), 'helplink', 'hordehelpwin', 'popup(this.href); return false;') .
- Horde::img('help.png', _("Help"), 'width="16" height="16"', $GLOBALS['registry']->getImageDir('horde')) . '</a>';
- }
-
- /**
- * Looks up the requested entry in the XML help buffer.
- *
- * @param string $entry String containing the entry ID.
- */
- public function lookup($entry)
- {
- $this->_output = '';
- $this->_reqEntry = Horde_String::upper($entry);
- $this->_init();
- xml_parse($this->_parser, $this->_buffer, true);
- }
-
- /**
- * Returns a hash of all of the topics in this help buffer
- * containing the keyword specified.
- *
- * @return array Hash of all of the search results.
- */
- public function search($keyword)
- {
- $this->_init();
- $this->_keyword = $keyword;
- xml_parse($this->_parser, $this->_buffer, true);
-
- return $this->_search;
- }
-
- /**
- * Returns a hash of all of the topics in this help buffer.
- *
- * @return array Hash of all of the topics in this buffer.
- */
- public function topics()
- {
- $this->_init();
- xml_parse($this->_parser, $this->_buffer, true);
-
- return $this->_entries;
- }
-
- /**
- * Display the contents of the formatted output buffer.
- */
- public function display()
- {
- echo $this->_output;
- }
-
- /**
- * Initializes the XML parser.
- *
- * @return boolean Returns true on success, false on failure.
- */
- protected function _init()
- {
- if (!isset($this->_parser)) {
- if (!Horde_Util::extensionExists('xml')) {
- Horde::fatal(PEAR::raiseError('The XML functions are not available. Rebuild PHP with --with-xml.'), __FILE__, __LINE__, false);
- }
-
- /* Create a new parser and set its default properties. */
- $this->_parser = xml_parser_create();
- xml_set_object($this->_parser, $this);
- xml_parser_set_option($this->_parser, XML_OPTION_CASE_FOLDING, false);
- xml_set_element_handler($this->_parser, '_startElement', '_endElement');
- xml_set_character_data_handler($this->_parser, '_defaultHandler');
- }
-
- return ($this->_parser != 0);
- }
-
- /**
- * User-defined function callback for start elements.
- *
- * @param object $parser Handle to the parser instance.
- * @param string $name The name of this XML element.
- * @param array $attrs List of this element's attributes.
- */
- protected function _startElement($parser, $name, $attrs)
- {
- /* Call the assigned handler for this element, if one is
- * available. */
- if (in_array($name, array_keys($this->_handlers))) {
- call_user_func(array(&$this, $this->_handlers[$name]), true, $attrs);
- }
- }
-
- /**
- * User-defined function callback for end elements.
- *
- * @param object $parser Handle to the parser instance.
- * @param string $name The name of this XML element.
- */
- protected function _endElement($parser, $name)
- {
- /* Call the assigned handler for this element, if one is available. */
- if (in_array($name, array_keys($this->_handlers))) {
- call_user_func(array(&$this, $this->_handlers[$name]), false);
- }
- }
-
- /**
- * User-defined function callback for character data.
- *
- * @param object $parser Handle to the parser instance.
- * @param string $data String of character data.
- */
- protected function _defaultHandler($parser, $data)
- {
- $data = Horde_String::convertCharset($data, version_compare(zend_version(), '2', '<') ? $this->_charset : 'UTF-8');
- if ($this->_inTitle) {
- $this->_entries[$this->_curEntry] .= $data;
- }
-
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= htmlspecialchars($data);
- }
-
- if ($this->_keyword) {
- if (stristr($data, $this->_keyword) !== false) {
- $this->_search[$this->_curEntry] = $this->_entries[$this->_curEntry];
- }
- }
- }
-
- /**
- * XML element handler for the <help> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes (Not used).
- */
- protected function _helpHandler($startTag, $attrs = array())
- {
- $this->_inHelp = $startTag ? true : false;
- }
-
- /**
- * XML element handler for the <entry> tag.
- * Attributes: id
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _entryHandler($startTag, $attrs = array())
- {
- if (!$startTag) {
- $this->_inBlock = false;
- } else {
- $id = Horde_String::upper($attrs['id']);
- $this->_curEntry = $id;
- $this->_entries[$id] = '';
- $this->_inBlock = ($id == $this->_reqEntry);
- }
- }
-
- /**
- * XML element handler for the <title> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes (Not used).
- */
- protected function _titleHandler($startTag, $attrs = array())
- {
- $this->_inTitle = $startTag;
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<h1>' : '</h1>';
- }
- }
-
- /**
- * XML element handler for the <heading> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes (Not used).
- */
- protected function _headingHandler($startTag, $attrs = array())
- {
- $this->_inHeading = $startTag;
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<h2>' : '</h2>';
- }
- }
-
- /**
- * XML element handler for the <para> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes (Not used).
- */
- protected function _paraHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<p>' : '</p>';
- }
- }
-
- /**
- * XML element handler for the <ref> tag.
- * Required attributes: ENTRY, MODULE
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _refHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- if ($startTag && isset($attrs['module']) && isset($attrs['entry'])) {
- $url = Horde_Util::addParameter(Horde::selfUrl(),
- array('show' => 'entry',
- 'module' => $attrs['module'],
- 'topic' => $attrs['entry']));
- $this->_output .= Horde::link($url);
- } else {
- $this->_output .= '</a>';
- }
- }
- }
-
- /**
- * XML element handler for the <eref> tag.
- * Required elements: URL
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _erefHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- if ($startTag) {
- $this->_output .= Horde::link($attrs['url'], null, '', '_blank');
- } else {
- $this->_output .= '</a>';
- }
- }
- }
-
- /**
- * XML element handler for the <href> tag.
- * Required elements: url, app.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _hrefHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- if ($startTag) {
- $url = Horde::url($GLOBALS['registry']->get('webroot', $attrs['app']) . '/' . $attrs['url']);
- $this->_output .= Horde::link($url, null, '', '_blank');
- } else {
- $this->_output .= '</a>';
- }
- }
- }
-
- /**
- * XML element handler for the <b> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes (Not used).
- */
- protected function _bHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<strong>' : '</strong>';
- }
- }
-
- /**
- * XML element handler for the <i> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _iHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<em>' : '</em>';
- }
- }
-
- /**
- * XML element handler for the <pre> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _preHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<pre>' : '</pre>';
- }
- }
-
- /**
- * XML element handler for the <tip> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _tipHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<em class="helpTip">' : '</em>';
- }
- }
-
- /**
- * XML element handler for the <warn> tag.
- *
- * @param boolean $startTag Boolean indicating whether this instance is a
- * start tag.
- * @param array $attrs Additional element attributes.
- */
- protected function _warnHandler($startTag, $attrs = array())
- {
- if ($this->_inHelp && $this->_inBlock) {
- $this->_output .= $startTag ? '<em class="helpWarn">' : '</em>';
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Horde_Menu:: class provides standardized methods for creating menus in
- * Horde applications.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jon Parise <jon@horde.org>
- * @package Core
- */
-class Horde_Menu
-{
- /* TODO */
- const MASK_NONE = 0;
- const MASK_HELP = 1;
- const MASK_LOGIN = 2;
- const MASK_PREFS = 4;
- const MASK_PROBLEM = 8;
- const MASK_ALL = 15;
-
- /* TODO */
- const POS_LAST = 999;
-
- /**
- * Menu array.
- *
- * @var array
- */
- protected $_menu = array();
-
- /**
- * Mask defining what general Horde links are shown in this Menu.
- *
- * @var integer
- */
- protected $_mask;
-
- /**
- * Constructor
- */
- public function __construct($mask = self::MASK_ALL)
- {
- /* Menuitem mask. */
- $this->_mask = $mask;
-
- /* Location of the menufile. */
- $this->_menufile = $GLOBALS['registry']->get('fileroot') . '/config/menu.php';
- }
-
- /**
- * Add an item to the menu array.
- *
- * @param string $url String containing the value for the hyperlink.
- * @param string $text String containing the label for this menu
- * item.
- * @param string $icon String containing the filename of the image
- * icon to display for this menu item.
- * @param string $icon_path If the icon lives in a non-default directory,
- * where is it?
- * @param string $target If the link needs to open in another frame or
- * window, what is its name?
- * @param string $onclick Onclick javascript, if desired.
- * @param string $class CSS class for the menu item.
- *
- * @return integer The id (NOT guaranteed to be an array index) of the
- * item just added to the menu.
- */
- public function add($url, $text, $icon = '', $icon_path = null,
- $target = '', $onclick = null, $class = null)
- {
- $pos = count($this->_menu);
- if (!$pos || ($pos - 1 != max(array_keys($this->_menu)))) {
- $pos = count($this->_menu);
- }
-
- $this->_menu[$pos] =
- array(
- 'url' => $url,
- 'text' => $text,
- 'icon' => $icon,
- 'icon_path' => $icon_path,
- 'target' => $target,
- 'onclick' => $onclick,
- 'class' => $class
- );
-
- return $pos;
- }
-
- /**
- * Add an item to the menu array.
- *
- * @param string $url String containing the value for the hyperlink.
- * @param string $text String containing the label for this menu
- * item.
- * @param string $icon String containing the filename of the image
- * icon to display for this menu item.
- * @param string $icon_path If the icon lives in a non-default directory,
- * where is it?
- * @param string $target If the link needs to open in another frame or
- * window, what is its name?
- * @param string $onclick Onclick javascript, if desired.
- * @param string $class CSS class for the menu item.
- *
- * @return integer The id (NOT guaranteed to be an array index) of the item
- * just added to the menu.
- */
- public function addArray($item)
- {
- $pos = count($this->_menu);
- if (!$pos || ($pos - 1 != max(array_keys($this->_menu)))) {
- $pos = count($this->_menu);
- }
-
- $this->_menu[$pos] = $item;
-
- return $pos;
- }
-
- /**
- * TODO
- */
- public function setPosition($id, $pos)
- {
- if (!isset($this->_menu[$id]) || isset($this->_menu[$pos])) {
- return false;
- }
-
- $item = $this->_menu[$id];
- unset($this->_menu[$id]);
- $this->_menu[$pos] = $item;
-
- return true;
- }
-
- /**
- * Return the unordered list representing the list of menu items. Styling
- * is done through CSS.
- *
- * @return string An unordered list of menu elements that can be entirely
- * styled with CSS.
- */
- public function render()
- {
- global $conf, $registry, $prefs;
-
- $graphics = $registry->getImageDir('horde');
- $app = $registry->getApp();
-
- if ($this->_mask !== self::MASK_NONE) {
- /* Add any custom menu items. */
- $this->addSiteLinks();
-
- /* Add any app menu items. */
- $this->addAppLinks();
- }
-
- /* Add settings link. */
- if ($this->_mask & self::MASK_PREFS && $url = Horde::getServiceLink('options', $app)) {
- $this->add($url, _("_Options"), 'prefs.png', $graphics);
- }
-
- /* Add problem link. */
- if ($this->_mask & self::MASK_PROBLEM && $problem_link = Horde::getServiceLink('problem', $app)) {
- $this->add($problem_link, _("Problem"), 'problem.png', $graphics);
- }
-
- /* Add help link. */
- if ($this->_mask & self::MASK_HELP && $help_link = Horde::getServiceLink('help', $app)) {
- $this->add($help_link, _("Help"), 'help_index.png', $graphics, 'help', 'popup(this.href); return false;', 'helplink');
- }
-
- /* Login/Logout. */
- if ($this->_mask & self::MASK_LOGIN) {
- /* If the sidebar isn't always shown, but is sometimes
- * shown, then logout links should be to the parent
- * frame. */
- $auth_target = null;
- if ($conf['menu']['always'] || $prefs->getValue('show_sidebar')) {
- $auth_target = '_parent';
- }
-
- if (Horde_Auth::getAuth()) {
- if ($logout_link = Horde::getServiceLink('logout', $app, !$prefs->getValue('show_sidebar'))) {
- $this->add($logout_link, _("_Log out"), 'logout.png', $graphics, $auth_target, null, '__noselection');
- }
- } else {
- if ($login_link = Horde::getServiceLink('login', $app)) {
- $this->add($login_link, _("_Log in"), 'login.png', $graphics, $auth_target, null, '__noselection');
- }
- }
- }
-
- /* No need to return an empty list if there are no menu
- * items. */
- if (!count($this->_menu)) {
- return '';
- }
-
- /* Sort to match explicitly set positions. */
- ksort($this->_menu);
- if (!empty($GLOBALS['nls']['rtl'][$GLOBALS['language']])) {
- $this->_menu = array_reverse($this->_menu) ;
- }
-
- $menu_view = $prefs->getValue('menu_view');
- $output = '<ul>';
- foreach ($this->_menu as $m) {
- /* Check for separators. */
- if ($m == 'separator') {
- $output .= "\n<li class=\"separator\"> </li>";
- continue;
- }
-
- /* Item class and selected indication. */
- if (!isset($m['class'])) {
- /* Try to match the item's path against the current
- * script filename as well as other possible URLs to
- * this script. */
- if (self::isSelected($m['url'])) {
- $m['class'] = 'current';
- }
- } elseif ($m['class'] === '__noselection') {
- unset($m['class']);
- }
-
- /* Icon. */
- $icon = '';
- if ($menu_view == 'icon' || $menu_view == 'both') {
- if (!isset($m['icon_path'])) {
- $m['icon_path'] = null;
- }
- $icon = Horde::img($m['icon'], Horde::stripAccessKey($m['text']), '', $m['icon_path']) . '<br />';
- }
-
- /* Link. */
- $accesskey = Horde::getAccessKey($m['text']);
- $link = Horde::link($m['url'], ($menu_view == 'icon') ? Horde::stripAccessKey($m['text']) : '',
- isset($m['class']) ? $m['class'] : '',
- isset($m['target']) ? $m['target'] : '',
- isset($m['onclick']) ? $m['onclick'] : '',
- '', $accesskey);
-
- $output .= sprintf("\n<li>%s%s%s</a></li>",
- $link, $icon, ($menu_view != 'icon') ? Horde::highlightAccessKey($m['text'], $accesskey) : '');
- }
-
- return $output . '</ul>';
- }
-
- /**
- * Any links to other Horde applications defined in an application's config
- * file by the $conf['menu']['apps'] array are added to the menu array.
- */
- public function addAppLinks()
- {
- global $conf, $registry;
-
- if (isset($conf['menu']['apps']) && is_array($conf['menu']['apps'])) {
- foreach ($conf['menu']['apps'] as $app) {
- if ($registry->get('status', $app) != 'inactive' && $registry->hasPermission($app, PERMS_SHOW)) {
- $url = $registry->getInitialPage($app);
- if (!is_a($url, 'PEAR_Error')) {
- $this->add(Horde::url($url), $registry->get('name', $app), $registry->get('icon', $app), '');
- }
- }
- }
- }
- }
-
- /**
- * Add any other links found in $this->_menufile to be included in the
- * menu.
- */
- public function addSiteLinks()
- {
- if (is_readable($this->_menufile)) {
- include $this->_menufile;
- if (isset($_menu) && is_array($_menu)) {
- foreach ($_menu as $menuitem) {
- $this->addArray($menuitem);
- }
- }
- }
- }
-
- /**
- * Checks to see if the current url matches the given url.
- *
- * @return boolean Whether the given URL is the current location.
- */
- static public function isSelected($url)
- {
- $server_url = parse_url($_SERVER['PHP_SELF']);
- $check_url = parse_url($url);
-
- /* Try to match the item's path against the current script
- filename as well as other possible URLs to this script. */
- if (isset($check_url['path']) &&
- (($check_url['path'] == $server_url['path']) ||
- ($check_url['path'] . 'index.php' == $server_url['path']) ||
- ($check_url['path'] . '/index.php' == $server_url['path']))) {
- return true;
- }
-
- return false;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Horde_Registry:: class provides a set of methods for communication
- * between Horde applications and keeping track of application
- * configuration information.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Chuck Hagenbuch <chuck@horde.org>
- * @author Jon Parise <jon@horde.org>
- * @author Anil Madhavapeddy <anil@recoil.org>
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Core
- */
-class Horde_Registry
-{
- /* Session flags. */
- const SESSION_NONE = 1;
- const SESSION_READONLY = 2;
-
- /**
- * Singleton value.
- *
- * @var Horde_Registry
- */
- static protected $_instance;
-
- /**
- * Cached information.
- *
- * @var array
- */
- protected $_cache = array();
-
- /**
- * The Horde_Cache object.
- *
- * @var Horde_Cache
- */
- protected $_cacheob;
-
- /**
- * The last modified time of the newest modified registry file.
- *
- * @var integer
- */
- protected $_regmtime;
-
- /**
- * Stack of in-use applications.
- *
- * @var array
- */
- protected $_appStack = array();
-
- /**
- * The list of APIs.
- *
- * @param array
- */
- protected $_apis = array();
-
- /**
- * Cached values of the image directories.
- *
- * @param array
- */
- protected $_imgDir = array();
-
- /**
- * Hash storing information on each registry-aware application.
- *
- * @var array
- */
- public $applications = array();
-
- /**
- * Returns a reference to the global Horde_Registry object, only creating
- * it if it doesn't already exist.
- *
- * This method must be invoked as:
- * $registry = Horde_Registry::singleton()
- *
- * @param integer $session_flags Any session flags.
- *
- * @return Horde_Registry The Horde_Registry instance.
- */
- static public function singleton($session_flags = 0)
- {
- if (!isset(self::$_instance)) {
- self::$_instance = new Horde_Registry($session_flags);
- }
-
- return self::$_instance;
- }
-
- /**
- * Create a new Horde_Registry instance.
- *
- * @param integer $session_flags Any session flags.
- *
- * @throws Horde_Exception
- */
- protected function __construct($session_flags = 0)
- {
- /* Import and global Horde's configuration values. */
- $this->_cache['conf-horde'] = Horde::loadConfiguration('conf.php', 'conf', 'horde');
-
- $conf = $GLOBALS['conf'] = &$this->_cache['conf-horde'];
-
- /* Initial Horde-wide settings. */
-
- /* Set the maximum execution time in accordance with the config
- * settings. */
- error_reporting(0);
- set_time_limit($conf['max_exec_time']);
-
- /* Set the error reporting level in accordance with the config
- * settings. */
- error_reporting($conf['debug_level']);
-
- /* Set the umask according to config settings. */
- if (isset($conf['umask'])) {
- umask($conf['umask']);
- }
-
- /* Start a session. */
- if ($session_flags & self::SESSION_NONE) {
- /* Never start a session if the session flags include
- SESSION_NONE. */
- $_SESSION = array();
- } else {
- Horde::setupSessionHandler();
- $old_error = error_reporting(0);
- session_start();
- if ($session_flags & self::SESSION_READONLY) {
- /* Close the session immediately so no changes can be
- made but values are still available. */
- session_write_close();
- }
- error_reporting($old_error);
-
- if (!isset($_SESSION['_registry'])) {
- $_SESSION['_registry'] = array();
- }
- }
-
- /* Initialize the localization routines and variables. We can't use
- * Horde_Nls::setLanguageEnvironment() here because that depends on the
- * registry to be already initialized. */
- Horde_Nls::setLang();
- Horde_Nls::setTextdomain('horde', HORDE_BASE . '/locale', Horde_Nls::getCharset());
- Horde_String::setDefaultCharset(Horde_Nls::getCharset());
-
- /* Check for caching availability. Using cache while not authenticated
- * isn't possible because, although storage is possible, retrieval
- * isn't since there is no MD5 sum in the session to use to build
- * the cache IDs. */
- if (Horde_Auth::getAuth()) {
- try {
- $this->_cacheob = Horde_Cache::singleton($conf['cache']['driver'], Horde::getDriverConfig('cache', $conf['cache']['driver']));
- } catch (Horde_Exception $e) {}
- }
-
- $this->_regmtime = max(filemtime(HORDE_BASE . '/config/registry.php'),
- filemtime(HORDE_BASE . '/config/registry.d'));
-
- $vhost = null;
- if (!empty($conf['vhosts'])) {
- $vhost = HORDE_BASE . '/config/registry-' . $conf['server']['name'] . '.php';
- if (file_exists($vhost)) {
- $this->_regmtime = max($this->_regmtime, filemtime($vhost));
- } else {
- $vhost = null;
- }
- }
-
- /* Always need to load applications information. */
- $this->_loadApplicationsCache($vhost);
-
- /* Stop system if Horde is inactive. */
- if ($this->applications['horde']['status'] == 'inactive') {
- Horde::fatal(_("This system is currently deactivated."), __FILE__, __LINE__);
- }
-
- /* Create the global Perms object. */
- $GLOBALS['perms'] = &Perms::singleton();
-
- /* Attach javascript notification listener. */
- $notification = &Horde_Notification::singleton();
- $notification->attach('javascript');
- }
-
- /**
- * Stores cacheable member variables in the session at shutdown.
- */
- public function __destruct()
- {
- /* Register access key logger for translators. */
- if (!empty($GLOBALS['conf']['log_accesskeys'])) {
- Horde::getAccessKey(null, null, true);
- }
-
- /* Register memory tracker if logging in debug mode. */
- if (!empty($GLOBALS['conf']['log']['enabled']) &&
- ($GLOBALS['conf']['log']['priority'] == PEAR_LOG_DEBUG) &&
- function_exists('memory_get_peak_usage')) {
- Horde::logMessage('Max memory usage: ' . memory_get_peak_usage(true) . ' bytes', __FILE__, __LINE__, PEAR_LOG_DEBUG);
- }
- }
-
- /**
- * TODO
- */
- public function __get($api)
- {
- if (in_array($api, $this->listAPIs())) {
- return new Horde_Registry_Caller($this, $api);
- }
- }
-
- /**
- * Clone should never be called on this object. If it is, die.
- */
- public function __clone()
- {
- Horde::fatal('Horde_Registry objects should never be cloned.', __FILE__, __LINE__);
- }
-
- /**
- * Clear the registry cache.
- */
- public function clearCache()
- {
- unset($_SESSION['_registry']);
- $this->_saveCacheVar('apicache', true);
- $this->_saveCacheVar('appcache', true);
- }
-
- /**
- * Fills the registry's application cache with application information.
- *
- * @param string $vhost TODO
- */
- protected function _loadApplicationsCache($vhost)
- {
- /* First, try to load from cache. */
- if ($this->_loadCacheVar('appcache')) {
- $this->applications = $this->_cache['appcache'][0];
- $this->_cache['interfaces'] = $this->_cache['appcache'][1];
- return;
- }
-
- $this->_cache['interfaces'] = array();
-
- /* Read the registry configuration files. */
- require HORDE_BASE . '/config/registry.php';
- $files = glob(HORDE_BASE . '/config/registry.d/*.php');
- if ($files) {
- foreach ($files as $r) {
- include $r;
- }
- }
-
- if ($vhost) {
- include $vhost;
- }
-
- /* Scan for all APIs provided by each app, and set other common
- * defaults like templates and graphics. */
- foreach (array_keys($this->applications) as $appName) {
- $app = &$this->applications[$appName];
- if ($app['status'] == 'heading') {
- continue;
- }
-
- if (isset($app['fileroot']) && !file_exists($app['fileroot'])) {
- $app['status'] = 'inactive';
- }
-
- if (($app['status'] != 'inactive') &&
- isset($app['provides']) &&
- (($app['status'] != 'admin') || Horde_Auth::isAdmin())) {
- if (is_array($app['provides'])) {
- foreach ($app['provides'] as $interface) {
- $this->_cache['interfaces'][$interface] = $appName;
- }
- } else {
- $this->_cache['interfaces'][$app['provides']] = $appName;
- }
- }
-
- if (!isset($app['templates']) && isset($app['fileroot'])) {
- $app['templates'] = $app['fileroot'] . '/templates';
- }
- if (!isset($app['jsuri']) && isset($app['webroot'])) {
- $app['jsuri'] = $app['webroot'] . '/js';
- }
- if (!isset($app['jsfs']) && isset($app['fileroot'])) {
- $app['jsfs'] = $app['fileroot'] . '/js';
- }
- if (!isset($app['themesuri']) && isset($app['webroot'])) {
- $app['themesuri'] = $app['webroot'] . '/themes';
- }
- if (!isset($app['themesfs']) && isset($app['fileroot'])) {
- $app['themesfs'] = $app['fileroot'] . '/themes';
- }
- }
-
- $this->_cache['appcache'] = array(
- // Index 0
- $this->applications,
- // Index 1
- $this->_cache['interfaces']
- );
- $this->_saveCacheVar('appcache');
- }
-
- /**
- * Fills the registry's API cache with the available services and types.
- */
- protected function _loadApiCache()
- {
- /* First, try to load from cache. */
- if ($this->_loadCacheVar('apicache')) {
- $this->_cache['api'] = $this->_cache['apicache'][0];
- $this->_cache['type'] = $this->_cache['apicache'][1];
- return;
- }
-
- /* Generate api/type cache. */
- $status = array('active', 'notoolbar', 'hidden');
- if (Horde_Auth::isAdmin()) {
- $status[] = 'admin';
- }
-
- $this->_cache['api'] = $this->_cache['type'] = array();
-
- $apps = $this->listApps($status);
- foreach ($apps as $app) {
- $_services = $_types = null;
- $api = $this->get('fileroot', $app) . '/lib/api.php';
- if (is_readable($api)) {
- include_once $api;
- }
- $this->_cache['api'][$app] = $_services;
- if (!is_null($_types)) {
- foreach ($_types as $type => $params) {
- /* Prefix non-Horde types with the application name. */
- $prefix = ($app == 'horde') ? '' : "${app}_";
- $this->_cache['type'][$prefix . $type] = $params;
- }
- }
- }
-
- $this->_cache['apicache'] = array(
- // Index 0
- $this->_cache['api'],
- // Index 1
- $this->_cache['type']
- );
- $this->_saveCacheVar('apicache');
- }
-
- /**
- * Return a list of the installed and registered applications.
- *
- * @param array $filter An array of the statuses that should be
- * returned. Defaults to non-hidden.
- * @param boolean $assoc Associative array with app names as keys.
- * @param integer $perms The permission level to check for in the list.
- *
- * @return array List of apps registered with Horde. If no
- * applications are defined returns an empty array.
- */
- public function listApps($filter = null, $assoc = false,
- $perms = PERMS_SHOW)
- {
- $apps = array();
- $ahandler = defined('AUTH_HANDLER');
- if (is_null($filter)) {
- $filter = array('notoolbar', 'active');
- }
-
- foreach ($this->applications as $app => $params) {
- if (in_array($params['status'], $filter) &&
- ($ahandler || $this->hasPermission($app, $perms))) {
- $apps[$app] = $app;
- }
- }
-
- return $assoc ? $apps : array_values($apps);
- }
-
- /**
- * Returns all available registry APIs.
- *
- * @return array The API list.
- */
- public function listAPIs()
- {
- if (empty($this->_apis)) {
- foreach (array_keys($this->_cache['interfaces']) as $interface) {
- list($api,) = explode('/', $interface, 2);
- $this->_apis[$api] = true;
- }
- }
-
- return array_keys($this->_apis);
- }
-
- /**
- * Returns all of the available registry methods, or alternately
- * only those for a specified API.
- *
- * @param string $api Defines the API for which the methods shall be
- * returned.
- *
- * @return array The method list.
- */
- public function listMethods($api = null)
- {
- $methods = array();
-
- $this->_loadApiCache();
-
- foreach (array_keys($this->applications) as $app) {
- if (isset($this->applications[$app]['provides'])) {
- $provides = $this->applications[$app]['provides'];
- if (!is_array($provides)) {
- $provides = array($provides);
- }
- foreach ($provides as $method) {
- if (strpos($method, '/') !== false) {
- if (is_null($api) ||
- (substr($method, 0, strlen($api)) == $api)) {
- $methods[$method] = true;
- }
- } elseif (is_null($api) || ($method == $api)) {
- if (isset($this->_cache['api'][$app])) {
- foreach (array_keys($this->_cache['api'][$app]) as $service) {
- $methods[$method . '/' . $service] = true;
- }
- }
- }
- }
- }
- }
-
- return array_keys($methods);
- }
-
- /**
- * Returns all of the available registry data types.
- *
- * @return array The data type list.
- */
- public function listTypes()
- {
- $this->_loadApiCache();
- return $this->_cache['type'];
- }
-
- /**
- * Returns a method's signature.
- *
- * @param string $method The full name of the method to check for.
- *
- * @return array A two dimensional array. The first element contains an
- * array with the parameter names, the second one the return
- * type.
- */
- public function getSignature($method)
- {
- if (!($app = $this->hasMethod($method))) {
- return false;
- }
-
- $this->_loadApiCache();
-
- list(,$function) = explode('/', $method, 2);
- if (!empty($function) &&
- isset($this->_cache['api'][$app][$function]['type']) &&
- isset($this->_cache['api'][$app][$function]['args'])) {
- return array($this->_cache['api'][$app][$function]['args'], $this->_cache['api'][$app][$function]['type']);
- }
-
- return false;
- }
-
- /**
- * Determine if an interface is implemented by an active application.
- *
- * @param string $interface The interface to check for.
- *
- * @return mixed The application implementing $interface if we have it,
- * false if the interface is not implemented.
- */
- public function hasInterface($interface)
- {
- return !empty($this->_cache['interfaces'][$interface]) ?
- $this->_cache['interfaces'][$interface] :
- false;
- }
-
- /**
- * Determine if a method has been registered with the registry.
- *
- * @param string $method The full name of the method to check for.
- * @param string $app Only check this application.
- *
- * @return mixed The application implementing $method if we have it,
- * false if the method doesn't exist.
- */
- public function hasMethod($method, $app = null)
- {
- if (is_null($app)) {
- list($interface, $call) = explode('/', $method, 2);
- if (!empty($this->_cache['interfaces'][$method])) {
- $app = $this->_cache['interfaces'][$method];
- } elseif (!empty($this->_cache['interfaces'][$interface])) {
- $app = $this->_cache['interfaces'][$interface];
- } else {
- return false;
- }
- } else {
- $call = $method;
- }
-
- $this->_loadApiCache();
-
- return empty($this->_cache['api'][$app][$call]) ? false : $app;
- }
-
- /**
- * Return the hook corresponding to the default package that
- * provides the functionality requested by the $method
- * parameter. $method is a string consisting of
- * "packagetype/methodname".
- *
- * @param string $method The method to call.
- * @param array $args Arguments to the method.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function call($method, $args = array())
- {
- list($interface, $call) = explode('/', $method, 2);
-
- if (!empty($this->_cache['interfaces'][$method])) {
- $app = $this->_cache['interfaces'][$method];
- } elseif (!empty($this->_cache['interfaces'][$interface])) {
- $app = $this->_cache['interfaces'][$interface];
- } else {
- return PEAR::raiseError('The method "' . $method . '" is not defined in the Horde Registry.');
- }
-
- return $this->callByPackage($app, $call, $args);
- }
-
- /**
- * Output the hook corresponding to the specific package named.
- *
- * @param string $app The application being called.
- * @param string $call The method to call.
- * @param array $args Arguments to the method.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function callByPackage($app, $call, $args = array())
- {
- /* Note: calling hasMethod() makes sure that we've cached
- * $app's services and included the API file, so we don't try
- * to do it again explicitly in this method. */
- if (!$this->hasMethod($call, $app)) {
- return PEAR::raiseError(sprintf('The method "%s" is not defined in the API for %s.', $call, $app));
- }
-
- /* Load the API now. */
- $api = $this->get('fileroot', $app) . '/lib/api.php';
- if (is_readable($api)) {
- include_once $api;
- }
-
- /* Make sure that the function actually exists. */
- $function = '_' . $app . '_' . str_replace('/', '_', $call);
- if (!function_exists($function)) {
- return PEAR::raiseError('The function implementing ' . $call . ' (' . $function . ') is not defined in ' . $app . '\'s API.');
- }
-
- $checkPerms = isset($this->_cache['api'][$app][$call]['checkperms'])
- ? $this->_cache['api'][$app][$call]['checkperms']
- : true;
-
- /* Switch application contexts now, if necessary, before
- * including any files which might do it for us. Return an
- * error immediately if pushApp() fails. */
- $pushed = $this->pushApp($app, $checkPerms);
- if (is_a($pushed, 'PEAR_Error')) {
- return $pushed;
- }
-
- $res = call_user_func_array($function, $args);
-
- /* If we changed application context in the course of this
- * call, undo that change now. */
- if ($pushed === true) {
- $this->popApp();
- }
-
- return $res;
- }
-
- /**
- * Return the hook corresponding to the default package that
- * provides the functionality requested by the $method
- * parameter. $method is a string consisting of
- * "packagetype/methodname".
- *
- * @param string $method The method to link to.
- * @param array $args Arguments to the method.
- * @param mixed $extra Extra, non-standard arguments to the method.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function link($method, $args = array(), $extra = '')
- {
- list($interface, $call) = explode('/', $method, 2);
-
- if (!empty($this->_cache['interfaces'][$method])) {
- $app = $this->_cache['interfaces'][$method];
- } elseif (!empty($this->_cache['interfaces'][$interface])) {
- $app = $this->_cache['interfaces'][$interface];
- } else {
- return PEAR::raiseError('The method "' . $method . '" is not defined in the Horde Registry.');
- }
-
- return $this->linkByPackage($app, $call, $args, $extra);
- }
-
- /**
- * Output the hook corresponding to the specific package named.
- *
- * @param string $app The application being called.
- * @param string $call The method to link to.
- * @param array $args Arguments to the method.
- * @param mixed $extra Extra, non-standard arguments to the method.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function linkByPackage($app, $call, $args = array(), $extra = '')
- {
- /* Note: calling hasMethod makes sure that we've cached $app's
- * services and included the API file, so we don't try to do
- * it it again explicitly in this method. */
- if (!$this->hasMethod($call, $app)) {
- return PEAR::raiseError('The method "' . $call . '" is not defined in ' . $app . '\'s API.');
- }
-
- /* Make sure the link is defined. */
- $this->_loadApiCache();
- if (empty($this->_cache['api'][$app][$call]['link'])) {
- return PEAR::raiseError('The link ' . $call . ' is not defined in ' . $app . '\'s API.');
- }
-
- /* Initial link value. */
- $link = $this->_cache['api'][$app][$call]['link'];
-
- /* Fill in html-encoded arguments. */
- foreach ($args as $key => $val) {
- $link = str_replace('%' . $key . '%', htmlentities($val), $link);
- }
- if (isset($this->applications[$app]['webroot'])) {
- $link = str_replace('%application%', $this->get('webroot', $app), $link);
- }
-
- /* Replace htmlencoded arguments that haven't been specified with
- an empty string (this is where the default would be substituted
- in a stricter registry implementation). */
- $link = preg_replace('|%.+%|U', '', $link);
-
- /* Fill in urlencoded arguments. */
- foreach ($args as $key => $val) {
- $link = str_replace('|' . Horde_String::lower($key) . '|', urlencode($val), $link);
- }
-
- /* Append any extra, non-standard arguments. */
- if (is_array($extra)) {
- $extra_args = '';
- foreach ($extra as $key => $val) {
- $extra_args .= '&' . urlencode($key) . '=' . urlencode($val);
- }
- } else {
- $extra_args = $extra;
- }
- $link = str_replace('|extra|', $extra_args, $link);
-
- /* Replace html-encoded arguments that haven't been specified with
- an empty string (this is where the default would be substituted
- in a stricter registry implementation). */
- $link = preg_replace('|\|.+\||U', '', $link);
-
- return $link;
- }
-
- /**
- * Replace any %application% strings with the filesystem path to the
- * application.
- *
- * @param string $path The application string.
- * @param string $app The application being called.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function applicationFilePath($path, $app = null)
- {
- if (is_null($app)) {
- $app = $this->getApp();
- }
-
- if (!isset($this->applications[$app])) {
- return PEAR::raiseError(sprintf(_("\"%s\" is not configured in the Horde Registry."), $app));
- }
-
- return str_replace('%application%', $this->applications[$app]['fileroot'], $path);
- }
-
- /**
- * Replace any %application% strings with the web path to the application.
- *
- * @param string $path The application string.
- * @param string $app The application being called.
- *
- * @return TODO
- * Returns PEAR_Error on error.
- */
- public function applicationWebPath($path, $app = null)
- {
- if (!isset($app)) {
- $app = $this->getApp();
- }
-
- return str_replace('%application%', $this->applications[$app]['webroot'], $path);
- }
-
- /**
- * Set the current application, adding it to the top of the Horde
- * application stack. If this is the first application to be
- * pushed, retrieve session information as well.
- *
- * pushApp() also reads the application's configuration file and
- * sets up its global $conf hash.
- *
- * @param string $app The name of the application to push.
- * @param boolean $checkPerms Make sure that the current user has
- * permissions to the application being loaded
- * Defaults to true. Should ONLY be disabled
- * by system scripts (cron jobs, etc.) and
- * scripts that handle login.
- *
- * @return boolean Whether or not the _appStack was modified.
- * @throws Horde_Exception
- */
- public function pushApp($app, $checkPerms = true)
- {
- if ($app == $this->getApp()) {
- return false;
- }
-
- /* Bail out if application is not present or inactive. */
- if (!isset($this->applications[$app]) ||
- $this->applications[$app]['status'] == 'inactive' ||
- ($this->applications[$app]['status'] == 'admin' && !Horde_Auth::isAdmin())) {
- Horde::fatal($app . ' is not activated', __FILE__, __LINE__);
- }
-
- /* If permissions checking is requested, return an error if the
- * current user does not have read perms to the application being
- * loaded. We allow access:
- *
- * - To all admins.
- * - To all authenticated users if no permission is set on $app.
- * - To anyone who is allowed by an explicit ACL on $app. */
- if ($checkPerms && !$this->hasPermission($app)) {
- Horde::logMessage(sprintf('%s does not have READ permission for %s', Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $app), __FILE__, __LINE__, PEAR_LOG_DEBUG);
- return PEAR::raiseError(sprintf(_('%s is not authorised for %s.'), Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $this->applications[$app]['name']), 'permission_denied');
- }
-
- /* Set up autoload paths for the current application. This needs to
- * be done here because it is possible to try to load app-specific
- * libraries from other applications. */
- $app_lib = $this->get('fileroot', $app) . '/lib';
- Horde_Autoloader::addClassPath($app_lib);
- Horde_Autoloader::addClassPattern('/^' . $app . '_/i', $app_lib);
-
- /* Chicken and egg problem: the language environment has to be loaded
- * before loading the configuration file, because it might contain
- * gettext strings. Though the preferences can specify a different
- * language for this app, the have to be loaded after the
- * configuration, because they rely on configuration settings. So try
- * with the current language, and reset the language later. */
- Horde_Nls::setLanguageEnvironment($GLOBALS['language'], $app);
-
- /* Import this application's configuration values. */
- $this->importConfig($app);
-
- /* Load preferences after the configuration has been loaded to make
- * sure the prefs file has all the information it needs. */
- $this->loadPrefs($app);
-
- /* Reset the language in case there is a different one selected in the
- * preferences. */
- $language = '';
- if (isset($GLOBALS['prefs'])) {
- $language = $GLOBALS['prefs']->getValue('language');
- if ($language != $GLOBALS['language']) {
- Horde_Nls::setLanguageEnvironment($language, $app);
- }
- }
-
- /* Once we know everything succeeded and is in a consistent state
- * again, push the new application onto the stack. */
- $this->_appStack[] = $app;
-
- /* Call post-push hook. */
- Horde::callHook('_horde_hook_post_pushapp', array($app), 'horde', null);
-
- return true;
- }
-
- /**
- * Remove the current app from the application stack, setting the current
- * app to whichever app was current before this one took over.
- *
- * @return string The name of the application that was popped.
- * @throws Horde_Exception
- */
- public function popApp()
- {
- /* Pop the current application off of the stack. */
- $previous = array_pop($this->_appStack);
-
- /* Import the new active application's configuration values
- * and set the gettext domain and the preferred language. */
- $app = $this->getApp();
- if ($app) {
- $this->importConfig($app);
- $this->loadPrefs($app);
- $language = $GLOBALS['prefs']->getValue('language');
- Horde_Nls::setLanguageEnvironment($language, $app);
- }
-
- return $previous;
- }
-
- /**
- * Return the current application - the app at the top of the application
- * stack.
- *
- * @return string The current application.
- */
- public function getApp()
- {
- return end($this->_appStack);
- }
-
- /**
- * Check permissions on an application.
- *
- * @param string $app The name of the application
- * @param integer $perms The permission level to check for.
- *
- * @return boolean Whether access is allowed.
- */
- public function hasPermission($app, $perms = PERMS_READ)
- {
- return Horde_Auth::isAdmin() ||
- ($GLOBALS['perms']->exists($app)
- ? $GLOBALS['perms']->hasPermission($app, Horde_Auth::getAuth(), $perms)
- : (bool)Horde_Auth::getAuth());
- }
-
- /**
- * Reads the configuration values for the given application and imports
- * them into the global $conf variable.
- *
- * @param string $app The name of the application.
- *
- * @throws Horde_Exception
- */
- public function importConfig($app)
- {
- if (($app != 'horde') &&
- !$this->_loadCacheVar('conf-' . $app)) {
- $this->_cache['conf-' . $app] = Horde_Array::array_merge_recursive_overwrite($this->_cache['conf-horde'], Horde::loadConfiguration('conf.php', 'conf', $app));
- $this->_saveCacheVar('conf-' . $app);
- }
-
- $GLOBALS['conf'] = &$this->_cache['conf-' . $app];
- }
-
- /**
- * Loads the preferences for the current user for the current application
- * and imports them into the global $prefs variable.
- *
- * @param string $app The name of the application.
- */
- public function loadPrefs($app = null)
- {
- require_once 'Horde/Prefs.php';
-
- if (is_null($app)) {
- $app = $this->getApp();
- }
-
- /* If there is no logged in user, return an empty Prefs::
- * object with just default preferences. */
- if (!Horde_Auth::getAuth()) {
- $GLOBALS['prefs'] = &Prefs::factory('session', $app, '', '', null, false);
- } else {
- if (!isset($GLOBALS['prefs']) || $GLOBALS['prefs']->getUser() != Horde_Auth::getAuth()) {
- $GLOBALS['prefs'] = &Prefs::factory($GLOBALS['conf']['prefs']['driver'], $app,
- Horde_Auth::getAuth(), Horde_Auth::getCredential('password'));
- } else {
- $GLOBALS['prefs']->retrieve($app);
- }
- }
- }
-
- /**
- * Unload preferences from an application or (if no application is
- * specified) from ALL applications. Useful when a user has logged
- * out but you need to continue on the same page, etc.
- *
- * After unloading, if there is an application on the app stack to
- * load preferences from, then we reload a fresh set.
- *
- * @param string $app The application to unload prefrences for. If null,
- * ALL preferences are reset.
- */
- public function unloadPrefs($app = null)
- {
- // TODO: $app not being used?
- if ($this->getApp()) {
- $this->loadPrefs();
- }
- }
-
- /**
- * Return the requested configuration parameter for the specified
- * application. If no application is specified, the value of
- * the current application is used. However, if the parameter is not
- * present for that application, the Horde-wide value is used instead.
- * If that is not present, we return null.
- *
- * @param string $parameter The configuration value to retrieve.
- * @param string $app The application to get the value for.
- *
- * @return string The requested parameter, or null if it is not set.
- */
- public function get($parameter, $app = null)
- {
- if (is_null($app)) {
- $app = $this->getApp();
- }
-
- if (isset($this->applications[$app][$parameter])) {
- $pval = $this->applications[$app][$parameter];
- } else {
- $pval = ($parameter == 'icon')
- ? $this->getImageDir($app) . '/' . $app . '.png'
- : (isset($this->applications['horde'][$parameter]) ? $this->applications['horde'][$parameter] : null);
- }
-
- return ($parameter == 'name')
- ? _($pval)
- : $pval;
- }
-
- /**
- * Function to work out an application's graphics URI, optionally taking
- * into account any themes directories that may be set up.
- *
- * @param string $app The application for which to get the image
- * directory. If blank will default to current
- * application.
- * @param boolean $usetheme Take into account any theme directory?
- *
- * @return string The image directory uri path.
- */
- public function getImageDir($app = null, $usetheme = true)
- {
- if (empty($app)) {
- $app = $this->getApp();
- }
-
- if ($this->get('status', $app) == 'heading') {
- $app = 'horde';
- }
-
- $sig = strval($app . '|' . $usetheme);
-
- if (isset($this->_imgDir[$sig])) {
- return $this->_imgDir[$sig];
- }
-
- /* This is the default location for the graphics. */
- $this->_imgDir[$sig] = $this->get('themesuri', $app) . '/graphics';
-
- /* Figure out if this is going to be overridden by any theme
- * settings. */
- if ($usetheme &&
- isset($GLOBALS['prefs']) &&
- ($theme = $GLOBALS['prefs']->getValue('theme'))) {
- /* Since theme information is so limited, store directly in the
- * session. */
- if (!isset($_SESSION['_registry']['theme'][$theme][$app])) {
- $_SESSION['_registry']['theme'][$theme][$app] = file_exists($this->get('themesfs', $app) . '/' . $theme . '/themed_graphics');
- }
-
- if ($_SESSION['_registry']['theme'][$theme][$app]) {
- $this->_imgDir[$sig] = $this->get('themesuri', $app) . '/' . $theme . '/graphics';
- }
- }
-
- return $this->_imgDir[$sig];
- }
-
- /**
- * Query the initial page for an application - the webroot, if there is no
- * initial_page set, and the initial_page, if it is set.
- *
- * @param string $app The name of the application.
- *
- * @return string URL pointing to the inital page of the application.
- * Returns PEAR_Error on error.
- */
- public function getInitialPage($app = null)
- {
- if (is_null($app)) {
- $app = $this->getApp();
- }
-
- return isset($this->applications[$app])
- ? $this->applications[$app]['webroot'] . '/' . (isset($this->applications[$app]['initial_page']) ? $this->applications[$app]['initial_page'] : '')
- : PEAR::raiseError(sprintf(_("\"%s\" is not configured in the Horde Registry."), $app));
- }
-
- /**
- * Saves a cache variable.
- *
- * @param string $name Cache variable name.
- * @param boolean $expire Expire the entry?
- */
- protected function _saveCacheVar($name, $expire = false)
- {
- if ($this->_cacheob) {
- if ($expire) {
- if ($id = $this->_getCacheId($name)) {
- $this->_cacheob->expire($id);
- }
- } else {
- $data = serialize($this->_cache[$name]);
- $_SESSION['_registry']['md5'][$name] = $md5sum = hash('md5', $data);
- $id = $this->_getCacheId($name, false) . '|' . $md5sum;
- $this->_cacheob->set($id, $data, 86400);
- Horde::logMessage('Horde_Registry: stored ' . $name . ' with cache ID ' . $id, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- }
- }
- }
-
- /**
- * Retrieves a cache variable.
- *
- * @param string $name Cache variable name.
- *
- * @return boolean True if value loaded from cache.
- */
- protected function _loadCacheVar($name)
- {
- if (isset($this->_cache[$name])) {
- return true;
- }
-
- if ($this->_cacheob &&
- ($id = $this->_getCacheId($name))) {
- $res = $this->_cacheob->get($id, 86400);
- if ($res !== false) {
- $this->_cache[$name] = unserialize($res);
- Horde::logMessage('Horde_Registry: retrieved ' . $name . ' with cache ID ' . $id, __FILE__, __LINE__, PEAR_LOG_DEBUG);
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * Get the cache storage ID for a particular cache name.
- *
- * @param string $name Cache variable name.
- * @param string $md5 Append MD5 value?
- *
- * @return mixed The cache ID or false if cache entry doesn't exist in
- * the session.
- */
- protected function _getCacheId($name, $md5 = true)
- {
- $id = 'horde_registry_' . $name . '|' . $this->_regmtime;
-
- if (!$md5) {
- return $id;
- } elseif (isset($_SESSION['_registry']['md5'][$name])) {
- return $id . '|' . $_SESSION['_registry']['md5'][$name];
- } else {
- return false;
- }
- }
-
-}
+++ /dev/null
-<?php
-/**
- * TODO
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @package Core
- */
-class Horde_Registry_Caller
-{
- /**
- * TODO
- */
- protected $registry;
-
- /**
- * TODO
- */
- protected $api;
-
- /**
- * TODO
- */
- public function __construct($registry, $api)
- {
- $this->registry = $registry;
- $this->api = $api;
- }
-
- /**
- * TODO
- */
- public function __call($method, $args)
- {
- return $this->registry->call($this->api . '/' . $method, $args);
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Class to make an "official" Horde or application release.
- *
- * Copyright 1999 Mike Hardy
- * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Mike Hardy
- * @author Jan Schneider <jan@horde.org>
- * @package Core
- */
-class Horde_Release
-{
- /**
- * Default options.
- *
- * @var array
- */
- protected $_options = array(
- 'test' => false,
- 'nocommit' => false,
- 'noftp' => false,
- 'noannounce' => false,
- 'nofreshmeat' => false,
- 'nowhups' => false,
- );
-
- /**
- * Version number of release.
- *
- * @var string
- */
- protected $_sourceVersionString;
-
- /**
- * Version number of previous release.
- *
- * @var string
- */
- protected $_oldSourceVersionString;
-
- /**
- * Version number of next release.
- *
- * @var string
- */
- protected $_newSourceVersionString;
-
- /**
- * Version number of next release for docs/CHANGES.
- *
- * @var string
- */
- protected $_newSourceVersionStringPlain;
-
- /**
- * Major version number of Horde compatible to this release.
- *
- * @var string
- */
- protected $_hordeVersionString;
-
- /**
- * Major version number of Horde compatible to the previous release.
- *
- * @var string
- */
- protected $_oldHordeVersionString;
-
- /**
- * CVS tag of release.
- *
- * @var string
- */
- protected $_tagVersionString;
-
- /**
- * CVS tag of previous release.
- *
- * @var string
- */
- protected $_oldTagVersionString;
-
- /**
- * Revision number of CHANGES file.
- *
- * @var string
- */
- protected $_changelogVersion;
-
- /**
- * Revision number of previous CHANGES file.
- *
- * @var string
- */
- protected $_oldChangelogVersion;
-
- /**
- * Version string to use in Whups
- *
- * @var string
- */
- protected $_ticketVersion;
-
- /**
- * Version description to use in Whups
- *
- * @var string
- */
- protected $_ticketVersionDesc = '';
-
- /**
- * Directory name of unpacked tarball.
- *
- * @var string
- */
- protected $_directoryName;
-
- /**
- * Directory name of unpacked previous tarball.
- *
- * @var string
- */
- protected $_oldDirectoryName;
-
- /**
- * Filename of the tarball.
- *
- * @var string
- */
- protected $_tarballName;
-
- /**
- * MD5 sum of the tarball.
- *
- * @var string
- */
- protected $_tarballMD5;
-
- /**
- * Whether or not to create a patch file.
- *
- * @var boolean
- */
- protected $_makeDiff = false;
-
- /**
- * The list of binary diffs.
- *
- * @var array
- */
- protected $_binaryDiffs = array();
-
- /**
- * Whether or not we have an old version to compare against.
- *
- * @var boolean
- */
- protected $_oldVersion = false;
-
- /**
- * Filename of the gzip'ed patch file (without .gz extension).
- *
- * @var string
- */
- protected $_patchName;
-
- /**
- * MD5 sum of the patch file.
- *
- * @var string
- */
- protected $_patchMD5;
-
- /**
- * Whether or not this is a final release version.
- *
- * @var boolean
- */
- protected $_latest = true;
-
- /**
- * Load the configuration
- */
- public function __construct($options = array())
- {
- $this->_options = array_merge($this->_options, $options);
- $cvsroot = getenv('CVSROOT');
- if (empty($cvsroot)) {
- putenv('CVSROOT=:ext:' . $this->_options['horde']['user'] . '@cvs.horde.org:/repository');
- }
- print 'CVSROOT ' . getenv('CVSROOT') . "\n";
- if (!empty($this->_options['cvs']['cvs_rsh'])) {
- putenv('CVS_RSH=' . $this->_options['cvs']['cvs_rsh']);
- }
- print 'CVS_RSH ' . getenv('CVS_RSH') . "\n";
- }
-
- /**
- * Delete the directory given as an argument
- */
- public function deleteDirectory($directory)
- {
- print "Deleting directory $directory\n";
- system("sudo rm -rf $directory");
- }
-
- /**
- * tar and gzip the directory given as an argument
- */
- public function makeTarball()
- {
- print "Setting owner/group to 0/0\n";
- system("sudo chown -R 0:0 $this->_directoryName");
-
- print "Making tarball\n";
- $this->_tarballName = $this->_directoryName . '.tar.gz';
- if (file_exists($this->_tarballName)) {
- unlink($this->_tarballName);
- }
- system("tar -zcf $this->_tarballName $this->_directoryName");
- exec($this->_options['md5'] . ' ' . $this->_tarballName, $this->_tarballMD5);
- }
-
- /**
- * Label all of the source here with the new label given as an argument
- */
- public function tagSource($directory = null, $version = null)
- {
- if (empty($directory)) {
- $directory = $this->_directoryName;
- }
- if (empty($version)) {
- $version = $this->_tagVersionString;
- }
- if (!$this->_options['nocommit']) {
- print "Tagging source in $directory with tag $version\n";
- system("cd $directory;cvs tag -F $version > /dev/null 2>&1");
- } else {
- print "NOT tagging source in $directory (would have been tag $version)\n";
- }
- }
-
- /**
- * Make a diff of the two directories given as arguments
- */
- public function diff()
- {
- $this->_patchName = 'patch-' . $this->_oldDirectoryName . str_replace($this->_options['module'], '', $this->_directoryName);
- print "Making diff between $this->_oldDirectoryName and $this->_directoryName\n";
- system("diff -uNr $this->_oldDirectoryName $this->_directoryName > $this->_patchName");
-
- // Search for binary diffs
- $this->_binaryDiffs = array();
- $handle = fopen($this->_patchName, 'r');
- if ($handle) {
- while (!feof($handle)) {
- // GNU diff reports binary diffs as the following:
- // Binary files ./locale/de_DE/LC_MESSAGES/imp.mo and ../../horde/imp/locale/de_DE/LC_MESSAGES/imp.mo differ
- if (preg_match("/^Binary files (.+) and (.+) differ$/i", rtrim(fgets($handle)), $matches)) {
- // [1] = oldname, [2] = newname
- $this->_binaryDiffs[] = ltrim(str_replace($this->_oldDirectoryName . '/', '', $matches[1]));
- }
- }
- fclose($handle);
- }
- system("gzip -9f $this->_patchName");
- exec($this->_options['md5'] . ' ' . $this->_patchName . '.gz', $this->_patchMD5);
- }
-
- /**
- * Change the version file for the module in the directory specified to
- * the version specified
- */
- public function updateVersionFile($directory, $version_string)
- {
- $module = $this->_options['module'];
- $all_caps_module = strtoupper($module);
- print "Updating version file for $module\n";
-
- // construct the filenames
- $filename_only = 'version.php';
- $filename = $directory . '/lib/' . $filename_only;
- $newfilename = $filename . '.new';
-
- $oldfp = fopen($filename, 'r');
- $newfp = fopen($newfilename, 'w');
- while ($line = fgets($oldfp)) {
- if (strstr($line, 'VERSION')) {
- fwrite($newfp, "<?php define('{$all_caps_module}_VERSION', '$version_string') ?>\n");
- } else {
- fwrite($newfp, $line);
- }
- }
- fclose($oldfp);
- fclose($newfp);
-
- system("mv -f $newfilename $filename");
- if (!$this->_options['nocommit']) {
- system("cd $directory/lib/; cvs commit -f -m \"Tarball script: building new $module release - $version_string\" $filename_only > /dev/null 2>&1");
- }
- }
-
- /**
- * Update the CHANGES file with the new version number
- */
- public function updateSentinel()
- {
- $module = $this->_options['module'];
- $all_caps_module = strtoupper($module);
- print "Updating CHANGES file for $module\n";
-
- // construct the filenames
- $filename_only = 'CHANGES';
- $filename = $this->_directoryName . '/docs/' . $filename_only;
- $newfilename = $filename . '.new';
-
- $version = 'v' . substr($this->_newSourceVersionStringPlain, 0, strpos($this->_newSourceVersionString, '-'));
-
- $oldfp = fopen($filename, 'r');
- $newfp = fopen($newfilename, 'w');
- fwrite($newfp, str_repeat('-', strlen($version)) . "\n$version\n" .
- str_repeat('-', strlen($version)) . "\n\n\n\n\n");
- while ($line = fgets($oldfp)) {
- fwrite($newfp, $line);
- }
- fclose($oldfp);
- fclose($newfp);
-
- system("mv -f $newfilename $filename");
- if (!$this->_options['nocommit']) {
- system("cd {$this->_directoryName}/docs/; cvs commit -f -m \"Tarball script: building new $module release - {$this->_newSourceVersionString}\" $filename_only > /dev/null 2>&1");
- }
- }
-
- /**
- * get and save the revision number of the CHANGES file
- */
- public function saveChangelog($old = false, $directory = null)
- {
- if (empty($directory)) {
- if ($old) {
- $directory = './' . $this->_oldDirectoryName . '/docs';
- } else {
- $directory = './' . $this->_directoryName . '/docs';
- }
- }
- if (!$old) {
- include "$directory/RELEASE_NOTES";
- if (strlen(htmlspecialchars($this->notes['fm']['changes'])) > 600) {
- print "WARNING: freshmeat release notes are longer than 600 characters!\n";
- }
- }
- exec("cd $directory; cvs status CHANGES", $output);
- foreach ($output as $line) {
- if (preg_match('/Repository revision:\s+([\d.]+)/', $line, $matches)) {
- if ($old) {
- $this->_oldChangelogVersion = $matches[1];
- } else {
- $this->_changelogVersion = $matches[1];
- }
- break;
- }
- }
- }
-
- /**
- * work through the source directory given, cleaning things up by removing
- * directories and files we don't want in the tarball
- */
- public function cleanDirectories($directory)
- {
- print "Cleaning source tree\n";
- $directories = explode("\n", `find $directory -type d \\( -name CVS -o -name packaging -o -name framework \\) -print | sort -r`);
- foreach ($directories as $dir) {
- system("rm -rf $dir");
- }
- $cvsignores = explode("\n", `find $directory -name .cvsignore -print`);
- foreach ($cvsignores as $file) {
- if (!empty($file)) {
- unlink($file);
- }
- }
- }
-
- /**
- * Check out the tag we've been given to work with and move it to the
- * directory name given
- */
- public function checkOutTag($mod_version, $directory, $module = null)
- {
- if (empty($module)) {
- $module = $this->_options['module'];
- }
-
- if (@is_dir($module)) {
- system("rm -rf $module");
- }
-
- // Use CVS to check the source out
- if ($mod_version == 'HEAD') {
- print "Checking out HEAD for $module\n";
- $cmd = "cvs -q co -P $module > /dev/null";
- system($cmd, $status);
- } else {
- print "Checking out tag $mod_version for $module\n";
- $cmd = "cvs -q co -P -r$mod_version $module > /dev/null";
- system($cmd, $status);
- }
- if ($status) {
- die("\nThere was an error running the command\n$cmd\n");
- }
-
- // Move the source into the directory specified
- print "Moving $module to $directory\n";
- if (@is_dir($directory)) {
- system("rm -rf $directory");
- }
- system("mv -f $module $directory");
- }
-
- /**
- * Checkout and install framework
- */
- public function checkOutFramework($mod_version, $directory)
- {
- if ($this->_options['module'] == 'horde' &&
- ($this->_options['branch'] == 'HEAD' ||
- strstr($this->_options['branch'], 'FRAMEWORK'))) {
- if ($this->_options['branch'] == 'HEAD') {
- print "Checking out HEAD for framework\n";
- } else {
- print "Checking out tag $mod_version for framework\n";
- }
- $cmd = "cd $directory; cvs co -P -r$mod_version framework > /dev/null 2>&1; cd ..";
- system($cmd, $status);
- if ($status) {
- die("\nThere was an error running the command\n$cmd\n");
- }
- print "Installing framework packages\n";
- if (file_exists("./$directory/scripts/create-symlinks.php")) {
- system("php ./$directory/scripts/create-symlinks.php --copy --src=./$directory/framework --dest=./$directory/lib");
- } else {
- system("horde-fw-symlinks.php --copy --src ./$directory/framework --dest ./$directory/lib");
- }
-
- print "Setting include path\n";
- $filename = $directory . '/lib/core.php';
- $newfilename = $filename . '.new';
- $oldfp = fopen($filename, 'r');
- $newfp = fopen($newfilename, 'w');
- while ($line = fgets($oldfp)) {
- fwrite($newfp, str_replace('// ini_set(\'include_path\'', 'ini_set(\'include_path\'', $line));
- }
- fclose($oldfp);
- fclose($newfp);
- system("mv -f $newfilename $filename");
- }
- }
-
- /**
- * Upload tarball to the FTP server
- */
- public function upload()
- {
- $module = $this->_options['module'];
- $user = $this->_options['horde']['user'];
- $identity = empty($this->_options['ssh']['identity']) ? '' : ' -i ' . $this->_options['ssh']['identity'];
- $chmod = "chmod 664 /horde/ftp/pub/$module/$this->_tarballName;";
- if ($this->_makeDiff) {
- $chmod .= " chmod 664 /horde/ftp/pub/$module/patches/$this->_patchName.gz;";
- }
- if ($this->_latest &&
- strpos($this->_options['branch'], 'RELENG') !== 0) {
- $chmod .= " ln -sf $this->_tarballName /horde/ftp/pub/$module/$module-latest.tar.gz;";
- }
-
- if (!$this->_options['noftp']) {
- print "Uploading $this->_tarballName to $user@ftp.horde.org:/horde/ftp/pub/$module/\n";
- system("scp -P 35$identity $this->_tarballName $user@ftp.horde.org:/horde/ftp/pub/$module/");
- if ($this->_makeDiff) {
- print "Uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
- system("scp -P 35$identity $this->_patchName.gz $user@ftp.horde.org:/horde/ftp/pub/$module/patches/");
- }
- print "Executing $chmod\n";
- system("ssh -p 35 -l $user$identity ftp.horde.org '$chmod'");
- } else {
- print "NOT uploading $this->_tarballName to ftp.horde.org:/horde/ftp/pub/$module/\n";
- if ($this->_makeDiff) {
- print "NOT uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
- }
- print "NOT executing $chmod\n";
- }
- }
-
- /**
- * check if freshmeat announcement was successful.
- */
- protected function _fmVerify($fm)
- {
- if (is_a($fm, 'PEAR_Error')) {
- print $fm->getMessage() . "\n";
- return false;
- } elseif (!is_array($fm)) {
- var_dump($fm);
- return false;
- }
- return true;
- }
-
- /**
- * announce release to mailing lists and freshmeat.
- */
- public function announce($doc_dir = null)
- {
- $module = $this->_options['module'];
- if (!isset($this->notes)) {
- print "NOT announcing release, RELEASE_NOTES missing.\n";
- return;
- }
- if (!empty($this->_options['noannounce']) ||
- !empty($this->_options['nofreshmeat'])) {
- print "NOT announcing release on freshmeat.net\n";
- } else {
- print "Announcing release on freshmeat.net\n";
- }
-
- if (empty($this->_options['nofreshmeat'])) {
- $fm = Horde_RPC::request(
- 'xmlrpc',
- 'http://freshmeat.net/xmlrpc/',
- 'login',
- array('username' => $this->_options['fm']['user'],
- 'password' => $this->_options['fm']['password']));
- } else {
- $fm = array('SID' => null);
- }
- if (empty($doc_dir)) {
- $doc_dir = $module . '/docs';
- }
-
- $url_changelog = $this->_oldVersion
- ? "http://cvs.horde.org/diff.php/$doc_dir/CHANGES?r1={$this->_oldChangelogVersion}&r2={$this->_changelogVersion}&ty=h"
- : '';
-
- if (is_a($fm, 'PEAR_Error')) {
- print $fm->getMessage() . "\n";
- } else {
- $announcement = array('SID' => $fm['SID'],
- 'project_name' => $this->notes['fm']['project'],
- 'branch_name' => $this->notes['fm']['branch'],
- 'version' => $this->_sourceVersionString,
- 'changes' => htmlspecialchars($this->notes['fm']['changes']),
- 'release_focus' => (int)$this->notes['fm']['focus'],
- 'url_changelog' => $url_changelog,
- 'url_tgz' => "ftp://ftp.horde.org/pub/$module/{$this->_tarballName}");
- if ($this->_fmVerify($fm)) {
- if (!empty($this->_options['noannounce']) ||
- !empty($this->_options['nofreshmeat'])) {
- print "Announcement data:\n";
- print_r($announcement);
- } else {
- $fm = Horde_RPC::request(
- 'xmlrpc',
- 'http://freshmeat.net/xmlrpc/',
- 'publish_release',
- $announcement);
- $this->_fmVerify($fm);
- }
- }
- }
-
- $ml = (!empty($this->notes['list'])) ? $this->notes['list'] : $module;
- if (substr($ml, 0, 6) == 'horde-') {
- $ml = 'horde';
- }
-
- $to = "announce@lists.horde.org, vendor@lists.horde.org, $ml@lists.horde.org";
- if (!$this->_latest) {
- $to .= ', i18n@lists.horde.org';
- }
-
- if (!empty($this->_options['noannounce'])) {
- print "NOT announcing release on $to\n";
- } else {
- print "Announcing release to $to\n";
- }
-
- // Building headers
- $subject = $this->notes['name'] . ' ' . $this->_sourceVersionString;
- if ($this->_latest) {
- $subject .= ' (final)';
- }
- if ($this->notes['fm']['focus'] == 9) {
- $subject = '[SECURITY] ' . $subject;
- }
- $headers = array('From' => $this->_options['ml']['from'],
- 'To' => $to,
- 'Subject' => $subject);
-
- // Building message text
- $body = $this->notes['ml']['changes'];
- if ($this->_oldVersion) {
- $body .= "\n\n" .
- sprintf('The full list of changes (from version %s) can be viewed here:', $this->_oldSourceVersionString) .
- "\n\n" .
- $url_changelog;
- }
- $body .= "\n\n" .
- sprintf('The %s %s distribution is available from the following locations:', $this->notes['name'], $this->_sourceVersionString) .
- "\n\n" .
- sprintf(' ftp://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName) . "\n" .
- sprintf(' http://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName);
- if ($this->_makeDiff) {
- $body .= "\n\n" .
- sprintf('Patches against version %s are available at:', $this->_oldSourceVersionString) .
- "\n\n" .
- sprintf(' ftp://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName) . "\n" .
- sprintf(' http://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName);
-
- if (!empty($this->_binaryDiffs)) {
- $body .= "\n\n" .
- 'NOTE: Patches do not contain differences between files containing binary data.' . "\n" .
- 'These files will need to be updated via the distribution files:' . "\n\n " .
- implode("\n ", $this->_binaryDiffs);
- }
- }
- $body .= "\n\n" .
- 'Or, for quicker access, download from your nearest mirror:' .
- "\n\n" .
- ' http://www.horde.org/mirrors.php' .
- "\n\n" .
- 'MD5 sums for the packages are as follows:' .
- "\n\n" .
- ' ' . $this->_tarballMD5[0] . "\n" .
- ' ' . $this->_patchMD5[0] .
- "\n\n" .
- 'Have fun!' .
- "\n\n" .
- 'The Horde Team.';
-
- if (!empty($this->_options['noannounce'])) {
- print "Message headers:\n";
- print_r($headers);
- print "Message body:\n$body\n";
- return;
- }
-
- // Building and sending message
- $mail = new Horde_Mime_Mail();
- $mail->setBody($body, 'iso-8859-1', false);
- $mail->addHeaders($headers);
- $result = $mail->send($this->_options['mailer']['type'], $this->_options['mailer']['params']);
- if (is_a($result, 'PEAR_Error')) {
- print $result->getMessage() . "\n";
- }
- }
-
- /**
- * Do testing (development only)
- */
- public function test()
- {
- if (!$this->_options['test']) {
- return;
- }
-
- print "options['version']={$this->_options['version']}\n";
- print "options['oldversion']={$this->_options['oldversion']}\n";
- print "options['module']={$this->_options['module']}\n";
- print "options['branch']={$this->_options['branch']}\n";
-
- $this->setVersionStrings();
-
- print "hordeVersionString={$this->_hordeVersionString}\n";
- print "oldHordeVersionString={$this->_oldHordeVersionString}\n";
- print "makeDiff={$this->_makeDiff}\n";
- print "oldVersion={$this->_oldVersion}\n";
- print "directoryName={$this->_directoryName}\n";
- if ($this->_oldVersion) {
- print "oldDirectoryName={$this->_oldDirectoryName}\n";
- }
- print "tagVersionString={$this->_tagVersionString}\n";
- if ($this->_oldVersion) {
- print "oldTagVersionString={$this->_oldTagVersionString}\n";
- }
- print "sourceVersionString={$this->_sourceVersionString}\n";
- if ($this->_oldVersion) {
- print "oldSourceVersionString={$this->_oldSourceVersionString}\n";
- }
- print "newSourceVersionString={$this->_newSourceVersionString}\n";
- print "newSourceVersionStringPlain={$this->_newSourceVersionStringPlain}\n";
- print "ticketVersion={$this->_ticketVersion}\n";
- print "ticketVersionDesc=MODULE{$this->_ticketVersionDesc}\n";
- if ($this->_latest) {
- print "This is a production release\n";
- }
- exit(0);
- }
-
- /**
- * Add the new version to bugs.horde.org
- */
- public function addWhupsVersion()
- {
- if (!isset($this->notes)) {
- print "\nNOT updating bugs.horde.org, RELEASE_NOTES missing.\n";
- return;
- }
- $this->_ticketVersionDesc = $this->notes['name'] . $this->_ticketVersionDesc;
-
- $params = array('url' => 'https://dev.horde.org/horde/rpc.php',
- 'user' => $this->_options['horde']['user'],
- 'pass' => $this->_options['horde']['pass']);
- $whups = new Horde_Release_Whups($params);
-
- if (!$this->_options['nowhups']) {
- print "Adding new versions to bugs.horde.org: ";
- /* Set the new version in the queue */
- try {
- $whups->addNewVersion($this->_options['module'], $this->_ticketVersion, $this->_ticketVersionDesc);
- print "OK\n";
- } catch (Horde_Exception $e) {
- print "Failed:\n";
- print $e->getMessage() . "\n";
- }
- } else {
- print "NOT updating bugs.horde.org:\n";
- print "New ticket version WOULD have been {$this->_ticketVersion}\n";
- print "New ticket version description WOULD have been {$this->_ticketVersionDesc}\n";
-
- /* Perform some sanity checks on bugs.horde.org */
- try {
- $queue = $whups->getQueueId($this->_options['module']);
-
- if ($queue === false) {
- print "Was UNABLE to locate the queue id for {$this->_options['module']}\n";
- } else {
- print "The queue id on bugs.horde.org is $queue \n";
- }
- } catch (Horde_Exception $e) {
- print "Will be UNABLE to update bugs.horde.org:\n";
- print $e->getMessage() . "\n";
- }
- }
- }
-
- /**
- * Set the version strings to use given the arguments
- */
- public function setVersionStrings()
- {
- $ver = explode('.', $this->_options['version']);
- if (preg_match('/(\d+)\-(.*)/', $ver[count($ver) - 1], $matches)) {
- $ver[count($ver) - 1] = $matches[1];
- $plus = $matches[2];
- }
- if (preg_match('/(H\d)-(\d+)/', $ver[0], $matches)) {
- $ver[0] = $matches[2];
- $this->_hordeVersionString = $matches[1];
- }
- if (count($ver) > 2 && $ver[count($ver) - 1] == '0') {
- die("version {$this->_options['version']} should not have the trailing 3rd-level .0\n");
- }
-
- // check if --oldversion is empty or 0
- if (!empty($this->_options['oldversion'])) {
- $this->_oldVersion = true;
- }
- $oldver = explode('.', $this->_options['oldversion']);
- if (preg_match('/(\d+)\-(.*)/', $oldver[count($oldver) - 1], $matches)) {
- $oldver[count($oldver) - 1] = $matches[1];
- $oldplus = $matches[2];
- }
- if (preg_match('/(H\d)-(\d+)/', $oldver[0], $matches)) {
- $oldver[0] = $matches[2];
- $this->_oldHordeVersionString = $matches[1];
- }
-
- // set the string to use as the tag name in CVS
- $this->_tagVersionString = strtoupper($this->_options['module'] . '_' . preg_replace('/\W/', '_', implode('_', $ver)));
- if (isset($plus)) {
- $this->_tagVersionString .= '_' . $plus;
- }
-
- // create patches only if not a major version change
- if ($this->_options['oldversion'] && $ver[0] == $oldver[0]) {
- $this->_makeDiff = true;
- }
-
- // is this really a production release?
- if (isset($plus) && !preg_match('/^pl\d/', $plus)) {
- $this->_latest = false;
- }
-
- // set the string to insert into the source version file
- $this->_sourceVersionString = implode('.', $ver);
- if (isset($plus)) {
- $this->_sourceVersionString .= '-' . $plus;
- }
-
- // set the string to be used for the directory to package from
- $this->_directoryName = $this->_options['module'] . '-';
- if (!empty($this->_hordeVersionString)) {
- $this->_directoryName .= $this->_hordeVersionString . '-';
- }
- $this->_directoryName = strtolower($this->_directoryName . $this->_sourceVersionString);
-
- if (!empty($this->_hordeVersionString)) {
- $this->_sourceVersionString = $this->_hordeVersionString . ' (' . $this->_sourceVersionString . ')';
- }
-
- if ($this->_oldVersion) {
- $this->_oldSourceVersionString = implode('.', $oldver);
- if (isset($oldplus)) {
- $this->_oldSourceVersionString .= '-' . $oldplus;
- }
- $this->_oldTagVersionString = strtoupper($this->_options['module'] . '_' . implode('_', $oldver));
- if (isset($oldplus)) {
- $this->_oldTagVersionString .= '_' . $oldplus;
- }
- $this->_oldDirectoryName = strtolower($this->_options['module'] . '-' . $this->_oldHordeVersionString . $this->_oldSourceVersionString);
- $this->_oldDirectoryName = $this->_options['module'] . '-';
- if (!empty($this->_oldHordeVersionString)) {
- $this->_oldDirectoryName .= $this->_oldHordeVersionString . '-';
- }
- $this->_oldDirectoryName = strtolower($this->_oldDirectoryName . $this->_oldSourceVersionString);
-
- if (!empty($this->_oldHordeVersionString)) {
- $this->_oldSourceVersionString = $this->_oldHordeVersionString . ' (' . $this->_oldSourceVersionString . ')';
- }
- }
-
- // Set string to use for updating ticketing system.
- $this->_ticketVersion = implode('.', $ver);
- if (!empty($plus)) {
- $this->_ticketVersion .= '-' . $plus;
- }
-
- if (!empty($this->_hordeVersionString)) {
- $this->_ticketVersionDesc .= ' ' . $this->_hordeVersionString;
- }
-
- // Account for the 'special' case of the horde module.
- if ($this->_options['module'] == 'horde') {
- $this->_ticketVersionDesc .= ' ' . implode('.', $ver);
- } else {
- $this->_ticketVersionDesc .= ' ' . '(' . implode('.', $ver) . ')';
- }
-
- // See if we have a 'Final', 'Alpha', or 'RC' to add.
- if ($this->_latest) {
- $this->_ticketVersionDesc .= ' Final';
- } elseif (!empty($plus) &&
- preg_match('/^RC(\d+)/', $plus, $matches)) {
- $this->_ticketVersionDesc .= ' Release Candidate ' . $matches[1];
-
- } elseif (!empty($plus) && strtolower($plus) == 'alpha') {
- $this->_ticketVersionDesc .= ' Alpha';
- }
-
- // set the name of the string to put into the source version file when
- // done
- if (!isset($plus)) {
- while (count($ver) < 3) {
- $ver[] = '0';
- }
- $ver[count($ver) - 1] += 1;
- }
- $this->_newSourceVersionString = implode('.', $ver) . '-cvs';
- $this->_newSourceVersionStringPlain = $this->_newSourceVersionString;
-
- if (!empty($this->_hordeVersionString)) {
- $this->_newSourceVersionString = $this->_hordeVersionString .
- ' (' . $this->_newSourceVersionString . ')';
- }
-
- }
-
- /**
- * Get all of the command-line arguments from the user
- */
- public function getArguments()
- {
- global $argv;
-
- // Parse the command-line arguments
- array_shift($argv);
- foreach ($argv as $arg) {
- // Check to see if they gave us a module
- if (preg_match('/--module=(.*)/', $arg, $matches)) {
- $this->_options['module'] = $matches[1];
-
- // Check to see if they tell us the version of the tarball to make
- } elseif (preg_match('/--version=(.*)/', $arg, $matches)) {
- $this->_options['version']= $matches[1];
-
- // Check to see if they tell us the last release version
- } elseif (preg_match('/--oldversion=(.*)/', $arg, $matches)) {
- $this->_options['oldversion']= $matches[1];
-
- // Check to see if they tell us which branch to work with
- } elseif (preg_match('/--branch=(.*)/', $arg, $matches)) {
- $this->_options['branch']= $matches[1];
-
- // Check to see if they tell us not to commit or tag
- } elseif (strstr($arg, '--nocommit')) {
- $this->_options['nocommit']= true;
-
- // Check to see if they tell us not to upload
- } elseif (strstr($arg, '--noftp')) {
- $this->_options['noftp']= true;
-
- // Check to see if they tell us not to announce
- } elseif (strstr($arg, '--noannounce')) {
- $this->_options['noannounce']= true;
-
- // Check to see if they tell us not to announce
- } elseif (strstr($arg, '--nofreshmeat')) {
- $this->_options['nofreshmeat']= true;
-
- // Check to see if they tell us not to add new ticket versions
- } elseif (strstr($arg, '--noticketversion')) {
- $this->_options['nowhups'] = true;
-
- // Check to see if they tell us to do a dry run
- } elseif (strstr($arg, '--dryrun')) {
- $this->_options['nocommit'] = true;
- $this->_options['noftp'] = true;
- $this->_options['noannounce'] = true;
- $this->_options['nowhups'] = true;
- $this->_options['nofreshmeat']= true;
-
- // Check to see if they tell us to test (for development only)
- } elseif (strstr($arg, '--test')) {
- $this->_options['test']= true;
- // safety first
- $this->_options['nocommit'] = true;
- $this->_options['noftp'] = true;
- $this->_options['noannounce'] = true;
- $this->_options['nowhups'] = true;
- $this->_options['nofreshmeat']= true;
-
- // Check for help usage.
- } elseif (strstr($arg, '--help')) {
- $this->print_usage();
- exit;
-
- // We have no idea what this is
- } else {
- $this->print_usage('You have used unknown arguments: ' . $arg);
- exit;
- }
- }
- }
-
- /**
- * Check the command-line arguments and set some internal defaults
- */
- public function checkArguments()
- {
- // Make sure that we have a module defined
- if (!isset($this->_options['module'])) {
- $this->print_usage('You must define which module to package.');
- exit;
- }
-
- // Let's make sure that there are valid version strings in here...
- if (!isset($this->_options['version'])) {
- $this->print_usage('You must define which version to package.');
- exit;
- }
- if (!preg_match('/\d+\.\d+.*/', $this->_options['version'])) {
- $this->print_usage('Incorrect version string.');
- exit;
- }
- if (!isset($this->_options['oldversion'])) {
- $this->print_usage('You must define last release\'s version.');
- exit;
- }
- if (!preg_match('/\d+(\.\d+.*)?/', $this->_options['oldversion'])) {
- $this->print_usage('Incorrect old version string.');
- exit;
- }
-
- // Make sure we have a horde.org user
- if (empty($this->_options['horde']['user'])) {
- $this->print_usage('You must define a horde.org user.');
- exit;
- }
-
- // If there is no branch defined, we're using the tip revisions.
- // These releases are always developmental, and should use the HEAD "branch" name.
- if (!isset($this->_options['branch'])) {
- $this->_options['branch'] = 'HEAD';
- }
- }
-
- /**
- * Check the command-line arguments and set some internal defaults
- */
- public function checkSetSystem()
- {
- // Set umask
- umask(022);
- }
-
- /**
- * Show people how to use the damned thing
- */
- public function print_usage($message = null)
- {
- if (!is_null($message)) {
- print "\n*** ERROR: $message ***\n";
- }
-
- print <<<USAGE
-
-make-release.php: Horde release generator.
-
- This script takes as arguments the module to make a release of, the
- version of the release, and the branch:
-
- horde-make-release.php --module=<name>
- --version=[Hn-]xx.yy[.zz[-<string>]]
- --oldversion=[Hn-]xx[.yy[.zz[-<string>]]]
- [--branch=<branchname>] [--nocommit] [--noftp]
- [--noannounce] [--nofreshmeat] [--noticketversion]
- [--test] [--dryrun] [--help]
-
- If you omit the branch, it will implicitly work with the HEAD branch.
- If you release a new major version use the --oldversion=0 option.
- Use the --nocommit option to do a test build (without touching the CVS
- repository).
- Use the --noftp option to not upload any files on the FTP server.
- Use the --noannounce option to not send any release announcements.
- Use the --nofreshmeat option to not send any freshmeat announcements.
- Use the --noticketversion option to not update the version information on
- bugs.horde.org.
- The --dryrun option is an alias for:
- --nocommit --noftp --noannounce --nofreshmeat --noticketversion.
- The --test option is for debugging purposes only.
-
- EXAMPLES:
-
- To make a new development release of Horde:
- horde-make-release.php --module=horde --version=2.1-dev --oldversion=2.0
-
- To make a new stable release of Turba:
- horde-make-release.php --module=turba --version=H3-2.0.2 \
- --oldversion=H3-2.0.1 --branch=FRAMEWORK_3
-
- To make a new stable release of IMP 3:
- horde-make-release.php --module=imp --version=3.0 --oldversion=2.3.7 \
- --branch=RELENG_3
-
- To make a brand new Alpha/Beta/RC release of Luxor:
- horde-make-release.php --module=luxor --version=H3-1.0-ALPHA \
- --oldversion=0
-
-USAGE;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * Class for interfacing with the tickets API.
- *
- * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Michael J. Rubinsky <mrubinsk@horde.org>
- * @package Core
- */
-class Horde_Release_Whups
-{
- /**
- * Instance of XML_RPC_Client object
- *
- * @var XML_RPC_CLient
- */
- protected $_client;
-
- /**
- * Local copy of config params.
- *
- * @var array
- */
- protected $_params;
-
- /**
- * Constructor.
- *
- * @param array $params TODO
- */
- public function __construct($params)
- {
- $this->_params = $params;
- }
-
- /**
- * Add a new version to the current modules queue.
- *
- * @param string $module The name of the module.
- * @param string $version The version string.
- * @param string $desc Descriptive text for this version.
- *
- * @throws Horde_Exception
- */
- public function addNewVersion($module, $version, $desc = '')
- {
- if ($module == 'horde') {
- $module = 'horde base';
- }
-
- $id = $this->getQueueId($module);
- if ($id === false) {
- throw new Horde_Exception('Unable to locate requested queue');
- }
-
- $method = 'tickets.addVersion';
- $params = array($id, $version, $desc);
- $options = array('user' => $this->_params['user'],
- 'pass' => $this->_params['pass']);
-
- $res = Horde_RPC::request('jsonrpc', $this->_params['url'], $method, $params, $options);
- if ($res instanceof PEAR_Error) {
- throw new Horde_Exception($res);
- }
- }
-
- /**
- * Look up the queue id for the requested module name.
- *
- * @param string $module TODO
- *
- * @return boolean TODO
- */
- function getQueueId($module)
- {
- $queues = $this->_listQueues();
-
- foreach ($queues as $id => $queue) {
- if (strtolower($queue) == $module) {
- return $id;
- }
- }
-
- return false;
- }
-
- /**
- * Perform a listQueue api call.
- *
- * @return string TODO
- * @throws Horde_Exception
- */
- protected function _listQueues()
- {
- $method = 'tickets.listQueues';
- $result = Horde_RPC::request('jsonrpc', $this->_params['url'], $method,
- null, array('user' => $this->_params['user'],
- 'pass' => $this->_params['pass']));
- if ($result instanceof PEAR_Error) {
- throw new Horde_Exception($result);
- }
-
- return $result->result;
- }
-
-}
+++ /dev/null
-<?php
-/**
- * The Horde_Script_Files:: class provides a coherent way to manage script
- * files for inclusion in Horde output. This class is meant to be used
- * internally by Horde:: only.
- *
- * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
- *
- * See the enclosed file COPYING for license information (LGPL). If you
- * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
- *
- * @author Michael Slusarz <slusarz@horde.org>
- * @package Core
- */
-class Horde_Script_Files
-{
- /**
- * The singleton instance.
- *
- * @var Horde_Script_Files
- */
- static protected $_instance;
-
- /**
- * The list of script files to add.
- *
- * @var array
- */
- protected $_files = array();
-
- /**
- * The list of files we have already included.
- *
- * @var array
- */
- protected $_included = array();
-
- /**
- * The list of deprecated files.
- *
- * @var array
- */
- protected $_ignored = array(
- 'horde' => array('tooltip.js')
- );
-
- /**
- * The list of javascript files to always load from Horde.
- *
- * @var array
- */
- protected $_fromhorde = array('prototype.js');
-
- /**
- * The list of javscript files in Horde that have prototypejs'd versions.
- *
- * @var array
- */
- protected $_ptversions = array('tables.js', 'stripe.js');
-
- /**
- * Auto load horde.js?
- *
- * @var boolean
- */
- protected $_loadhordejs = true;
-
- /**
- * Singleton.
- */
- static public function singleton()
- {
- if (!self::$_instance) {
- self::$_instance = new Horde_Script_Files();
- }
-
- return self::$_instance;
- }
-
- /**
- * Adds the javascript code to the output (if output has already started)
- * or to the list of script files to include.
- *
- * @param string $file The full javascript file name.
- * @param string $app The application name. Defaults to the current
- * application.
- * @param boolean $direct Include the file directly without passing it
- * through javascript.php?
- * @param boolean $full Output a full url
- */
- public function add($file, $app = null, $direct = false, $full = false)
- {
- $res = $this->_add($file, $app, $direct, $full);
-
- if (empty($res) || (!ob_get_length() && !headers_sent())) {
- return;
- }
-
- // If headers have already been sent, we need to output a <script>
- // tag directly.
- echo '<script type="text/javascript" src="' . $res['u'] . '"></script>' . "\n";
- }
-
- /**
- * Helper function to determine if given file needs to be output.
- */
- public function _add($file, $app, $direct, $full = false)
- {
- global $registry;
-
- if (empty($app)) {
- $app = $registry->getApp();
- }
-
- // Skip any js files that have since been deprecated.
- if (!empty($this->_ignored[$app]) &&
- in_array($file, $this->_ignored[$app])) {
- return false;
- }
-
- // Several files will always be the same thing. Don't distinguish
- // between loading them in different $app scopes; always load them
- // from Horde scope.
- if (in_array($file, $this->_fromhorde)) {
- $app = 'horde';
- }
-
- // Don't include scripts multiple times.
- if (!empty($this->_included[$app][$file])) {
- return false;
- }
- $this->_included[$app][$file] = true;
-
- // Explicitly check for a directly serve-able version of the script.
- $path = $GLOBALS['registry']->get('fileroot', $app);
- if (!$direct &&
- file_exists($file[0] == '/'
- ? $path . $file
- : $registry->get('jsfs', $app) . '/' . $file)) {
- $direct = true;
- }
-
- if ($direct) {
- if ($file[0] == '/') {
- $url = Horde::url($registry->get('webroot', $app) . $file,
- $full, -1);
- } else {
- $url = Horde::url($registry->get('jsuri', $app) . '/' . $file,
- $full, -1);
- $path = $registry->get('jsfs', $app) . '/';
- }
-
- } else {
- $path = $registry->get('templates', $app) . '/javascript/';
- $url = Horde::url(
- Horde_Util::addParameter(
- $registry->get('webroot', 'horde') . '/services/javascript.php',
- array('file' => $file, 'app' => $app)));
- }
-
- $out = $this->_files[$app][] = array('f' => $file, 'd' => $direct, 'u' => $url, 'p' => $path);
- return $out;
- }
-
- /**
- * Includes javascript files that are needed before any headers are sent.
- */
- public function includeFiles()
- {
- foreach ($this->listFiles() as $app => $files) {
- foreach ($files as $file) {
- echo '<script type="text/javascript" src="' . $file['u'] . '"></script>' . "\n";
- }
- }
- }
-
- /**
- * Prepares the list of javascript files to include.
- *
- * @return array
- */
- public function listFiles()
- {
- /* If there is no javascript available, there's no point in including
- * the rest of the files. */
- if (!$GLOBALS['browser']->hasFeature('javascript')) {
- return array();
- }
-
- $prototype = false;
-
- // Always include Horde-level scripts first.
- if (!empty($this->_files['horde'])) {
- foreach ($this->_files['horde'] as $file) {
- if ($file['f'] == 'prototype.js') {
- $prototype = true;
- break;
- }
- }
-
- if (!$prototype) {
- $keys = array_keys($this->_files['horde']);
- foreach ($keys as $key) {
- $file = $this->_files['horde'][$key];
- if (in_array($file['f'], $this->_ptversions)) {
- $this->_add('prototype.js', 'horde', true);
- $prototype = true;
- break;
- }
- }
- }
-
- // prototype.js must be included before any script that uses it
- if ($prototype) {
- $keys = array_keys($this->_files['horde']);
- foreach ($keys as $key) {
- $file = $this->_files['horde'][$key];
- if ($file['f'] == 'prototype.js') {
- unset($this->_files['horde'][$key]);
- array_unshift($this->_files['horde'], $file);
- }
- }
- reset($this->_files);
- }
- }
-
- /* Add general UI js library. */
- if ($this->_loadhordejs) {
- $this->_add('prototype.js', 'horde', true);
- $this->_add('horde.js', 'horde', true);
- }
-
- /* Add accesskeys.js if access keys are enabled. */
- if ($GLOBALS['prefs']->getValue('widget_accesskey')) {
- $this->_add('prototype.js', 'horde', true);
- $this->_add('accesskeys.js', 'horde', true);
- }
-
- /* Make sure 'horde' entries appear first. */
- reset($this->_files);
- if (key($this->_files) == 'horde') {
- return $this->_files;
- }
-
- if (isset($this->_files['horde'])) {
- $jslist = array('horde' => $this->_files['horde']);
- } else {
- $jslist = array();
- }
- foreach ($this->_files as $key => $val) {
- if ($key != 'horde') {
- $jslist[$key] = $val;
- }
- }
-
- return $jslist;
- }
-
- /**
- * Disable auto-loading of the horde.js script.
- * Needs to auto-load by default for BC.
- *
- * @todo Remove for Horde 4
- */
- public function disableAutoloadHordeJS()
- {
- $this->_loadhordejs = false;
- }
-
-}
--- /dev/null
+<?php
+/**
+ * The Horde_Menu:: class provides standardized methods for creating menus in
+ * Horde applications.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jon Parise <jon@horde.org>
+ * @package Core
+ */
+class Horde_Menu
+{
+ /* TODO */
+ const MASK_NONE = 0;
+ const MASK_HELP = 1;
+ const MASK_LOGIN = 2;
+ const MASK_PREFS = 4;
+ const MASK_PROBLEM = 8;
+ const MASK_ALL = 15;
+
+ /* TODO */
+ const POS_LAST = 999;
+
+ /**
+ * Menu array.
+ *
+ * @var array
+ */
+ protected $_menu = array();
+
+ /**
+ * Mask defining what general Horde links are shown in this Menu.
+ *
+ * @var integer
+ */
+ protected $_mask;
+
+ /**
+ * Constructor
+ */
+ public function __construct($mask = self::MASK_ALL)
+ {
+ /* Menuitem mask. */
+ $this->_mask = $mask;
+
+ /* Location of the menufile. */
+ $this->_menufile = $GLOBALS['registry']->get('fileroot') . '/config/menu.php';
+ }
+
+ /**
+ * Add an item to the menu array.
+ *
+ * @param string $url String containing the value for the hyperlink.
+ * @param string $text String containing the label for this menu
+ * item.
+ * @param string $icon String containing the filename of the image
+ * icon to display for this menu item.
+ * @param string $icon_path If the icon lives in a non-default directory,
+ * where is it?
+ * @param string $target If the link needs to open in another frame or
+ * window, what is its name?
+ * @param string $onclick Onclick javascript, if desired.
+ * @param string $class CSS class for the menu item.
+ *
+ * @return integer The id (NOT guaranteed to be an array index) of the
+ * item just added to the menu.
+ */
+ public function add($url, $text, $icon = '', $icon_path = null,
+ $target = '', $onclick = null, $class = null)
+ {
+ $pos = count($this->_menu);
+ if (!$pos || ($pos - 1 != max(array_keys($this->_menu)))) {
+ $pos = count($this->_menu);
+ }
+
+ $this->_menu[$pos] =
+ array(
+ 'url' => $url,
+ 'text' => $text,
+ 'icon' => $icon,
+ 'icon_path' => $icon_path,
+ 'target' => $target,
+ 'onclick' => $onclick,
+ 'class' => $class
+ );
+
+ return $pos;
+ }
+
+ /**
+ * Add an item to the menu array.
+ *
+ * @param string $url String containing the value for the hyperlink.
+ * @param string $text String containing the label for this menu
+ * item.
+ * @param string $icon String containing the filename of the image
+ * icon to display for this menu item.
+ * @param string $icon_path If the icon lives in a non-default directory,
+ * where is it?
+ * @param string $target If the link needs to open in another frame or
+ * window, what is its name?
+ * @param string $onclick Onclick javascript, if desired.
+ * @param string $class CSS class for the menu item.
+ *
+ * @return integer The id (NOT guaranteed to be an array index) of the item
+ * just added to the menu.
+ */
+ public function addArray($item)
+ {
+ $pos = count($this->_menu);
+ if (!$pos || ($pos - 1 != max(array_keys($this->_menu)))) {
+ $pos = count($this->_menu);
+ }
+
+ $this->_menu[$pos] = $item;
+
+ return $pos;
+ }
+
+ /**
+ * TODO
+ */
+ public function setPosition($id, $pos)
+ {
+ if (!isset($this->_menu[$id]) || isset($this->_menu[$pos])) {
+ return false;
+ }
+
+ $item = $this->_menu[$id];
+ unset($this->_menu[$id]);
+ $this->_menu[$pos] = $item;
+
+ return true;
+ }
+
+ /**
+ * Return the unordered list representing the list of menu items. Styling
+ * is done through CSS.
+ *
+ * @return string An unordered list of menu elements that can be entirely
+ * styled with CSS.
+ */
+ public function render()
+ {
+ global $conf, $registry, $prefs;
+
+ $graphics = $registry->getImageDir('horde');
+ $app = $registry->getApp();
+
+ if ($this->_mask !== self::MASK_NONE) {
+ /* Add any custom menu items. */
+ $this->addSiteLinks();
+
+ /* Add any app menu items. */
+ $this->addAppLinks();
+ }
+
+ /* Add settings link. */
+ if ($this->_mask & self::MASK_PREFS && $url = Horde::getServiceLink('options', $app)) {
+ $this->add($url, _("_Options"), 'prefs.png', $graphics);
+ }
+
+ /* Add problem link. */
+ if ($this->_mask & self::MASK_PROBLEM && $problem_link = Horde::getServiceLink('problem', $app)) {
+ $this->add($problem_link, _("Problem"), 'problem.png', $graphics);
+ }
+
+ /* Add help link. */
+ if ($this->_mask & self::MASK_HELP && $help_link = Horde::getServiceLink('help', $app)) {
+ $this->add($help_link, _("Help"), 'help_index.png', $graphics, 'help', 'popup(this.href); return false;', 'helplink');
+ }
+
+ /* Login/Logout. */
+ if ($this->_mask & self::MASK_LOGIN) {
+ /* If the sidebar isn't always shown, but is sometimes
+ * shown, then logout links should be to the parent
+ * frame. */
+ $auth_target = null;
+ if ($conf['menu']['always'] || $prefs->getValue('show_sidebar')) {
+ $auth_target = '_parent';
+ }
+
+ if (Horde_Auth::getAuth()) {
+ if ($logout_link = Horde::getServiceLink('logout', $app, !$prefs->getValue('show_sidebar'))) {
+ $this->add($logout_link, _("_Log out"), 'logout.png', $graphics, $auth_target, null, '__noselection');
+ }
+ } else {
+ if ($login_link = Horde::getServiceLink('login', $app)) {
+ $this->add($login_link, _("_Log in"), 'login.png', $graphics, $auth_target, null, '__noselection');
+ }
+ }
+ }
+
+ /* No need to return an empty list if there are no menu
+ * items. */
+ if (!count($this->_menu)) {
+ return '';
+ }
+
+ /* Sort to match explicitly set positions. */
+ ksort($this->_menu);
+ if (!empty($GLOBALS['nls']['rtl'][$GLOBALS['language']])) {
+ $this->_menu = array_reverse($this->_menu) ;
+ }
+
+ $menu_view = $prefs->getValue('menu_view');
+ $output = '<ul>';
+ foreach ($this->_menu as $m) {
+ /* Check for separators. */
+ if ($m == 'separator') {
+ $output .= "\n<li class=\"separator\"> </li>";
+ continue;
+ }
+
+ /* Item class and selected indication. */
+ if (!isset($m['class'])) {
+ /* Try to match the item's path against the current
+ * script filename as well as other possible URLs to
+ * this script. */
+ if (self::isSelected($m['url'])) {
+ $m['class'] = 'current';
+ }
+ } elseif ($m['class'] === '__noselection') {
+ unset($m['class']);
+ }
+
+ /* Icon. */
+ $icon = '';
+ if ($menu_view == 'icon' || $menu_view == 'both') {
+ if (!isset($m['icon_path'])) {
+ $m['icon_path'] = null;
+ }
+ $icon = Horde::img($m['icon'], Horde::stripAccessKey($m['text']), '', $m['icon_path']) . '<br />';
+ }
+
+ /* Link. */
+ $accesskey = Horde::getAccessKey($m['text']);
+ $link = Horde::link($m['url'], ($menu_view == 'icon') ? Horde::stripAccessKey($m['text']) : '',
+ isset($m['class']) ? $m['class'] : '',
+ isset($m['target']) ? $m['target'] : '',
+ isset($m['onclick']) ? $m['onclick'] : '',
+ '', $accesskey);
+
+ $output .= sprintf("\n<li>%s%s%s</a></li>",
+ $link, $icon, ($menu_view != 'icon') ? Horde::highlightAccessKey($m['text'], $accesskey) : '');
+ }
+
+ return $output . '</ul>';
+ }
+
+ /**
+ * Any links to other Horde applications defined in an application's config
+ * file by the $conf['menu']['apps'] array are added to the menu array.
+ */
+ public function addAppLinks()
+ {
+ global $conf, $registry;
+
+ if (isset($conf['menu']['apps']) && is_array($conf['menu']['apps'])) {
+ foreach ($conf['menu']['apps'] as $app) {
+ if ($registry->get('status', $app) != 'inactive' && $registry->hasPermission($app, PERMS_SHOW)) {
+ $url = $registry->getInitialPage($app);
+ if (!is_a($url, 'PEAR_Error')) {
+ $this->add(Horde::url($url), $registry->get('name', $app), $registry->get('icon', $app), '');
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Add any other links found in $this->_menufile to be included in the
+ * menu.
+ */
+ public function addSiteLinks()
+ {
+ if (is_readable($this->_menufile)) {
+ include $this->_menufile;
+ if (isset($_menu) && is_array($_menu)) {
+ foreach ($_menu as $menuitem) {
+ $this->addArray($menuitem);
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks to see if the current url matches the given url.
+ *
+ * @return boolean Whether the given URL is the current location.
+ */
+ static public function isSelected($url)
+ {
+ $server_url = parse_url($_SERVER['PHP_SELF']);
+ $check_url = parse_url($url);
+
+ /* Try to match the item's path against the current script
+ filename as well as other possible URLs to this script. */
+ if (isset($check_url['path']) &&
+ (($check_url['path'] == $server_url['path']) ||
+ ($check_url['path'] . 'index.php' == $server_url['path']) ||
+ ($check_url['path'] . '/index.php' == $server_url['path']))) {
+ return true;
+ }
+
+ return false;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Registry:: class provides a set of methods for communication
+ * between Horde applications and keeping track of application
+ * configuration information.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Chuck Hagenbuch <chuck@horde.org>
+ * @author Jon Parise <jon@horde.org>
+ * @author Anil Madhavapeddy <anil@recoil.org>
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Core
+ */
+class Horde_Registry
+{
+ /* Session flags. */
+ const SESSION_NONE = 1;
+ const SESSION_READONLY = 2;
+
+ /**
+ * Singleton value.
+ *
+ * @var Horde_Registry
+ */
+ static protected $_instance;
+
+ /**
+ * Cached information.
+ *
+ * @var array
+ */
+ protected $_cache = array();
+
+ /**
+ * The Horde_Cache object.
+ *
+ * @var Horde_Cache
+ */
+ protected $_cacheob;
+
+ /**
+ * The last modified time of the newest modified registry file.
+ *
+ * @var integer
+ */
+ protected $_regmtime;
+
+ /**
+ * Stack of in-use applications.
+ *
+ * @var array
+ */
+ protected $_appStack = array();
+
+ /**
+ * The list of APIs.
+ *
+ * @param array
+ */
+ protected $_apis = array();
+
+ /**
+ * Cached values of the image directories.
+ *
+ * @param array
+ */
+ protected $_imgDir = array();
+
+ /**
+ * Hash storing information on each registry-aware application.
+ *
+ * @var array
+ */
+ public $applications = array();
+
+ /**
+ * Returns a reference to the global Horde_Registry object, only creating
+ * it if it doesn't already exist.
+ *
+ * This method must be invoked as:
+ * $registry = Horde_Registry::singleton()
+ *
+ * @param integer $session_flags Any session flags.
+ *
+ * @return Horde_Registry The Horde_Registry instance.
+ */
+ static public function singleton($session_flags = 0)
+ {
+ if (!isset(self::$_instance)) {
+ self::$_instance = new Horde_Registry($session_flags);
+ }
+
+ return self::$_instance;
+ }
+
+ /**
+ * Create a new Horde_Registry instance.
+ *
+ * @param integer $session_flags Any session flags.
+ *
+ * @throws Horde_Exception
+ */
+ protected function __construct($session_flags = 0)
+ {
+ /* Import and global Horde's configuration values. */
+ $this->_cache['conf-horde'] = Horde::loadConfiguration('conf.php', 'conf', 'horde');
+
+ $conf = $GLOBALS['conf'] = &$this->_cache['conf-horde'];
+
+ /* Initial Horde-wide settings. */
+
+ /* Set the maximum execution time in accordance with the config
+ * settings. */
+ error_reporting(0);
+ set_time_limit($conf['max_exec_time']);
+
+ /* Set the error reporting level in accordance with the config
+ * settings. */
+ error_reporting($conf['debug_level']);
+
+ /* Set the umask according to config settings. */
+ if (isset($conf['umask'])) {
+ umask($conf['umask']);
+ }
+
+ /* Start a session. */
+ if ($session_flags & self::SESSION_NONE) {
+ /* Never start a session if the session flags include
+ SESSION_NONE. */
+ $_SESSION = array();
+ } else {
+ Horde::setupSessionHandler();
+ $old_error = error_reporting(0);
+ session_start();
+ if ($session_flags & self::SESSION_READONLY) {
+ /* Close the session immediately so no changes can be
+ made but values are still available. */
+ session_write_close();
+ }
+ error_reporting($old_error);
+
+ if (!isset($_SESSION['_registry'])) {
+ $_SESSION['_registry'] = array();
+ }
+ }
+
+ /* Initialize the localization routines and variables. We can't use
+ * Horde_Nls::setLanguageEnvironment() here because that depends on the
+ * registry to be already initialized. */
+ Horde_Nls::setLang();
+ Horde_Nls::setTextdomain('horde', HORDE_BASE . '/locale', Horde_Nls::getCharset());
+ Horde_String::setDefaultCharset(Horde_Nls::getCharset());
+
+ /* Check for caching availability. Using cache while not authenticated
+ * isn't possible because, although storage is possible, retrieval
+ * isn't since there is no MD5 sum in the session to use to build
+ * the cache IDs. */
+ if (Horde_Auth::getAuth()) {
+ try {
+ $this->_cacheob = Horde_Cache::singleton($conf['cache']['driver'], Horde::getDriverConfig('cache', $conf['cache']['driver']));
+ } catch (Horde_Exception $e) {}
+ }
+
+ $this->_regmtime = max(filemtime(HORDE_BASE . '/config/registry.php'),
+ filemtime(HORDE_BASE . '/config/registry.d'));
+
+ $vhost = null;
+ if (!empty($conf['vhosts'])) {
+ $vhost = HORDE_BASE . '/config/registry-' . $conf['server']['name'] . '.php';
+ if (file_exists($vhost)) {
+ $this->_regmtime = max($this->_regmtime, filemtime($vhost));
+ } else {
+ $vhost = null;
+ }
+ }
+
+ /* Always need to load applications information. */
+ $this->_loadApplicationsCache($vhost);
+
+ /* Stop system if Horde is inactive. */
+ if ($this->applications['horde']['status'] == 'inactive') {
+ Horde::fatal(_("This system is currently deactivated."), __FILE__, __LINE__);
+ }
+
+ /* Create the global Perms object. */
+ $GLOBALS['perms'] = &Perms::singleton();
+
+ /* Attach javascript notification listener. */
+ $notification = &Horde_Notification::singleton();
+ $notification->attach('javascript');
+ }
+
+ /**
+ * Stores cacheable member variables in the session at shutdown.
+ */
+ public function __destruct()
+ {
+ /* Register access key logger for translators. */
+ if (!empty($GLOBALS['conf']['log_accesskeys'])) {
+ Horde::getAccessKey(null, null, true);
+ }
+
+ /* Register memory tracker if logging in debug mode. */
+ if (!empty($GLOBALS['conf']['log']['enabled']) &&
+ ($GLOBALS['conf']['log']['priority'] == PEAR_LOG_DEBUG) &&
+ function_exists('memory_get_peak_usage')) {
+ Horde::logMessage('Max memory usage: ' . memory_get_peak_usage(true) . ' bytes', __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ }
+ }
+
+ /**
+ * TODO
+ */
+ public function __get($api)
+ {
+ if (in_array($api, $this->listAPIs())) {
+ return new Horde_Registry_Caller($this, $api);
+ }
+ }
+
+ /**
+ * Clone should never be called on this object. If it is, die.
+ */
+ public function __clone()
+ {
+ Horde::fatal('Horde_Registry objects should never be cloned.', __FILE__, __LINE__);
+ }
+
+ /**
+ * Clear the registry cache.
+ */
+ public function clearCache()
+ {
+ unset($_SESSION['_registry']);
+ $this->_saveCacheVar('apicache', true);
+ $this->_saveCacheVar('appcache', true);
+ }
+
+ /**
+ * Fills the registry's application cache with application information.
+ *
+ * @param string $vhost TODO
+ */
+ protected function _loadApplicationsCache($vhost)
+ {
+ /* First, try to load from cache. */
+ if ($this->_loadCacheVar('appcache')) {
+ $this->applications = $this->_cache['appcache'][0];
+ $this->_cache['interfaces'] = $this->_cache['appcache'][1];
+ return;
+ }
+
+ $this->_cache['interfaces'] = array();
+
+ /* Read the registry configuration files. */
+ require HORDE_BASE . '/config/registry.php';
+ $files = glob(HORDE_BASE . '/config/registry.d/*.php');
+ if ($files) {
+ foreach ($files as $r) {
+ include $r;
+ }
+ }
+
+ if ($vhost) {
+ include $vhost;
+ }
+
+ /* Scan for all APIs provided by each app, and set other common
+ * defaults like templates and graphics. */
+ foreach (array_keys($this->applications) as $appName) {
+ $app = &$this->applications[$appName];
+ if ($app['status'] == 'heading') {
+ continue;
+ }
+
+ if (isset($app['fileroot']) && !file_exists($app['fileroot'])) {
+ $app['status'] = 'inactive';
+ }
+
+ if (($app['status'] != 'inactive') &&
+ isset($app['provides']) &&
+ (($app['status'] != 'admin') || Horde_Auth::isAdmin())) {
+ if (is_array($app['provides'])) {
+ foreach ($app['provides'] as $interface) {
+ $this->_cache['interfaces'][$interface] = $appName;
+ }
+ } else {
+ $this->_cache['interfaces'][$app['provides']] = $appName;
+ }
+ }
+
+ if (!isset($app['templates']) && isset($app['fileroot'])) {
+ $app['templates'] = $app['fileroot'] . '/templates';
+ }
+ if (!isset($app['jsuri']) && isset($app['webroot'])) {
+ $app['jsuri'] = $app['webroot'] . '/js';
+ }
+ if (!isset($app['jsfs']) && isset($app['fileroot'])) {
+ $app['jsfs'] = $app['fileroot'] . '/js';
+ }
+ if (!isset($app['themesuri']) && isset($app['webroot'])) {
+ $app['themesuri'] = $app['webroot'] . '/themes';
+ }
+ if (!isset($app['themesfs']) && isset($app['fileroot'])) {
+ $app['themesfs'] = $app['fileroot'] . '/themes';
+ }
+ }
+
+ $this->_cache['appcache'] = array(
+ // Index 0
+ $this->applications,
+ // Index 1
+ $this->_cache['interfaces']
+ );
+ $this->_saveCacheVar('appcache');
+ }
+
+ /**
+ * Fills the registry's API cache with the available services and types.
+ */
+ protected function _loadApiCache()
+ {
+ /* First, try to load from cache. */
+ if ($this->_loadCacheVar('apicache')) {
+ $this->_cache['api'] = $this->_cache['apicache'][0];
+ $this->_cache['type'] = $this->_cache['apicache'][1];
+ return;
+ }
+
+ /* Generate api/type cache. */
+ $status = array('active', 'notoolbar', 'hidden');
+ if (Horde_Auth::isAdmin()) {
+ $status[] = 'admin';
+ }
+
+ $this->_cache['api'] = $this->_cache['type'] = array();
+
+ $apps = $this->listApps($status);
+ foreach ($apps as $app) {
+ $_services = $_types = null;
+ $api = $this->get('fileroot', $app) . '/lib/api.php';
+ if (is_readable($api)) {
+ include_once $api;
+ }
+ $this->_cache['api'][$app] = $_services;
+ if (!is_null($_types)) {
+ foreach ($_types as $type => $params) {
+ /* Prefix non-Horde types with the application name. */
+ $prefix = ($app == 'horde') ? '' : "${app}_";
+ $this->_cache['type'][$prefix . $type] = $params;
+ }
+ }
+ }
+
+ $this->_cache['apicache'] = array(
+ // Index 0
+ $this->_cache['api'],
+ // Index 1
+ $this->_cache['type']
+ );
+ $this->_saveCacheVar('apicache');
+ }
+
+ /**
+ * Return a list of the installed and registered applications.
+ *
+ * @param array $filter An array of the statuses that should be
+ * returned. Defaults to non-hidden.
+ * @param boolean $assoc Associative array with app names as keys.
+ * @param integer $perms The permission level to check for in the list.
+ *
+ * @return array List of apps registered with Horde. If no
+ * applications are defined returns an empty array.
+ */
+ public function listApps($filter = null, $assoc = false,
+ $perms = PERMS_SHOW)
+ {
+ $apps = array();
+ $ahandler = defined('AUTH_HANDLER');
+ if (is_null($filter)) {
+ $filter = array('notoolbar', 'active');
+ }
+
+ foreach ($this->applications as $app => $params) {
+ if (in_array($params['status'], $filter) &&
+ ($ahandler || $this->hasPermission($app, $perms))) {
+ $apps[$app] = $app;
+ }
+ }
+
+ return $assoc ? $apps : array_values($apps);
+ }
+
+ /**
+ * Returns all available registry APIs.
+ *
+ * @return array The API list.
+ */
+ public function listAPIs()
+ {
+ if (empty($this->_apis)) {
+ foreach (array_keys($this->_cache['interfaces']) as $interface) {
+ list($api,) = explode('/', $interface, 2);
+ $this->_apis[$api] = true;
+ }
+ }
+
+ return array_keys($this->_apis);
+ }
+
+ /**
+ * Returns all of the available registry methods, or alternately
+ * only those for a specified API.
+ *
+ * @param string $api Defines the API for which the methods shall be
+ * returned.
+ *
+ * @return array The method list.
+ */
+ public function listMethods($api = null)
+ {
+ $methods = array();
+
+ $this->_loadApiCache();
+
+ foreach (array_keys($this->applications) as $app) {
+ if (isset($this->applications[$app]['provides'])) {
+ $provides = $this->applications[$app]['provides'];
+ if (!is_array($provides)) {
+ $provides = array($provides);
+ }
+ foreach ($provides as $method) {
+ if (strpos($method, '/') !== false) {
+ if (is_null($api) ||
+ (substr($method, 0, strlen($api)) == $api)) {
+ $methods[$method] = true;
+ }
+ } elseif (is_null($api) || ($method == $api)) {
+ if (isset($this->_cache['api'][$app])) {
+ foreach (array_keys($this->_cache['api'][$app]) as $service) {
+ $methods[$method . '/' . $service] = true;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return array_keys($methods);
+ }
+
+ /**
+ * Returns all of the available registry data types.
+ *
+ * @return array The data type list.
+ */
+ public function listTypes()
+ {
+ $this->_loadApiCache();
+ return $this->_cache['type'];
+ }
+
+ /**
+ * Returns a method's signature.
+ *
+ * @param string $method The full name of the method to check for.
+ *
+ * @return array A two dimensional array. The first element contains an
+ * array with the parameter names, the second one the return
+ * type.
+ */
+ public function getSignature($method)
+ {
+ if (!($app = $this->hasMethod($method))) {
+ return false;
+ }
+
+ $this->_loadApiCache();
+
+ list(,$function) = explode('/', $method, 2);
+ if (!empty($function) &&
+ isset($this->_cache['api'][$app][$function]['type']) &&
+ isset($this->_cache['api'][$app][$function]['args'])) {
+ return array($this->_cache['api'][$app][$function]['args'], $this->_cache['api'][$app][$function]['type']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Determine if an interface is implemented by an active application.
+ *
+ * @param string $interface The interface to check for.
+ *
+ * @return mixed The application implementing $interface if we have it,
+ * false if the interface is not implemented.
+ */
+ public function hasInterface($interface)
+ {
+ return !empty($this->_cache['interfaces'][$interface]) ?
+ $this->_cache['interfaces'][$interface] :
+ false;
+ }
+
+ /**
+ * Determine if a method has been registered with the registry.
+ *
+ * @param string $method The full name of the method to check for.
+ * @param string $app Only check this application.
+ *
+ * @return mixed The application implementing $method if we have it,
+ * false if the method doesn't exist.
+ */
+ public function hasMethod($method, $app = null)
+ {
+ if (is_null($app)) {
+ list($interface, $call) = explode('/', $method, 2);
+ if (!empty($this->_cache['interfaces'][$method])) {
+ $app = $this->_cache['interfaces'][$method];
+ } elseif (!empty($this->_cache['interfaces'][$interface])) {
+ $app = $this->_cache['interfaces'][$interface];
+ } else {
+ return false;
+ }
+ } else {
+ $call = $method;
+ }
+
+ $this->_loadApiCache();
+
+ return empty($this->_cache['api'][$app][$call]) ? false : $app;
+ }
+
+ /**
+ * Return the hook corresponding to the default package that
+ * provides the functionality requested by the $method
+ * parameter. $method is a string consisting of
+ * "packagetype/methodname".
+ *
+ * @param string $method The method to call.
+ * @param array $args Arguments to the method.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function call($method, $args = array())
+ {
+ list($interface, $call) = explode('/', $method, 2);
+
+ if (!empty($this->_cache['interfaces'][$method])) {
+ $app = $this->_cache['interfaces'][$method];
+ } elseif (!empty($this->_cache['interfaces'][$interface])) {
+ $app = $this->_cache['interfaces'][$interface];
+ } else {
+ return PEAR::raiseError('The method "' . $method . '" is not defined in the Horde Registry.');
+ }
+
+ return $this->callByPackage($app, $call, $args);
+ }
+
+ /**
+ * Output the hook corresponding to the specific package named.
+ *
+ * @param string $app The application being called.
+ * @param string $call The method to call.
+ * @param array $args Arguments to the method.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function callByPackage($app, $call, $args = array())
+ {
+ /* Note: calling hasMethod() makes sure that we've cached
+ * $app's services and included the API file, so we don't try
+ * to do it again explicitly in this method. */
+ if (!$this->hasMethod($call, $app)) {
+ return PEAR::raiseError(sprintf('The method "%s" is not defined in the API for %s.', $call, $app));
+ }
+
+ /* Load the API now. */
+ $api = $this->get('fileroot', $app) . '/lib/api.php';
+ if (is_readable($api)) {
+ include_once $api;
+ }
+
+ /* Make sure that the function actually exists. */
+ $function = '_' . $app . '_' . str_replace('/', '_', $call);
+ if (!function_exists($function)) {
+ return PEAR::raiseError('The function implementing ' . $call . ' (' . $function . ') is not defined in ' . $app . '\'s API.');
+ }
+
+ $checkPerms = isset($this->_cache['api'][$app][$call]['checkperms'])
+ ? $this->_cache['api'][$app][$call]['checkperms']
+ : true;
+
+ /* Switch application contexts now, if necessary, before
+ * including any files which might do it for us. Return an
+ * error immediately if pushApp() fails. */
+ $pushed = $this->pushApp($app, $checkPerms);
+ if (is_a($pushed, 'PEAR_Error')) {
+ return $pushed;
+ }
+
+ $res = call_user_func_array($function, $args);
+
+ /* If we changed application context in the course of this
+ * call, undo that change now. */
+ if ($pushed === true) {
+ $this->popApp();
+ }
+
+ return $res;
+ }
+
+ /**
+ * Return the hook corresponding to the default package that
+ * provides the functionality requested by the $method
+ * parameter. $method is a string consisting of
+ * "packagetype/methodname".
+ *
+ * @param string $method The method to link to.
+ * @param array $args Arguments to the method.
+ * @param mixed $extra Extra, non-standard arguments to the method.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function link($method, $args = array(), $extra = '')
+ {
+ list($interface, $call) = explode('/', $method, 2);
+
+ if (!empty($this->_cache['interfaces'][$method])) {
+ $app = $this->_cache['interfaces'][$method];
+ } elseif (!empty($this->_cache['interfaces'][$interface])) {
+ $app = $this->_cache['interfaces'][$interface];
+ } else {
+ return PEAR::raiseError('The method "' . $method . '" is not defined in the Horde Registry.');
+ }
+
+ return $this->linkByPackage($app, $call, $args, $extra);
+ }
+
+ /**
+ * Output the hook corresponding to the specific package named.
+ *
+ * @param string $app The application being called.
+ * @param string $call The method to link to.
+ * @param array $args Arguments to the method.
+ * @param mixed $extra Extra, non-standard arguments to the method.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function linkByPackage($app, $call, $args = array(), $extra = '')
+ {
+ /* Note: calling hasMethod makes sure that we've cached $app's
+ * services and included the API file, so we don't try to do
+ * it it again explicitly in this method. */
+ if (!$this->hasMethod($call, $app)) {
+ return PEAR::raiseError('The method "' . $call . '" is not defined in ' . $app . '\'s API.');
+ }
+
+ /* Make sure the link is defined. */
+ $this->_loadApiCache();
+ if (empty($this->_cache['api'][$app][$call]['link'])) {
+ return PEAR::raiseError('The link ' . $call . ' is not defined in ' . $app . '\'s API.');
+ }
+
+ /* Initial link value. */
+ $link = $this->_cache['api'][$app][$call]['link'];
+
+ /* Fill in html-encoded arguments. */
+ foreach ($args as $key => $val) {
+ $link = str_replace('%' . $key . '%', htmlentities($val), $link);
+ }
+ if (isset($this->applications[$app]['webroot'])) {
+ $link = str_replace('%application%', $this->get('webroot', $app), $link);
+ }
+
+ /* Replace htmlencoded arguments that haven't been specified with
+ an empty string (this is where the default would be substituted
+ in a stricter registry implementation). */
+ $link = preg_replace('|%.+%|U', '', $link);
+
+ /* Fill in urlencoded arguments. */
+ foreach ($args as $key => $val) {
+ $link = str_replace('|' . Horde_String::lower($key) . '|', urlencode($val), $link);
+ }
+
+ /* Append any extra, non-standard arguments. */
+ if (is_array($extra)) {
+ $extra_args = '';
+ foreach ($extra as $key => $val) {
+ $extra_args .= '&' . urlencode($key) . '=' . urlencode($val);
+ }
+ } else {
+ $extra_args = $extra;
+ }
+ $link = str_replace('|extra|', $extra_args, $link);
+
+ /* Replace html-encoded arguments that haven't been specified with
+ an empty string (this is where the default would be substituted
+ in a stricter registry implementation). */
+ $link = preg_replace('|\|.+\||U', '', $link);
+
+ return $link;
+ }
+
+ /**
+ * Replace any %application% strings with the filesystem path to the
+ * application.
+ *
+ * @param string $path The application string.
+ * @param string $app The application being called.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function applicationFilePath($path, $app = null)
+ {
+ if (is_null($app)) {
+ $app = $this->getApp();
+ }
+
+ if (!isset($this->applications[$app])) {
+ return PEAR::raiseError(sprintf(_("\"%s\" is not configured in the Horde Registry."), $app));
+ }
+
+ return str_replace('%application%', $this->applications[$app]['fileroot'], $path);
+ }
+
+ /**
+ * Replace any %application% strings with the web path to the application.
+ *
+ * @param string $path The application string.
+ * @param string $app The application being called.
+ *
+ * @return TODO
+ * Returns PEAR_Error on error.
+ */
+ public function applicationWebPath($path, $app = null)
+ {
+ if (!isset($app)) {
+ $app = $this->getApp();
+ }
+
+ return str_replace('%application%', $this->applications[$app]['webroot'], $path);
+ }
+
+ /**
+ * Set the current application, adding it to the top of the Horde
+ * application stack. If this is the first application to be
+ * pushed, retrieve session information as well.
+ *
+ * pushApp() also reads the application's configuration file and
+ * sets up its global $conf hash.
+ *
+ * @param string $app The name of the application to push.
+ * @param boolean $checkPerms Make sure that the current user has
+ * permissions to the application being loaded
+ * Defaults to true. Should ONLY be disabled
+ * by system scripts (cron jobs, etc.) and
+ * scripts that handle login.
+ *
+ * @return boolean Whether or not the _appStack was modified.
+ * @throws Horde_Exception
+ */
+ public function pushApp($app, $checkPerms = true)
+ {
+ if ($app == $this->getApp()) {
+ return false;
+ }
+
+ /* Bail out if application is not present or inactive. */
+ if (!isset($this->applications[$app]) ||
+ $this->applications[$app]['status'] == 'inactive' ||
+ ($this->applications[$app]['status'] == 'admin' && !Horde_Auth::isAdmin())) {
+ Horde::fatal($app . ' is not activated', __FILE__, __LINE__);
+ }
+
+ /* If permissions checking is requested, return an error if the
+ * current user does not have read perms to the application being
+ * loaded. We allow access:
+ *
+ * - To all admins.
+ * - To all authenticated users if no permission is set on $app.
+ * - To anyone who is allowed by an explicit ACL on $app. */
+ if ($checkPerms && !$this->hasPermission($app)) {
+ Horde::logMessage(sprintf('%s does not have READ permission for %s', Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $app), __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ return PEAR::raiseError(sprintf(_('%s is not authorised for %s.'), Horde_Auth::getAuth() ? 'User ' . Horde_Auth::getAuth() : 'Guest user', $this->applications[$app]['name']), 'permission_denied');
+ }
+
+ /* Set up autoload paths for the current application. This needs to
+ * be done here because it is possible to try to load app-specific
+ * libraries from other applications. */
+ $app_lib = $this->get('fileroot', $app) . '/lib';
+ Horde_Autoloader::addClassPath($app_lib);
+ Horde_Autoloader::addClassPattern('/^' . $app . '_/i', $app_lib);
+
+ /* Chicken and egg problem: the language environment has to be loaded
+ * before loading the configuration file, because it might contain
+ * gettext strings. Though the preferences can specify a different
+ * language for this app, the have to be loaded after the
+ * configuration, because they rely on configuration settings. So try
+ * with the current language, and reset the language later. */
+ Horde_Nls::setLanguageEnvironment($GLOBALS['language'], $app);
+
+ /* Import this application's configuration values. */
+ $this->importConfig($app);
+
+ /* Load preferences after the configuration has been loaded to make
+ * sure the prefs file has all the information it needs. */
+ $this->loadPrefs($app);
+
+ /* Reset the language in case there is a different one selected in the
+ * preferences. */
+ $language = '';
+ if (isset($GLOBALS['prefs'])) {
+ $language = $GLOBALS['prefs']->getValue('language');
+ if ($language != $GLOBALS['language']) {
+ Horde_Nls::setLanguageEnvironment($language, $app);
+ }
+ }
+
+ /* Once we know everything succeeded and is in a consistent state
+ * again, push the new application onto the stack. */
+ $this->_appStack[] = $app;
+
+ /* Call post-push hook. */
+ Horde::callHook('_horde_hook_post_pushapp', array($app), 'horde', null);
+
+ return true;
+ }
+
+ /**
+ * Remove the current app from the application stack, setting the current
+ * app to whichever app was current before this one took over.
+ *
+ * @return string The name of the application that was popped.
+ * @throws Horde_Exception
+ */
+ public function popApp()
+ {
+ /* Pop the current application off of the stack. */
+ $previous = array_pop($this->_appStack);
+
+ /* Import the new active application's configuration values
+ * and set the gettext domain and the preferred language. */
+ $app = $this->getApp();
+ if ($app) {
+ $this->importConfig($app);
+ $this->loadPrefs($app);
+ $language = $GLOBALS['prefs']->getValue('language');
+ Horde_Nls::setLanguageEnvironment($language, $app);
+ }
+
+ return $previous;
+ }
+
+ /**
+ * Return the current application - the app at the top of the application
+ * stack.
+ *
+ * @return string The current application.
+ */
+ public function getApp()
+ {
+ return end($this->_appStack);
+ }
+
+ /**
+ * Check permissions on an application.
+ *
+ * @param string $app The name of the application
+ * @param integer $perms The permission level to check for.
+ *
+ * @return boolean Whether access is allowed.
+ */
+ public function hasPermission($app, $perms = PERMS_READ)
+ {
+ return Horde_Auth::isAdmin() ||
+ ($GLOBALS['perms']->exists($app)
+ ? $GLOBALS['perms']->hasPermission($app, Horde_Auth::getAuth(), $perms)
+ : (bool)Horde_Auth::getAuth());
+ }
+
+ /**
+ * Reads the configuration values for the given application and imports
+ * them into the global $conf variable.
+ *
+ * @param string $app The name of the application.
+ *
+ * @throws Horde_Exception
+ */
+ public function importConfig($app)
+ {
+ if (($app != 'horde') &&
+ !$this->_loadCacheVar('conf-' . $app)) {
+ $this->_cache['conf-' . $app] = Horde_Array::array_merge_recursive_overwrite($this->_cache['conf-horde'], Horde::loadConfiguration('conf.php', 'conf', $app));
+ $this->_saveCacheVar('conf-' . $app);
+ }
+
+ $GLOBALS['conf'] = &$this->_cache['conf-' . $app];
+ }
+
+ /**
+ * Loads the preferences for the current user for the current application
+ * and imports them into the global $prefs variable.
+ *
+ * @param string $app The name of the application.
+ */
+ public function loadPrefs($app = null)
+ {
+ require_once 'Horde/Prefs.php';
+
+ if (is_null($app)) {
+ $app = $this->getApp();
+ }
+
+ /* If there is no logged in user, return an empty Prefs::
+ * object with just default preferences. */
+ if (!Horde_Auth::getAuth()) {
+ $GLOBALS['prefs'] = &Prefs::factory('session', $app, '', '', null, false);
+ } else {
+ if (!isset($GLOBALS['prefs']) || $GLOBALS['prefs']->getUser() != Horde_Auth::getAuth()) {
+ $GLOBALS['prefs'] = &Prefs::factory($GLOBALS['conf']['prefs']['driver'], $app,
+ Horde_Auth::getAuth(), Horde_Auth::getCredential('password'));
+ } else {
+ $GLOBALS['prefs']->retrieve($app);
+ }
+ }
+ }
+
+ /**
+ * Unload preferences from an application or (if no application is
+ * specified) from ALL applications. Useful when a user has logged
+ * out but you need to continue on the same page, etc.
+ *
+ * After unloading, if there is an application on the app stack to
+ * load preferences from, then we reload a fresh set.
+ *
+ * @param string $app The application to unload prefrences for. If null,
+ * ALL preferences are reset.
+ */
+ public function unloadPrefs($app = null)
+ {
+ // TODO: $app not being used?
+ if ($this->getApp()) {
+ $this->loadPrefs();
+ }
+ }
+
+ /**
+ * Return the requested configuration parameter for the specified
+ * application. If no application is specified, the value of
+ * the current application is used. However, if the parameter is not
+ * present for that application, the Horde-wide value is used instead.
+ * If that is not present, we return null.
+ *
+ * @param string $parameter The configuration value to retrieve.
+ * @param string $app The application to get the value for.
+ *
+ * @return string The requested parameter, or null if it is not set.
+ */
+ public function get($parameter, $app = null)
+ {
+ if (is_null($app)) {
+ $app = $this->getApp();
+ }
+
+ if (isset($this->applications[$app][$parameter])) {
+ $pval = $this->applications[$app][$parameter];
+ } else {
+ $pval = ($parameter == 'icon')
+ ? $this->getImageDir($app) . '/' . $app . '.png'
+ : (isset($this->applications['horde'][$parameter]) ? $this->applications['horde'][$parameter] : null);
+ }
+
+ return ($parameter == 'name')
+ ? _($pval)
+ : $pval;
+ }
+
+ /**
+ * Function to work out an application's graphics URI, optionally taking
+ * into account any themes directories that may be set up.
+ *
+ * @param string $app The application for which to get the image
+ * directory. If blank will default to current
+ * application.
+ * @param boolean $usetheme Take into account any theme directory?
+ *
+ * @return string The image directory uri path.
+ */
+ public function getImageDir($app = null, $usetheme = true)
+ {
+ if (empty($app)) {
+ $app = $this->getApp();
+ }
+
+ if ($this->get('status', $app) == 'heading') {
+ $app = 'horde';
+ }
+
+ $sig = strval($app . '|' . $usetheme);
+
+ if (isset($this->_imgDir[$sig])) {
+ return $this->_imgDir[$sig];
+ }
+
+ /* This is the default location for the graphics. */
+ $this->_imgDir[$sig] = $this->get('themesuri', $app) . '/graphics';
+
+ /* Figure out if this is going to be overridden by any theme
+ * settings. */
+ if ($usetheme &&
+ isset($GLOBALS['prefs']) &&
+ ($theme = $GLOBALS['prefs']->getValue('theme'))) {
+ /* Since theme information is so limited, store directly in the
+ * session. */
+ if (!isset($_SESSION['_registry']['theme'][$theme][$app])) {
+ $_SESSION['_registry']['theme'][$theme][$app] = file_exists($this->get('themesfs', $app) . '/' . $theme . '/themed_graphics');
+ }
+
+ if ($_SESSION['_registry']['theme'][$theme][$app]) {
+ $this->_imgDir[$sig] = $this->get('themesuri', $app) . '/' . $theme . '/graphics';
+ }
+ }
+
+ return $this->_imgDir[$sig];
+ }
+
+ /**
+ * Query the initial page for an application - the webroot, if there is no
+ * initial_page set, and the initial_page, if it is set.
+ *
+ * @param string $app The name of the application.
+ *
+ * @return string URL pointing to the inital page of the application.
+ * Returns PEAR_Error on error.
+ */
+ public function getInitialPage($app = null)
+ {
+ if (is_null($app)) {
+ $app = $this->getApp();
+ }
+
+ return isset($this->applications[$app])
+ ? $this->applications[$app]['webroot'] . '/' . (isset($this->applications[$app]['initial_page']) ? $this->applications[$app]['initial_page'] : '')
+ : PEAR::raiseError(sprintf(_("\"%s\" is not configured in the Horde Registry."), $app));
+ }
+
+ /**
+ * Saves a cache variable.
+ *
+ * @param string $name Cache variable name.
+ * @param boolean $expire Expire the entry?
+ */
+ protected function _saveCacheVar($name, $expire = false)
+ {
+ if ($this->_cacheob) {
+ if ($expire) {
+ if ($id = $this->_getCacheId($name)) {
+ $this->_cacheob->expire($id);
+ }
+ } else {
+ $data = serialize($this->_cache[$name]);
+ $_SESSION['_registry']['md5'][$name] = $md5sum = hash('md5', $data);
+ $id = $this->_getCacheId($name, false) . '|' . $md5sum;
+ $this->_cacheob->set($id, $data, 86400);
+ Horde::logMessage('Horde_Registry: stored ' . $name . ' with cache ID ' . $id, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ }
+ }
+ }
+
+ /**
+ * Retrieves a cache variable.
+ *
+ * @param string $name Cache variable name.
+ *
+ * @return boolean True if value loaded from cache.
+ */
+ protected function _loadCacheVar($name)
+ {
+ if (isset($this->_cache[$name])) {
+ return true;
+ }
+
+ if ($this->_cacheob &&
+ ($id = $this->_getCacheId($name))) {
+ $res = $this->_cacheob->get($id, 86400);
+ if ($res !== false) {
+ $this->_cache[$name] = unserialize($res);
+ Horde::logMessage('Horde_Registry: retrieved ' . $name . ' with cache ID ' . $id, __FILE__, __LINE__, PEAR_LOG_DEBUG);
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Get the cache storage ID for a particular cache name.
+ *
+ * @param string $name Cache variable name.
+ * @param string $md5 Append MD5 value?
+ *
+ * @return mixed The cache ID or false if cache entry doesn't exist in
+ * the session.
+ */
+ protected function _getCacheId($name, $md5 = true)
+ {
+ $id = 'horde_registry_' . $name . '|' . $this->_regmtime;
+
+ if (!$md5) {
+ return $id;
+ } elseif (isset($_SESSION['_registry']['md5'][$name])) {
+ return $id . '|' . $_SESSION['_registry']['md5'][$name];
+ } else {
+ return false;
+ }
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * TODO
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @package Core
+ */
+class Horde_Registry_Caller
+{
+ /**
+ * TODO
+ */
+ protected $registry;
+
+ /**
+ * TODO
+ */
+ protected $api;
+
+ /**
+ * TODO
+ */
+ public function __construct($registry, $api)
+ {
+ $this->registry = $registry;
+ $this->api = $api;
+ }
+
+ /**
+ * TODO
+ */
+ public function __call($method, $args)
+ {
+ return $this->registry->call($this->api . '/' . $method, $args);
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Class to make an "official" Horde or application release.
+ *
+ * Copyright 1999 Mike Hardy
+ * Copyright 2004-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Mike Hardy
+ * @author Jan Schneider <jan@horde.org>
+ * @package Core
+ */
+class Horde_Release
+{
+ /**
+ * Default options.
+ *
+ * @var array
+ */
+ protected $_options = array(
+ 'test' => false,
+ 'nocommit' => false,
+ 'noftp' => false,
+ 'noannounce' => false,
+ 'nofreshmeat' => false,
+ 'nowhups' => false,
+ );
+
+ /**
+ * Version number of release.
+ *
+ * @var string
+ */
+ protected $_sourceVersionString;
+
+ /**
+ * Version number of previous release.
+ *
+ * @var string
+ */
+ protected $_oldSourceVersionString;
+
+ /**
+ * Version number of next release.
+ *
+ * @var string
+ */
+ protected $_newSourceVersionString;
+
+ /**
+ * Version number of next release for docs/CHANGES.
+ *
+ * @var string
+ */
+ protected $_newSourceVersionStringPlain;
+
+ /**
+ * Major version number of Horde compatible to this release.
+ *
+ * @var string
+ */
+ protected $_hordeVersionString;
+
+ /**
+ * Major version number of Horde compatible to the previous release.
+ *
+ * @var string
+ */
+ protected $_oldHordeVersionString;
+
+ /**
+ * CVS tag of release.
+ *
+ * @var string
+ */
+ protected $_tagVersionString;
+
+ /**
+ * CVS tag of previous release.
+ *
+ * @var string
+ */
+ protected $_oldTagVersionString;
+
+ /**
+ * Revision number of CHANGES file.
+ *
+ * @var string
+ */
+ protected $_changelogVersion;
+
+ /**
+ * Revision number of previous CHANGES file.
+ *
+ * @var string
+ */
+ protected $_oldChangelogVersion;
+
+ /**
+ * Version string to use in Whups
+ *
+ * @var string
+ */
+ protected $_ticketVersion;
+
+ /**
+ * Version description to use in Whups
+ *
+ * @var string
+ */
+ protected $_ticketVersionDesc = '';
+
+ /**
+ * Directory name of unpacked tarball.
+ *
+ * @var string
+ */
+ protected $_directoryName;
+
+ /**
+ * Directory name of unpacked previous tarball.
+ *
+ * @var string
+ */
+ protected $_oldDirectoryName;
+
+ /**
+ * Filename of the tarball.
+ *
+ * @var string
+ */
+ protected $_tarballName;
+
+ /**
+ * MD5 sum of the tarball.
+ *
+ * @var string
+ */
+ protected $_tarballMD5;
+
+ /**
+ * Whether or not to create a patch file.
+ *
+ * @var boolean
+ */
+ protected $_makeDiff = false;
+
+ /**
+ * The list of binary diffs.
+ *
+ * @var array
+ */
+ protected $_binaryDiffs = array();
+
+ /**
+ * Whether or not we have an old version to compare against.
+ *
+ * @var boolean
+ */
+ protected $_oldVersion = false;
+
+ /**
+ * Filename of the gzip'ed patch file (without .gz extension).
+ *
+ * @var string
+ */
+ protected $_patchName;
+
+ /**
+ * MD5 sum of the patch file.
+ *
+ * @var string
+ */
+ protected $_patchMD5;
+
+ /**
+ * Whether or not this is a final release version.
+ *
+ * @var boolean
+ */
+ protected $_latest = true;
+
+ /**
+ * Load the configuration
+ */
+ public function __construct($options = array())
+ {
+ $this->_options = array_merge($this->_options, $options);
+ $cvsroot = getenv('CVSROOT');
+ if (empty($cvsroot)) {
+ putenv('CVSROOT=:ext:' . $this->_options['horde']['user'] . '@cvs.horde.org:/repository');
+ }
+ print 'CVSROOT ' . getenv('CVSROOT') . "\n";
+ if (!empty($this->_options['cvs']['cvs_rsh'])) {
+ putenv('CVS_RSH=' . $this->_options['cvs']['cvs_rsh']);
+ }
+ print 'CVS_RSH ' . getenv('CVS_RSH') . "\n";
+ }
+
+ /**
+ * Delete the directory given as an argument
+ */
+ public function deleteDirectory($directory)
+ {
+ print "Deleting directory $directory\n";
+ system("sudo rm -rf $directory");
+ }
+
+ /**
+ * tar and gzip the directory given as an argument
+ */
+ public function makeTarball()
+ {
+ print "Setting owner/group to 0/0\n";
+ system("sudo chown -R 0:0 $this->_directoryName");
+
+ print "Making tarball\n";
+ $this->_tarballName = $this->_directoryName . '.tar.gz';
+ if (file_exists($this->_tarballName)) {
+ unlink($this->_tarballName);
+ }
+ system("tar -zcf $this->_tarballName $this->_directoryName");
+ exec($this->_options['md5'] . ' ' . $this->_tarballName, $this->_tarballMD5);
+ }
+
+ /**
+ * Label all of the source here with the new label given as an argument
+ */
+ public function tagSource($directory = null, $version = null)
+ {
+ if (empty($directory)) {
+ $directory = $this->_directoryName;
+ }
+ if (empty($version)) {
+ $version = $this->_tagVersionString;
+ }
+ if (!$this->_options['nocommit']) {
+ print "Tagging source in $directory with tag $version\n";
+ system("cd $directory;cvs tag -F $version > /dev/null 2>&1");
+ } else {
+ print "NOT tagging source in $directory (would have been tag $version)\n";
+ }
+ }
+
+ /**
+ * Make a diff of the two directories given as arguments
+ */
+ public function diff()
+ {
+ $this->_patchName = 'patch-' . $this->_oldDirectoryName . str_replace($this->_options['module'], '', $this->_directoryName);
+ print "Making diff between $this->_oldDirectoryName and $this->_directoryName\n";
+ system("diff -uNr $this->_oldDirectoryName $this->_directoryName > $this->_patchName");
+
+ // Search for binary diffs
+ $this->_binaryDiffs = array();
+ $handle = fopen($this->_patchName, 'r');
+ if ($handle) {
+ while (!feof($handle)) {
+ // GNU diff reports binary diffs as the following:
+ // Binary files ./locale/de_DE/LC_MESSAGES/imp.mo and ../../horde/imp/locale/de_DE/LC_MESSAGES/imp.mo differ
+ if (preg_match("/^Binary files (.+) and (.+) differ$/i", rtrim(fgets($handle)), $matches)) {
+ // [1] = oldname, [2] = newname
+ $this->_binaryDiffs[] = ltrim(str_replace($this->_oldDirectoryName . '/', '', $matches[1]));
+ }
+ }
+ fclose($handle);
+ }
+ system("gzip -9f $this->_patchName");
+ exec($this->_options['md5'] . ' ' . $this->_patchName . '.gz', $this->_patchMD5);
+ }
+
+ /**
+ * Change the version file for the module in the directory specified to
+ * the version specified
+ */
+ public function updateVersionFile($directory, $version_string)
+ {
+ $module = $this->_options['module'];
+ $all_caps_module = strtoupper($module);
+ print "Updating version file for $module\n";
+
+ // construct the filenames
+ $filename_only = 'version.php';
+ $filename = $directory . '/lib/' . $filename_only;
+ $newfilename = $filename . '.new';
+
+ $oldfp = fopen($filename, 'r');
+ $newfp = fopen($newfilename, 'w');
+ while ($line = fgets($oldfp)) {
+ if (strstr($line, 'VERSION')) {
+ fwrite($newfp, "<?php define('{$all_caps_module}_VERSION', '$version_string') ?>\n");
+ } else {
+ fwrite($newfp, $line);
+ }
+ }
+ fclose($oldfp);
+ fclose($newfp);
+
+ system("mv -f $newfilename $filename");
+ if (!$this->_options['nocommit']) {
+ system("cd $directory/lib/; cvs commit -f -m \"Tarball script: building new $module release - $version_string\" $filename_only > /dev/null 2>&1");
+ }
+ }
+
+ /**
+ * Update the CHANGES file with the new version number
+ */
+ public function updateSentinel()
+ {
+ $module = $this->_options['module'];
+ $all_caps_module = strtoupper($module);
+ print "Updating CHANGES file for $module\n";
+
+ // construct the filenames
+ $filename_only = 'CHANGES';
+ $filename = $this->_directoryName . '/docs/' . $filename_only;
+ $newfilename = $filename . '.new';
+
+ $version = 'v' . substr($this->_newSourceVersionStringPlain, 0, strpos($this->_newSourceVersionString, '-'));
+
+ $oldfp = fopen($filename, 'r');
+ $newfp = fopen($newfilename, 'w');
+ fwrite($newfp, str_repeat('-', strlen($version)) . "\n$version\n" .
+ str_repeat('-', strlen($version)) . "\n\n\n\n\n");
+ while ($line = fgets($oldfp)) {
+ fwrite($newfp, $line);
+ }
+ fclose($oldfp);
+ fclose($newfp);
+
+ system("mv -f $newfilename $filename");
+ if (!$this->_options['nocommit']) {
+ system("cd {$this->_directoryName}/docs/; cvs commit -f -m \"Tarball script: building new $module release - {$this->_newSourceVersionString}\" $filename_only > /dev/null 2>&1");
+ }
+ }
+
+ /**
+ * get and save the revision number of the CHANGES file
+ */
+ public function saveChangelog($old = false, $directory = null)
+ {
+ if (empty($directory)) {
+ if ($old) {
+ $directory = './' . $this->_oldDirectoryName . '/docs';
+ } else {
+ $directory = './' . $this->_directoryName . '/docs';
+ }
+ }
+ if (!$old) {
+ include "$directory/RELEASE_NOTES";
+ if (strlen(htmlspecialchars($this->notes['fm']['changes'])) > 600) {
+ print "WARNING: freshmeat release notes are longer than 600 characters!\n";
+ }
+ }
+ exec("cd $directory; cvs status CHANGES", $output);
+ foreach ($output as $line) {
+ if (preg_match('/Repository revision:\s+([\d.]+)/', $line, $matches)) {
+ if ($old) {
+ $this->_oldChangelogVersion = $matches[1];
+ } else {
+ $this->_changelogVersion = $matches[1];
+ }
+ break;
+ }
+ }
+ }
+
+ /**
+ * work through the source directory given, cleaning things up by removing
+ * directories and files we don't want in the tarball
+ */
+ public function cleanDirectories($directory)
+ {
+ print "Cleaning source tree\n";
+ $directories = explode("\n", `find $directory -type d \\( -name CVS -o -name packaging -o -name framework \\) -print | sort -r`);
+ foreach ($directories as $dir) {
+ system("rm -rf $dir");
+ }
+ $cvsignores = explode("\n", `find $directory -name .cvsignore -print`);
+ foreach ($cvsignores as $file) {
+ if (!empty($file)) {
+ unlink($file);
+ }
+ }
+ }
+
+ /**
+ * Check out the tag we've been given to work with and move it to the
+ * directory name given
+ */
+ public function checkOutTag($mod_version, $directory, $module = null)
+ {
+ if (empty($module)) {
+ $module = $this->_options['module'];
+ }
+
+ if (@is_dir($module)) {
+ system("rm -rf $module");
+ }
+
+ // Use CVS to check the source out
+ if ($mod_version == 'HEAD') {
+ print "Checking out HEAD for $module\n";
+ $cmd = "cvs -q co -P $module > /dev/null";
+ system($cmd, $status);
+ } else {
+ print "Checking out tag $mod_version for $module\n";
+ $cmd = "cvs -q co -P -r$mod_version $module > /dev/null";
+ system($cmd, $status);
+ }
+ if ($status) {
+ die("\nThere was an error running the command\n$cmd\n");
+ }
+
+ // Move the source into the directory specified
+ print "Moving $module to $directory\n";
+ if (@is_dir($directory)) {
+ system("rm -rf $directory");
+ }
+ system("mv -f $module $directory");
+ }
+
+ /**
+ * Checkout and install framework
+ */
+ public function checkOutFramework($mod_version, $directory)
+ {
+ if ($this->_options['module'] == 'horde' &&
+ ($this->_options['branch'] == 'HEAD' ||
+ strstr($this->_options['branch'], 'FRAMEWORK'))) {
+ if ($this->_options['branch'] == 'HEAD') {
+ print "Checking out HEAD for framework\n";
+ } else {
+ print "Checking out tag $mod_version for framework\n";
+ }
+ $cmd = "cd $directory; cvs co -P -r$mod_version framework > /dev/null 2>&1; cd ..";
+ system($cmd, $status);
+ if ($status) {
+ die("\nThere was an error running the command\n$cmd\n");
+ }
+ print "Installing framework packages\n";
+ if (file_exists("./$directory/scripts/create-symlinks.php")) {
+ system("php ./$directory/scripts/create-symlinks.php --copy --src=./$directory/framework --dest=./$directory/lib");
+ } else {
+ system("horde-fw-symlinks.php --copy --src ./$directory/framework --dest ./$directory/lib");
+ }
+
+ print "Setting include path\n";
+ $filename = $directory . '/lib/core.php';
+ $newfilename = $filename . '.new';
+ $oldfp = fopen($filename, 'r');
+ $newfp = fopen($newfilename, 'w');
+ while ($line = fgets($oldfp)) {
+ fwrite($newfp, str_replace('// ini_set(\'include_path\'', 'ini_set(\'include_path\'', $line));
+ }
+ fclose($oldfp);
+ fclose($newfp);
+ system("mv -f $newfilename $filename");
+ }
+ }
+
+ /**
+ * Upload tarball to the FTP server
+ */
+ public function upload()
+ {
+ $module = $this->_options['module'];
+ $user = $this->_options['horde']['user'];
+ $identity = empty($this->_options['ssh']['identity']) ? '' : ' -i ' . $this->_options['ssh']['identity'];
+ $chmod = "chmod 664 /horde/ftp/pub/$module/$this->_tarballName;";
+ if ($this->_makeDiff) {
+ $chmod .= " chmod 664 /horde/ftp/pub/$module/patches/$this->_patchName.gz;";
+ }
+ if ($this->_latest &&
+ strpos($this->_options['branch'], 'RELENG') !== 0) {
+ $chmod .= " ln -sf $this->_tarballName /horde/ftp/pub/$module/$module-latest.tar.gz;";
+ }
+
+ if (!$this->_options['noftp']) {
+ print "Uploading $this->_tarballName to $user@ftp.horde.org:/horde/ftp/pub/$module/\n";
+ system("scp -P 35$identity $this->_tarballName $user@ftp.horde.org:/horde/ftp/pub/$module/");
+ if ($this->_makeDiff) {
+ print "Uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
+ system("scp -P 35$identity $this->_patchName.gz $user@ftp.horde.org:/horde/ftp/pub/$module/patches/");
+ }
+ print "Executing $chmod\n";
+ system("ssh -p 35 -l $user$identity ftp.horde.org '$chmod'");
+ } else {
+ print "NOT uploading $this->_tarballName to ftp.horde.org:/horde/ftp/pub/$module/\n";
+ if ($this->_makeDiff) {
+ print "NOT uploading $this->_patchName.gz to $user@ftp.horde.org:/horde/ftp/pub/$module/patches/\n";
+ }
+ print "NOT executing $chmod\n";
+ }
+ }
+
+ /**
+ * check if freshmeat announcement was successful.
+ */
+ protected function _fmVerify($fm)
+ {
+ if (is_a($fm, 'PEAR_Error')) {
+ print $fm->getMessage() . "\n";
+ return false;
+ } elseif (!is_array($fm)) {
+ var_dump($fm);
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * announce release to mailing lists and freshmeat.
+ */
+ public function announce($doc_dir = null)
+ {
+ $module = $this->_options['module'];
+ if (!isset($this->notes)) {
+ print "NOT announcing release, RELEASE_NOTES missing.\n";
+ return;
+ }
+ if (!empty($this->_options['noannounce']) ||
+ !empty($this->_options['nofreshmeat'])) {
+ print "NOT announcing release on freshmeat.net\n";
+ } else {
+ print "Announcing release on freshmeat.net\n";
+ }
+
+ if (empty($this->_options['nofreshmeat'])) {
+ $fm = Horde_RPC::request(
+ 'xmlrpc',
+ 'http://freshmeat.net/xmlrpc/',
+ 'login',
+ array('username' => $this->_options['fm']['user'],
+ 'password' => $this->_options['fm']['password']));
+ } else {
+ $fm = array('SID' => null);
+ }
+ if (empty($doc_dir)) {
+ $doc_dir = $module . '/docs';
+ }
+
+ $url_changelog = $this->_oldVersion
+ ? "http://cvs.horde.org/diff.php/$doc_dir/CHANGES?r1={$this->_oldChangelogVersion}&r2={$this->_changelogVersion}&ty=h"
+ : '';
+
+ if (is_a($fm, 'PEAR_Error')) {
+ print $fm->getMessage() . "\n";
+ } else {
+ $announcement = array('SID' => $fm['SID'],
+ 'project_name' => $this->notes['fm']['project'],
+ 'branch_name' => $this->notes['fm']['branch'],
+ 'version' => $this->_sourceVersionString,
+ 'changes' => htmlspecialchars($this->notes['fm']['changes']),
+ 'release_focus' => (int)$this->notes['fm']['focus'],
+ 'url_changelog' => $url_changelog,
+ 'url_tgz' => "ftp://ftp.horde.org/pub/$module/{$this->_tarballName}");
+ if ($this->_fmVerify($fm)) {
+ if (!empty($this->_options['noannounce']) ||
+ !empty($this->_options['nofreshmeat'])) {
+ print "Announcement data:\n";
+ print_r($announcement);
+ } else {
+ $fm = Horde_RPC::request(
+ 'xmlrpc',
+ 'http://freshmeat.net/xmlrpc/',
+ 'publish_release',
+ $announcement);
+ $this->_fmVerify($fm);
+ }
+ }
+ }
+
+ $ml = (!empty($this->notes['list'])) ? $this->notes['list'] : $module;
+ if (substr($ml, 0, 6) == 'horde-') {
+ $ml = 'horde';
+ }
+
+ $to = "announce@lists.horde.org, vendor@lists.horde.org, $ml@lists.horde.org";
+ if (!$this->_latest) {
+ $to .= ', i18n@lists.horde.org';
+ }
+
+ if (!empty($this->_options['noannounce'])) {
+ print "NOT announcing release on $to\n";
+ } else {
+ print "Announcing release to $to\n";
+ }
+
+ // Building headers
+ $subject = $this->notes['name'] . ' ' . $this->_sourceVersionString;
+ if ($this->_latest) {
+ $subject .= ' (final)';
+ }
+ if ($this->notes['fm']['focus'] == 9) {
+ $subject = '[SECURITY] ' . $subject;
+ }
+ $headers = array('From' => $this->_options['ml']['from'],
+ 'To' => $to,
+ 'Subject' => $subject);
+
+ // Building message text
+ $body = $this->notes['ml']['changes'];
+ if ($this->_oldVersion) {
+ $body .= "\n\n" .
+ sprintf('The full list of changes (from version %s) can be viewed here:', $this->_oldSourceVersionString) .
+ "\n\n" .
+ $url_changelog;
+ }
+ $body .= "\n\n" .
+ sprintf('The %s %s distribution is available from the following locations:', $this->notes['name'], $this->_sourceVersionString) .
+ "\n\n" .
+ sprintf(' ftp://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName) . "\n" .
+ sprintf(' http://ftp.horde.org/pub/%s/%s', $module, $this->_tarballName);
+ if ($this->_makeDiff) {
+ $body .= "\n\n" .
+ sprintf('Patches against version %s are available at:', $this->_oldSourceVersionString) .
+ "\n\n" .
+ sprintf(' ftp://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName) . "\n" .
+ sprintf(' http://ftp.horde.org/pub/%s/patches/%s.gz', $module, $this->_patchName);
+
+ if (!empty($this->_binaryDiffs)) {
+ $body .= "\n\n" .
+ 'NOTE: Patches do not contain differences between files containing binary data.' . "\n" .
+ 'These files will need to be updated via the distribution files:' . "\n\n " .
+ implode("\n ", $this->_binaryDiffs);
+ }
+ }
+ $body .= "\n\n" .
+ 'Or, for quicker access, download from your nearest mirror:' .
+ "\n\n" .
+ ' http://www.horde.org/mirrors.php' .
+ "\n\n" .
+ 'MD5 sums for the packages are as follows:' .
+ "\n\n" .
+ ' ' . $this->_tarballMD5[0] . "\n" .
+ ' ' . $this->_patchMD5[0] .
+ "\n\n" .
+ 'Have fun!' .
+ "\n\n" .
+ 'The Horde Team.';
+
+ if (!empty($this->_options['noannounce'])) {
+ print "Message headers:\n";
+ print_r($headers);
+ print "Message body:\n$body\n";
+ return;
+ }
+
+ // Building and sending message
+ $mail = new Horde_Mime_Mail();
+ $mail->setBody($body, 'iso-8859-1', false);
+ $mail->addHeaders($headers);
+ $result = $mail->send($this->_options['mailer']['type'], $this->_options['mailer']['params']);
+ if (is_a($result, 'PEAR_Error')) {
+ print $result->getMessage() . "\n";
+ }
+ }
+
+ /**
+ * Do testing (development only)
+ */
+ public function test()
+ {
+ if (!$this->_options['test']) {
+ return;
+ }
+
+ print "options['version']={$this->_options['version']}\n";
+ print "options['oldversion']={$this->_options['oldversion']}\n";
+ print "options['module']={$this->_options['module']}\n";
+ print "options['branch']={$this->_options['branch']}\n";
+
+ $this->setVersionStrings();
+
+ print "hordeVersionString={$this->_hordeVersionString}\n";
+ print "oldHordeVersionString={$this->_oldHordeVersionString}\n";
+ print "makeDiff={$this->_makeDiff}\n";
+ print "oldVersion={$this->_oldVersion}\n";
+ print "directoryName={$this->_directoryName}\n";
+ if ($this->_oldVersion) {
+ print "oldDirectoryName={$this->_oldDirectoryName}\n";
+ }
+ print "tagVersionString={$this->_tagVersionString}\n";
+ if ($this->_oldVersion) {
+ print "oldTagVersionString={$this->_oldTagVersionString}\n";
+ }
+ print "sourceVersionString={$this->_sourceVersionString}\n";
+ if ($this->_oldVersion) {
+ print "oldSourceVersionString={$this->_oldSourceVersionString}\n";
+ }
+ print "newSourceVersionString={$this->_newSourceVersionString}\n";
+ print "newSourceVersionStringPlain={$this->_newSourceVersionStringPlain}\n";
+ print "ticketVersion={$this->_ticketVersion}\n";
+ print "ticketVersionDesc=MODULE{$this->_ticketVersionDesc}\n";
+ if ($this->_latest) {
+ print "This is a production release\n";
+ }
+ exit(0);
+ }
+
+ /**
+ * Add the new version to bugs.horde.org
+ */
+ public function addWhupsVersion()
+ {
+ if (!isset($this->notes)) {
+ print "\nNOT updating bugs.horde.org, RELEASE_NOTES missing.\n";
+ return;
+ }
+ $this->_ticketVersionDesc = $this->notes['name'] . $this->_ticketVersionDesc;
+
+ $params = array('url' => 'https://dev.horde.org/horde/rpc.php',
+ 'user' => $this->_options['horde']['user'],
+ 'pass' => $this->_options['horde']['pass']);
+ $whups = new Horde_Release_Whups($params);
+
+ if (!$this->_options['nowhups']) {
+ print "Adding new versions to bugs.horde.org: ";
+ /* Set the new version in the queue */
+ try {
+ $whups->addNewVersion($this->_options['module'], $this->_ticketVersion, $this->_ticketVersionDesc);
+ print "OK\n";
+ } catch (Horde_Exception $e) {
+ print "Failed:\n";
+ print $e->getMessage() . "\n";
+ }
+ } else {
+ print "NOT updating bugs.horde.org:\n";
+ print "New ticket version WOULD have been {$this->_ticketVersion}\n";
+ print "New ticket version description WOULD have been {$this->_ticketVersionDesc}\n";
+
+ /* Perform some sanity checks on bugs.horde.org */
+ try {
+ $queue = $whups->getQueueId($this->_options['module']);
+
+ if ($queue === false) {
+ print "Was UNABLE to locate the queue id for {$this->_options['module']}\n";
+ } else {
+ print "The queue id on bugs.horde.org is $queue \n";
+ }
+ } catch (Horde_Exception $e) {
+ print "Will be UNABLE to update bugs.horde.org:\n";
+ print $e->getMessage() . "\n";
+ }
+ }
+ }
+
+ /**
+ * Set the version strings to use given the arguments
+ */
+ public function setVersionStrings()
+ {
+ $ver = explode('.', $this->_options['version']);
+ if (preg_match('/(\d+)\-(.*)/', $ver[count($ver) - 1], $matches)) {
+ $ver[count($ver) - 1] = $matches[1];
+ $plus = $matches[2];
+ }
+ if (preg_match('/(H\d)-(\d+)/', $ver[0], $matches)) {
+ $ver[0] = $matches[2];
+ $this->_hordeVersionString = $matches[1];
+ }
+ if (count($ver) > 2 && $ver[count($ver) - 1] == '0') {
+ die("version {$this->_options['version']} should not have the trailing 3rd-level .0\n");
+ }
+
+ // check if --oldversion is empty or 0
+ if (!empty($this->_options['oldversion'])) {
+ $this->_oldVersion = true;
+ }
+ $oldver = explode('.', $this->_options['oldversion']);
+ if (preg_match('/(\d+)\-(.*)/', $oldver[count($oldver) - 1], $matches)) {
+ $oldver[count($oldver) - 1] = $matches[1];
+ $oldplus = $matches[2];
+ }
+ if (preg_match('/(H\d)-(\d+)/', $oldver[0], $matches)) {
+ $oldver[0] = $matches[2];
+ $this->_oldHordeVersionString = $matches[1];
+ }
+
+ // set the string to use as the tag name in CVS
+ $this->_tagVersionString = strtoupper($this->_options['module'] . '_' . preg_replace('/\W/', '_', implode('_', $ver)));
+ if (isset($plus)) {
+ $this->_tagVersionString .= '_' . $plus;
+ }
+
+ // create patches only if not a major version change
+ if ($this->_options['oldversion'] && $ver[0] == $oldver[0]) {
+ $this->_makeDiff = true;
+ }
+
+ // is this really a production release?
+ if (isset($plus) && !preg_match('/^pl\d/', $plus)) {
+ $this->_latest = false;
+ }
+
+ // set the string to insert into the source version file
+ $this->_sourceVersionString = implode('.', $ver);
+ if (isset($plus)) {
+ $this->_sourceVersionString .= '-' . $plus;
+ }
+
+ // set the string to be used for the directory to package from
+ $this->_directoryName = $this->_options['module'] . '-';
+ if (!empty($this->_hordeVersionString)) {
+ $this->_directoryName .= $this->_hordeVersionString . '-';
+ }
+ $this->_directoryName = strtolower($this->_directoryName . $this->_sourceVersionString);
+
+ if (!empty($this->_hordeVersionString)) {
+ $this->_sourceVersionString = $this->_hordeVersionString . ' (' . $this->_sourceVersionString . ')';
+ }
+
+ if ($this->_oldVersion) {
+ $this->_oldSourceVersionString = implode('.', $oldver);
+ if (isset($oldplus)) {
+ $this->_oldSourceVersionString .= '-' . $oldplus;
+ }
+ $this->_oldTagVersionString = strtoupper($this->_options['module'] . '_' . implode('_', $oldver));
+ if (isset($oldplus)) {
+ $this->_oldTagVersionString .= '_' . $oldplus;
+ }
+ $this->_oldDirectoryName = strtolower($this->_options['module'] . '-' . $this->_oldHordeVersionString . $this->_oldSourceVersionString);
+ $this->_oldDirectoryName = $this->_options['module'] . '-';
+ if (!empty($this->_oldHordeVersionString)) {
+ $this->_oldDirectoryName .= $this->_oldHordeVersionString . '-';
+ }
+ $this->_oldDirectoryName = strtolower($this->_oldDirectoryName . $this->_oldSourceVersionString);
+
+ if (!empty($this->_oldHordeVersionString)) {
+ $this->_oldSourceVersionString = $this->_oldHordeVersionString . ' (' . $this->_oldSourceVersionString . ')';
+ }
+ }
+
+ // Set string to use for updating ticketing system.
+ $this->_ticketVersion = implode('.', $ver);
+ if (!empty($plus)) {
+ $this->_ticketVersion .= '-' . $plus;
+ }
+
+ if (!empty($this->_hordeVersionString)) {
+ $this->_ticketVersionDesc .= ' ' . $this->_hordeVersionString;
+ }
+
+ // Account for the 'special' case of the horde module.
+ if ($this->_options['module'] == 'horde') {
+ $this->_ticketVersionDesc .= ' ' . implode('.', $ver);
+ } else {
+ $this->_ticketVersionDesc .= ' ' . '(' . implode('.', $ver) . ')';
+ }
+
+ // See if we have a 'Final', 'Alpha', or 'RC' to add.
+ if ($this->_latest) {
+ $this->_ticketVersionDesc .= ' Final';
+ } elseif (!empty($plus) &&
+ preg_match('/^RC(\d+)/', $plus, $matches)) {
+ $this->_ticketVersionDesc .= ' Release Candidate ' . $matches[1];
+
+ } elseif (!empty($plus) && strtolower($plus) == 'alpha') {
+ $this->_ticketVersionDesc .= ' Alpha';
+ }
+
+ // set the name of the string to put into the source version file when
+ // done
+ if (!isset($plus)) {
+ while (count($ver) < 3) {
+ $ver[] = '0';
+ }
+ $ver[count($ver) - 1] += 1;
+ }
+ $this->_newSourceVersionString = implode('.', $ver) . '-cvs';
+ $this->_newSourceVersionStringPlain = $this->_newSourceVersionString;
+
+ if (!empty($this->_hordeVersionString)) {
+ $this->_newSourceVersionString = $this->_hordeVersionString .
+ ' (' . $this->_newSourceVersionString . ')';
+ }
+
+ }
+
+ /**
+ * Get all of the command-line arguments from the user
+ */
+ public function getArguments()
+ {
+ global $argv;
+
+ // Parse the command-line arguments
+ array_shift($argv);
+ foreach ($argv as $arg) {
+ // Check to see if they gave us a module
+ if (preg_match('/--module=(.*)/', $arg, $matches)) {
+ $this->_options['module'] = $matches[1];
+
+ // Check to see if they tell us the version of the tarball to make
+ } elseif (preg_match('/--version=(.*)/', $arg, $matches)) {
+ $this->_options['version']= $matches[1];
+
+ // Check to see if they tell us the last release version
+ } elseif (preg_match('/--oldversion=(.*)/', $arg, $matches)) {
+ $this->_options['oldversion']= $matches[1];
+
+ // Check to see if they tell us which branch to work with
+ } elseif (preg_match('/--branch=(.*)/', $arg, $matches)) {
+ $this->_options['branch']= $matches[1];
+
+ // Check to see if they tell us not to commit or tag
+ } elseif (strstr($arg, '--nocommit')) {
+ $this->_options['nocommit']= true;
+
+ // Check to see if they tell us not to upload
+ } elseif (strstr($arg, '--noftp')) {
+ $this->_options['noftp']= true;
+
+ // Check to see if they tell us not to announce
+ } elseif (strstr($arg, '--noannounce')) {
+ $this->_options['noannounce']= true;
+
+ // Check to see if they tell us not to announce
+ } elseif (strstr($arg, '--nofreshmeat')) {
+ $this->_options['nofreshmeat']= true;
+
+ // Check to see if they tell us not to add new ticket versions
+ } elseif (strstr($arg, '--noticketversion')) {
+ $this->_options['nowhups'] = true;
+
+ // Check to see if they tell us to do a dry run
+ } elseif (strstr($arg, '--dryrun')) {
+ $this->_options['nocommit'] = true;
+ $this->_options['noftp'] = true;
+ $this->_options['noannounce'] = true;
+ $this->_options['nowhups'] = true;
+ $this->_options['nofreshmeat']= true;
+
+ // Check to see if they tell us to test (for development only)
+ } elseif (strstr($arg, '--test')) {
+ $this->_options['test']= true;
+ // safety first
+ $this->_options['nocommit'] = true;
+ $this->_options['noftp'] = true;
+ $this->_options['noannounce'] = true;
+ $this->_options['nowhups'] = true;
+ $this->_options['nofreshmeat']= true;
+
+ // Check for help usage.
+ } elseif (strstr($arg, '--help')) {
+ $this->print_usage();
+ exit;
+
+ // We have no idea what this is
+ } else {
+ $this->print_usage('You have used unknown arguments: ' . $arg);
+ exit;
+ }
+ }
+ }
+
+ /**
+ * Check the command-line arguments and set some internal defaults
+ */
+ public function checkArguments()
+ {
+ // Make sure that we have a module defined
+ if (!isset($this->_options['module'])) {
+ $this->print_usage('You must define which module to package.');
+ exit;
+ }
+
+ // Let's make sure that there are valid version strings in here...
+ if (!isset($this->_options['version'])) {
+ $this->print_usage('You must define which version to package.');
+ exit;
+ }
+ if (!preg_match('/\d+\.\d+.*/', $this->_options['version'])) {
+ $this->print_usage('Incorrect version string.');
+ exit;
+ }
+ if (!isset($this->_options['oldversion'])) {
+ $this->print_usage('You must define last release\'s version.');
+ exit;
+ }
+ if (!preg_match('/\d+(\.\d+.*)?/', $this->_options['oldversion'])) {
+ $this->print_usage('Incorrect old version string.');
+ exit;
+ }
+
+ // Make sure we have a horde.org user
+ if (empty($this->_options['horde']['user'])) {
+ $this->print_usage('You must define a horde.org user.');
+ exit;
+ }
+
+ // If there is no branch defined, we're using the tip revisions.
+ // These releases are always developmental, and should use the HEAD "branch" name.
+ if (!isset($this->_options['branch'])) {
+ $this->_options['branch'] = 'HEAD';
+ }
+ }
+
+ /**
+ * Check the command-line arguments and set some internal defaults
+ */
+ public function checkSetSystem()
+ {
+ // Set umask
+ umask(022);
+ }
+
+ /**
+ * Show people how to use the damned thing
+ */
+ public function print_usage($message = null)
+ {
+ if (!is_null($message)) {
+ print "\n*** ERROR: $message ***\n";
+ }
+
+ print <<<USAGE
+
+make-release.php: Horde release generator.
+
+ This script takes as arguments the module to make a release of, the
+ version of the release, and the branch:
+
+ horde-make-release.php --module=<name>
+ --version=[Hn-]xx.yy[.zz[-<string>]]
+ --oldversion=[Hn-]xx[.yy[.zz[-<string>]]]
+ [--branch=<branchname>] [--nocommit] [--noftp]
+ [--noannounce] [--nofreshmeat] [--noticketversion]
+ [--test] [--dryrun] [--help]
+
+ If you omit the branch, it will implicitly work with the HEAD branch.
+ If you release a new major version use the --oldversion=0 option.
+ Use the --nocommit option to do a test build (without touching the CVS
+ repository).
+ Use the --noftp option to not upload any files on the FTP server.
+ Use the --noannounce option to not send any release announcements.
+ Use the --nofreshmeat option to not send any freshmeat announcements.
+ Use the --noticketversion option to not update the version information on
+ bugs.horde.org.
+ The --dryrun option is an alias for:
+ --nocommit --noftp --noannounce --nofreshmeat --noticketversion.
+ The --test option is for debugging purposes only.
+
+ EXAMPLES:
+
+ To make a new development release of Horde:
+ horde-make-release.php --module=horde --version=2.1-dev --oldversion=2.0
+
+ To make a new stable release of Turba:
+ horde-make-release.php --module=turba --version=H3-2.0.2 \
+ --oldversion=H3-2.0.1 --branch=FRAMEWORK_3
+
+ To make a new stable release of IMP 3:
+ horde-make-release.php --module=imp --version=3.0 --oldversion=2.3.7 \
+ --branch=RELENG_3
+
+ To make a brand new Alpha/Beta/RC release of Luxor:
+ horde-make-release.php --module=luxor --version=H3-1.0-ALPHA \
+ --oldversion=0
+
+USAGE;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * Class for interfacing with the tickets API.
+ *
+ * Copyright 2007-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Michael J. Rubinsky <mrubinsk@horde.org>
+ * @package Core
+ */
+class Horde_Release_Whups
+{
+ /**
+ * Instance of XML_RPC_Client object
+ *
+ * @var XML_RPC_CLient
+ */
+ protected $_client;
+
+ /**
+ * Local copy of config params.
+ *
+ * @var array
+ */
+ protected $_params;
+
+ /**
+ * Constructor.
+ *
+ * @param array $params TODO
+ */
+ public function __construct($params)
+ {
+ $this->_params = $params;
+ }
+
+ /**
+ * Add a new version to the current modules queue.
+ *
+ * @param string $module The name of the module.
+ * @param string $version The version string.
+ * @param string $desc Descriptive text for this version.
+ *
+ * @throws Horde_Exception
+ */
+ public function addNewVersion($module, $version, $desc = '')
+ {
+ if ($module == 'horde') {
+ $module = 'horde base';
+ }
+
+ $id = $this->getQueueId($module);
+ if ($id === false) {
+ throw new Horde_Exception('Unable to locate requested queue');
+ }
+
+ $method = 'tickets.addVersion';
+ $params = array($id, $version, $desc);
+ $options = array('user' => $this->_params['user'],
+ 'pass' => $this->_params['pass']);
+
+ $res = Horde_RPC::request('jsonrpc', $this->_params['url'], $method, $params, $options);
+ if ($res instanceof PEAR_Error) {
+ throw new Horde_Exception($res);
+ }
+ }
+
+ /**
+ * Look up the queue id for the requested module name.
+ *
+ * @param string $module TODO
+ *
+ * @return boolean TODO
+ */
+ function getQueueId($module)
+ {
+ $queues = $this->_listQueues();
+
+ foreach ($queues as $id => $queue) {
+ if (strtolower($queue) == $module) {
+ return $id;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Perform a listQueue api call.
+ *
+ * @return string TODO
+ * @throws Horde_Exception
+ */
+ protected function _listQueues()
+ {
+ $method = 'tickets.listQueues';
+ $result = Horde_RPC::request('jsonrpc', $this->_params['url'], $method,
+ null, array('user' => $this->_params['user'],
+ 'pass' => $this->_params['pass']));
+ if ($result instanceof PEAR_Error) {
+ throw new Horde_Exception($result);
+ }
+
+ return $result->result;
+ }
+
+}
--- /dev/null
+<?php
+/**
+ * The Horde_Script_Files:: class provides a coherent way to manage script
+ * files for inclusion in Horde output. This class is meant to be used
+ * internally by Horde:: only.
+ *
+ * Copyright 1999-2009 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @author Michael Slusarz <slusarz@horde.org>
+ * @package Core
+ */
+class Horde_Script_Files
+{
+ /**
+ * The singleton instance.
+ *
+ * @var Horde_Script_Files
+ */
+ static protected $_instance;
+
+ /**
+ * The list of script files to add.
+ *
+ * @var array
+ */
+ protected $_files = array();
+
+ /**
+ * The list of files we have already included.
+ *
+ * @var array
+ */
+ protected $_included = array();
+
+ /**
+ * The list of deprecated files.
+ *
+ * @var array
+ */
+ protected $_ignored = array(
+ 'horde' => array('tooltip.js')
+ );
+
+ /**
+ * The list of javascript files to always load from Horde.
+ *
+ * @var array
+ */
+ protected $_fromhorde = array('prototype.js');
+
+ /**
+ * The list of javscript files in Horde that have prototypejs'd versions.
+ *
+ * @var array
+ */
+ protected $_ptversions = array('tables.js', 'stripe.js');
+
+ /**
+ * Auto load horde.js?
+ *
+ * @var boolean
+ */
+ protected $_loadhordejs = true;
+
+ /**
+ * Singleton.
+ */
+ static public function singleton()
+ {
+ if (!self::$_instance) {
+ self::$_instance = new Horde_Script_Files();
+ }
+
+ return self::$_instance;
+ }
+
+ /**
+ * Adds the javascript code to the output (if output has already started)
+ * or to the list of script files to include.
+ *
+ * @param string $file The full javascript file name.
+ * @param string $app The application name. Defaults to the current
+ * application.
+ * @param boolean $direct Include the file directly without passing it
+ * through javascript.php?
+ * @param boolean $full Output a full url
+ */
+ public function add($file, $app = null, $direct = false, $full = false)
+ {
+ $res = $this->_add($file, $app, $direct, $full);
+
+ if (empty($res) || (!ob_get_length() && !headers_sent())) {
+ return;
+ }
+
+ // If headers have already been sent, we need to output a <script>
+ // tag directly.
+ echo '<script type="text/javascript" src="' . $res['u'] . '"></script>' . "\n";
+ }
+
+ /**
+ * Helper function to determine if given file needs to be output.
+ */
+ public function _add($file, $app, $direct, $full = false)
+ {
+ global $registry;
+
+ if (empty($app)) {
+ $app = $registry->getApp();
+ }
+
+ // Skip any js files that have since been deprecated.
+ if (!empty($this->_ignored[$app]) &&
+ in_array($file, $this->_ignored[$app])) {
+ return false;
+ }
+
+ // Several files will always be the same thing. Don't distinguish
+ // between loading them in different $app scopes; always load them
+ // from Horde scope.
+ if (in_array($file, $this->_fromhorde)) {
+ $app = 'horde';
+ }
+
+ // Don't include scripts multiple times.
+ if (!empty($this->_included[$app][$file])) {
+ return false;
+ }
+ $this->_included[$app][$file] = true;
+
+ // Explicitly check for a directly serve-able version of the script.
+ $path = $GLOBALS['registry']->get('fileroot', $app);
+ if (!$direct &&
+ file_exists($file[0] == '/'
+ ? $path . $file
+ : $registry->get('jsfs', $app) . '/' . $file)) {
+ $direct = true;
+ }
+
+ if ($direct) {
+ if ($file[0] == '/') {
+ $url = Horde::url($registry->get('webroot', $app) . $file,
+ $full, -1);
+ } else {
+ $url = Horde::url($registry->get('jsuri', $app) . '/' . $file,
+ $full, -1);
+ $path = $registry->get('jsfs', $app) . '/';
+ }
+
+ } else {
+ $path = $registry->get('templates', $app) . '/javascript/';
+ $url = Horde::url(
+ Horde_Util::addParameter(
+ $registry->get('webroot', 'horde') . '/services/javascript.php',
+ array('file' => $file, 'app' => $app)));
+ }
+
+ $out = $this->_files[$app][] = array('f' => $file, 'd' => $direct, 'u' => $url, 'p' => $path);
+ return $out;
+ }
+
+ /**
+ * Includes javascript files that are needed before any headers are sent.
+ */
+ public function includeFiles()
+ {
+ foreach ($this->listFiles() as $app => $files) {
+ foreach ($files as $file) {
+ echo '<script type="text/javascript" src="' . $file['u'] . '"></script>' . "\n";
+ }
+ }
+ }
+
+ /**
+ * Prepares the list of javascript files to include.
+ *
+ * @return array
+ */
+ public function listFiles()
+ {
+ /* If there is no javascript available, there's no point in including
+ * the rest of the files. */
+ if (!$GLOBALS['browser']->hasFeature('javascript')) {
+ return array();
+ }
+
+ $prototype = false;
+
+ // Always include Horde-level scripts first.
+ if (!empty($this->_files['horde'])) {
+ foreach ($this->_files['horde'] as $file) {
+ if ($file['f'] == 'prototype.js') {
+ $prototype = true;
+ break;
+ }
+ }
+
+ if (!$prototype) {
+ $keys = array_keys($this->_files['horde']);
+ foreach ($keys as $key) {
+ $file = $this->_files['horde'][$key];
+ if (in_array($file['f'], $this->_ptversions)) {
+ $this->_add('prototype.js', 'horde', true);
+ $prototype = true;
+ break;
+ }
+ }
+ }
+
+ // prototype.js must be included before any script that uses it
+ if ($prototype) {
+ $keys = array_keys($this->_files['horde']);
+ foreach ($keys as $key) {
+ $file = $this->_files['horde'][$key];
+ if ($file['f'] == 'prototype.js') {
+ unset($this->_files['horde'][$key]);
+ array_unshift($this->_files['horde'], $file);
+ }
+ }
+ reset($this->_files);
+ }
+ }
+
+ /* Add general UI js library. */
+ if ($this->_loadhordejs) {
+ $this->_add('prototype.js', 'horde', true);
+ $this->_add('horde.js', 'horde', true);
+ }
+
+ /* Add accesskeys.js if access keys are enabled. */
+ if ($GLOBALS['prefs']->getValue('widget_accesskey')) {
+ $this->_add('prototype.js', 'horde', true);
+ $this->_add('accesskeys.js', 'horde', true);
+ }
+
+ /* Make sure 'horde' entries appear first. */
+ reset($this->_files);
+ if (key($this->_files) == 'horde') {
+ return $this->_files;
+ }
+
+ if (isset($this->_files['horde'])) {
+ $jslist = array('horde' => $this->_files['horde']);
+ } else {
+ $jslist = array();
+ }
+ foreach ($this->_files as $key => $val) {
+ if ($key != 'horde') {
+ $jslist[$key] = $val;
+ }
+ }
+
+ return $jslist;
+ }
+
+ /**
+ * Disable auto-loading of the horde.js script.
+ * Needs to auto-load by default for BC.
+ *
+ * @todo Remove for Horde 4
+ */
+ public function disableAutoloadHordeJS()
+ {
+ $this->_loadhordejs = false;
+ }
+
+}
<dir name="/">
<dir name="lib">
<dir name="Horde">
- <dir name="Horde">
- <file name="Config.php" role="php" />
- <file name="ErrorHandler.php" role="php" />
- <file name="Exception.php" role="php" />
- <file name="Help.php" role="php" />
- <file name="Menu.php" role="php" />
- <file name="Registry.php" role="php" />
- <dir name="Registry">
- <file name="Caller.php" role="php" />
- </dir> <!-- /lib/Horde/Horde/Registry -->
- <dir name="Script">
- <file name="Files.php" role="php" />
- </dir> <!-- /lib/Horde/Horde/Script -->
- </dir> <!-- /lib/Horde/Horde -->
- <file name="Horde.php" role="php" />
+ <file name="Config.php" role="php" />
+ <file name="ErrorHandler.php" role="php" />
+ <file name="Exception.php" role="php" />
+ <file name="Help.php" role="php" />
+ <file name="Menu.php" role="php" />
+ <file name="Registry.php" role="php" />
+ <dir name="Registry">
+ <file name="Caller.php" role="php" />
+ </dir> <!-- /lib/Horde/Registry -->
+ <dir name="Script">
+ <file name="Files.php" role="php" />
+ </dir> <!-- /lib/Horde/Script -->
</dir> <!-- /lib/Horde -->
+ <file name="Horde.php" role="php" />
</dir> <!-- /lib -->
<dir name="test">
<dir name="Horde">
</dependencies>
<phprelease>
<filelist>
- <install name="lib/Horde/Horde/Config.php" as="Horde/Config.php" />
- <install name="lib/Horde/Horde/ErrorHandler.php" as="Horde/ErrorHandler.php" />
- <install name="lib/Horde/Horde/Exception.php" as="Horde/Exception.php" />
- <install name="lib/Horde/Horde/Help.php" as="Horde/Help.php" />
- <install name="lib/Horde/Horde/Menu.php" as="Horde/Menu.php" />
- <install name="lib/Horde/Horde/Registry.php" as="Horde/Registry.php" />
- <install name="lib/Horde/Horde/Registry/Caller.php" as="Horde/Registry/Caller.php" />
- <install name="lib/Horde/Horde/Script/Files.php" as="Horde/Script/Files.php" />
- <install name="lib/Horde/Horde.php" as="Horde.php" />
+ <install name="lib/Horde/Config.php" as="Horde/Config.php" />
+ <install name="lib/Horde/ErrorHandler.php" as="Horde/ErrorHandler.php" />
+ <install name="lib/Horde/Exception.php" as="Horde/Exception.php" />
+ <install name="lib/Horde/Help.php" as="Horde/Help.php" />
+ <install name="lib/Horde/Menu.php" as="Horde/Menu.php" />
+ <install name="lib/Horde/Registry.php" as="Horde/Registry.php" />
+ <install name="lib/Horde/Registry/Caller.php" as="Horde/Registry/Caller.php" />
+ <install name="lib/Horde/Script/Files.php" as="Horde/Script/Files.php" />
+ <install name="lib/Horde.php" as="Horde.php" />
</filelist>
</phprelease>
<changelog>
--- /dev/null
+--TEST--
+Horde::url() tests
+--FILE--
+<?php
+
+require_once dirname(__FILE__) . '/../../../lib/Horde/Horde.php';
+
+class Registry {
+
+ function get()
+ {
+ return '/hordeurl';
+ }
+
+}
+
+$registry = new Horde_Registry();
+$conf['server']['name'] = 'example.com';
+
+$uris = array(
+ 'test.php',
+ 'test.php?foo=1',
+ 'test.php?foo=1&bar=2',
+ 'test.php?foo=1&bar=2',
+ 'test.php?foo=1&bar=2&baz=3'
+);
+
+$fulls = array(false, true);
+$ssls = array(0, 1, 3);
+$ports = array(80, 443);
+
+foreach ($uris as $uri) {
+ foreach ($fulls as $full) {
+ foreach ($ssls as $ssl) {
+ $conf['use_ssl'] = $ssl;
+ foreach ($ports as $port) {
+ $conf['server']['port'] = $port;
+ echo Horde::url($uri, $full, -1) . "\n";
+ unset($_COOKIE[session_name()]);
+ echo Horde::url($uri, $full, 0) . "\n";
+ $_COOKIE[session_name()] = array();
+ echo Horde::url($uri, $full, 0) . "\n";
+ echo Horde::url($uri, $full, 1) . "\n";
+ }
+ }
+ }
+}
+
+?>
+--EXPECT--
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+test.php
+test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com:443/hordeurl/test.php
+http://example.com:443/hordeurl/test.php?PHPSESSID=
+http://example.com:443/hordeurl/test.php
+http://example.com:443/hordeurl/test.php?PHPSESSID=
+https://example.com:80/hordeurl/test.php
+https://example.com:80/hordeurl/test.php?PHPSESSID=
+https://example.com:80/hordeurl/test.php
+https://example.com:80/hordeurl/test.php?PHPSESSID=
+https://example.com/hordeurl/test.php
+https://example.com/hordeurl/test.php?PHPSESSID=
+https://example.com/hordeurl/test.php
+https://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+http://example.com/hordeurl/test.php
+http://example.com/hordeurl/test.php?PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+test.php?foo=1
+test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1
+http://example.com:443/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1
+http://example.com:443/hordeurl/test.php?foo=1&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1
+https://example.com:80/hordeurl/test.php?foo=1&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1
+https://example.com:80/hordeurl/test.php?foo=1&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1
+https://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1
+https://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1
+http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2
+https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2
+https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2
+test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2
+https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2
+https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2
+http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+test.php?foo=1&bar=2&baz=3
+test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3
+https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
+http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
+++ /dev/null
---TEST--
-Horde::url() tests
---FILE--
-<?php
-
-require_once dirname(__FILE__) . '/../../../lib/Horde/Horde.php';
-
-class Registry {
-
- function get()
- {
- return '/hordeurl';
- }
-
-}
-
-$registry = new Horde_Registry();
-$conf['server']['name'] = 'example.com';
-
-$uris = array(
- 'test.php',
- 'test.php?foo=1',
- 'test.php?foo=1&bar=2',
- 'test.php?foo=1&bar=2',
- 'test.php?foo=1&bar=2&baz=3'
-);
-
-$fulls = array(false, true);
-$ssls = array(0, 1, 3);
-$ports = array(80, 443);
-
-foreach ($uris as $uri) {
- foreach ($fulls as $full) {
- foreach ($ssls as $ssl) {
- $conf['use_ssl'] = $ssl;
- foreach ($ports as $port) {
- $conf['server']['port'] = $port;
- echo Horde::url($uri, $full, -1) . "\n";
- unset($_COOKIE[session_name()]);
- echo Horde::url($uri, $full, 0) . "\n";
- $_COOKIE[session_name()] = array();
- echo Horde::url($uri, $full, 0) . "\n";
- echo Horde::url($uri, $full, 1) . "\n";
- }
- }
- }
-}
-
-?>
---EXPECT--
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-test.php
-test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com:443/hordeurl/test.php
-http://example.com:443/hordeurl/test.php?PHPSESSID=
-http://example.com:443/hordeurl/test.php
-http://example.com:443/hordeurl/test.php?PHPSESSID=
-https://example.com:80/hordeurl/test.php
-https://example.com:80/hordeurl/test.php?PHPSESSID=
-https://example.com:80/hordeurl/test.php
-https://example.com:80/hordeurl/test.php?PHPSESSID=
-https://example.com/hordeurl/test.php
-https://example.com/hordeurl/test.php?PHPSESSID=
-https://example.com/hordeurl/test.php
-https://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-http://example.com/hordeurl/test.php
-http://example.com/hordeurl/test.php?PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-test.php?foo=1
-test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1
-http://example.com:443/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1
-http://example.com:443/hordeurl/test.php?foo=1&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1
-https://example.com:80/hordeurl/test.php?foo=1&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1
-https://example.com:80/hordeurl/test.php?foo=1&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1
-https://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1
-https://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1
-http://example.com/hordeurl/test.php?foo=1&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2
-https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2
-https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2
-test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2
-https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2
-https://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2
-http://example.com/hordeurl/test.php?foo=1&bar=2&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-test.php?foo=1&bar=2&baz=3
-test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com:443/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3
-https://example.com:80/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-https://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3
-http://example.com/hordeurl/test.php?foo=1&bar=2&baz=3&PHPSESSID=