From: Chuck Hagenbuch Date: Fri, 10 Jul 2009 00:16:02 +0000 (-0400) Subject: fix file paths; remove extra Horde/ directory X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=14440e42d8640071d6c6955cfd7d6a9eaf63d77f;p=horde.git fix file paths; remove extra Horde/ directory --- diff --git a/framework/Core/lib/Horde.php b/framework/Core/lib/Horde.php new file mode 100644 index 000000000..c0f17c7f8 --- /dev/null +++ b/framework/Core/lib/Horde.php @@ -0,0 +1,1949 @@ + + * @author Jon Parise + * @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: + *
+     * PEAR_LOG_EMERG
+     * PEAR_LOG_ALERT
+     * PEAR_LOG_CRIT
+     * PEAR_LOG_ERR
+     * PEAR_LOG_WARNING
+     * PEAR_LOG_NOTICE
+     * PEAR_LOG_INFO
+     * PEAR_LOG_DEBUG
+     * 
+ */ + 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 = '

' . _("A fatal error has occurred") . '

'; + 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']) . '
' . + 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']) . '
' . + 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 .= '

' . htmlspecialchars($message) . '

'; + } elseif (is_object($error) && method_exists($error, 'getMessage')) { + $errortext .= '

' . htmlspecialchars($error->getMessage()) . '

'; + } elseif (is_string($error)) { + $errortext .= '

' . htmlspecialchars($error) . '

'; + } + + if (is_null($file) && $error instanceof Exception) { + $file = $error->getFile(); + } + if (is_null($line) && $error instanceof Exception) { + $line = $error->getLine(); + } + + if ($admin) { + $errortext .= '

' . sprintf(_("[line %d of %s]"), $line, $file) . '

'; + if (is_object($error)) { + $errortext .= '

' . _("Details:") . '

'; + $errortext .= '

' . _("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.") . '

'; + if (extension_loaded('xdebug')) { + $errortext .= '
' . print_r($error, true); + } else { + $errortext .= '

' . htmlspecialchars(print_r($error, true)) . '

'; + } + } + } elseif ($log) { + $errortext .= '

' . _("Details have been logged for the administrator.") . '

'; + } + + // 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('
', '

', '

', '

', '

', '

', '

'), "\n", $errortext)); + } else { + echo <<< HTML + +Horde :: Fatal Error +$errortext + +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 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 + * tag. + * @param boolean $escape Whether to escape special characters in the + * title attribute. + * + * @return string The full tag. + */ + static public function link($url = '', $title = '', $class = '', + $target = '', $onclick = '', $title2 = '', + $accesskey = '', $attributes = array(), + $escape = true) + { + if (!empty($title2)) { + $title = $title2; + } + + $ret = ' $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 + * tag. + * + * @return string The full 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/', '/((?))/em', '/

/', '/
/'), 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
Title 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) . ''; + } + + /** + * 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 = ' $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 = ' $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 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 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 .= '' . "\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 . '(' + . strtoupper($accessKey) . '' . ')'; + } else { + return str_replace('_' . $accessKey, '' . $accessKey . '', $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('', + $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; + } + +} diff --git a/framework/Core/lib/Horde/Config.php b/framework/Core/lib/Horde/Config.php new file mode 100644 index 000000000..d35b819eb --- /dev/null +++ b/framework/Core/lib/Horde/Config.php @@ -0,0 +1,1548 @@ + + * @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 = "_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 tag. + * Subnodes will be parsed and added to both the Horde defaults and the + * Custom configuration parts. + * + * @param string $ctx The context of the tag. + * @param DomNode $node The DomNode representation of the + * 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 tag. + * Subnodes will be parsed and added to both the Horde defaults and the + * Custom configuration parts. + * + * @param string $ctx The context of the tag. + * @param DomNode $node The DomNode representation of the + * 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 tag. + * + * The keys contain the actual enum values while the values contain their + * corresponding descriptions. + * + * @param DomNode $node The DomNode representation of the + * 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 + * tag. + * + * @param DomNode &$node The DomNode representation of the + * 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 + * tag as used inside of enum configurations. + * + * @param DomNode $node The DomNode representation of the + * 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 + * @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); + } + } + } + +} diff --git a/framework/Core/lib/Horde/ErrorHandler.php b/framework/Core/lib/Horde/ErrorHandler.php new file mode 100644 index 000000000..c70669470 --- /dev/null +++ b/framework/Core/lib/Horde/ErrorHandler.php @@ -0,0 +1,202 @@ + '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\[function\.[\d\w-_]+\]%", '', $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 '

' . htmlspecialchars($error['file']) . ':' . htmlspecialchars($error['line']) . ': ' . htmlspecialchars($error['str']) . '

'; + } + } + } + +} diff --git a/framework/Core/lib/Horde/Exception.php b/framework/Core/lib/Horde/Exception.php new file mode 100644 index 000000000..375485eaf --- /dev/null +++ b/framework/Core/lib/Horde/Exception.php @@ -0,0 +1,66 @@ +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); + } + +} diff --git a/framework/Core/lib/Horde/Help.php b/framework/Core/lib/Horde/Help.php new file mode 100644 index 000000000..cc1a1ea25 --- /dev/null +++ b/framework/Core/lib/Horde/Help.php @@ -0,0 +1,530 @@ + + * @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 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 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>'; + } + } + +} diff --git a/framework/Core/lib/Horde/Horde.php b/framework/Core/lib/Horde/Horde.php deleted file mode 100644 index c0f17c7f8..000000000 --- a/framework/Core/lib/Horde/Horde.php +++ /dev/null @@ -1,1949 +0,0 @@ -<?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 -$errortext - -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 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 - * tag. - * @param boolean $escape Whether to escape special characters in the - * title attribute. - * - * @return string The full tag. - */ - static public function link($url = '', $title = '', $class = '', - $target = '', $onclick = '', $title2 = '', - $accesskey = '', $attributes = array(), - $escape = true) - { - if (!empty($title2)) { - $title = $title2; - } - - $ret = ' $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 - * tag. - * - * @return string The full 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/', '/((?))/em', '/

/', '/
/'), 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
Title 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) . ''; - } - - /** - * 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 = ' $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 = ' $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 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 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 .= '' . "\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 . '(' - . strtoupper($accessKey) . '' . ')'; - } else { - return str_replace('_' . $accessKey, '' . $accessKey . '', $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('', - $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; - } - -} diff --git a/framework/Core/lib/Horde/Horde/Config.php b/framework/Core/lib/Horde/Horde/Config.php deleted file mode 100644 index d35b819eb..000000000 --- a/framework/Core/lib/Horde/Horde/Config.php +++ /dev/null @@ -1,1548 +0,0 @@ - - * @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 = "_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 tag. - * Subnodes will be parsed and added to both the Horde defaults and the - * Custom configuration parts. - * - * @param string $ctx The context of the tag. - * @param DomNode $node The DomNode representation of the - * 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 tag. - * Subnodes will be parsed and added to both the Horde defaults and the - * Custom configuration parts. - * - * @param string $ctx The context of the tag. - * @param DomNode $node The DomNode representation of the - * 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 tag. - * - * The keys contain the actual enum values while the values contain their - * corresponding descriptions. - * - * @param DomNode $node The DomNode representation of the - * 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 - * tag. - * - * @param DomNode &$node The DomNode representation of the - * 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 - * tag as used inside of enum configurations. - * - * @param DomNode $node The DomNode representation of the - * 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 - * @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); - } - } - } - -} diff --git a/framework/Core/lib/Horde/Horde/ErrorHandler.php b/framework/Core/lib/Horde/Horde/ErrorHandler.php deleted file mode 100644 index c70669470..000000000 --- a/framework/Core/lib/Horde/Horde/ErrorHandler.php +++ /dev/null @@ -1,202 +0,0 @@ - '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\[function\.[\d\w-_]+\]%", '', $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 '

' . htmlspecialchars($error['file']) . ':' . htmlspecialchars($error['line']) . ': ' . htmlspecialchars($error['str']) . '

'; - } - } - } - -} diff --git a/framework/Core/lib/Horde/Horde/Exception.php b/framework/Core/lib/Horde/Horde/Exception.php deleted file mode 100644 index 375485eaf..000000000 --- a/framework/Core/lib/Horde/Horde/Exception.php +++ /dev/null @@ -1,66 +0,0 @@ -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); - } - -} diff --git a/framework/Core/lib/Horde/Horde/Help.php b/framework/Core/lib/Horde/Horde/Help.php deleted file mode 100644 index cc1a1ea25..000000000 --- a/framework/Core/lib/Horde/Horde/Help.php +++ /dev/null @@ -1,530 +0,0 @@ - - * @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 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 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>'; - } - } - -} diff --git a/framework/Core/lib/Horde/Horde/Menu.php b/framework/Core/lib/Horde/Horde/Menu.php deleted file mode 100644 index 5a66ce835..000000000 --- a/framework/Core/lib/Horde/Horde/Menu.php +++ /dev/null @@ -1,313 +0,0 @@ -<?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; - } - -} diff --git a/framework/Core/lib/Horde/Horde/Registry.php b/framework/Core/lib/Horde/Horde/Registry.php deleted file mode 100644 index c693f9b42..000000000 --- a/framework/Core/lib/Horde/Horde/Registry.php +++ /dev/null @@ -1,1128 +0,0 @@ -<?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; - } - } - -} diff --git a/framework/Core/lib/Horde/Horde/Registry/Caller.php b/framework/Core/lib/Horde/Horde/Registry/Caller.php deleted file mode 100644 index 1189a7710..000000000 --- a/framework/Core/lib/Horde/Horde/Registry/Caller.php +++ /dev/null @@ -1,41 +0,0 @@ -<?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); - } - -} diff --git a/framework/Core/lib/Horde/Horde/Release.php b/framework/Core/lib/Horde/Horde/Release.php deleted file mode 100644 index f93b2989d..000000000 --- a/framework/Core/lib/Horde/Horde/Release.php +++ /dev/null @@ -1,1067 +0,0 @@ -<?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; - } - -} diff --git a/framework/Core/lib/Horde/Horde/Release/Whups.php b/framework/Core/lib/Horde/Horde/Release/Whups.php deleted file mode 100644 index 27ec2fa91..000000000 --- a/framework/Core/lib/Horde/Horde/Release/Whups.php +++ /dev/null @@ -1,109 +0,0 @@ -<?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; - } - -} diff --git a/framework/Core/lib/Horde/Horde/Script/Files.php b/framework/Core/lib/Horde/Horde/Script/Files.php deleted file mode 100644 index c9c06cba8..000000000 --- a/framework/Core/lib/Horde/Horde/Script/Files.php +++ /dev/null @@ -1,270 +0,0 @@ -<?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; - } - -} diff --git a/framework/Core/lib/Horde/Menu.php b/framework/Core/lib/Horde/Menu.php new file mode 100644 index 000000000..5a66ce835 --- /dev/null +++ b/framework/Core/lib/Horde/Menu.php @@ -0,0 +1,313 @@ +<?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; + } + +} diff --git a/framework/Core/lib/Horde/Registry.php b/framework/Core/lib/Horde/Registry.php new file mode 100644 index 000000000..c693f9b42 --- /dev/null +++ b/framework/Core/lib/Horde/Registry.php @@ -0,0 +1,1128 @@ +<?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; + } + } + +} diff --git a/framework/Core/lib/Horde/Registry/Caller.php b/framework/Core/lib/Horde/Registry/Caller.php new file mode 100644 index 000000000..1189a7710 --- /dev/null +++ b/framework/Core/lib/Horde/Registry/Caller.php @@ -0,0 +1,41 @@ +<?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); + } + +} diff --git a/framework/Core/lib/Horde/Release.php b/framework/Core/lib/Horde/Release.php new file mode 100644 index 000000000..f93b2989d --- /dev/null +++ b/framework/Core/lib/Horde/Release.php @@ -0,0 +1,1067 @@ +<?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; + } + +} diff --git a/framework/Core/lib/Horde/Release/Whups.php b/framework/Core/lib/Horde/Release/Whups.php new file mode 100644 index 000000000..27ec2fa91 --- /dev/null +++ b/framework/Core/lib/Horde/Release/Whups.php @@ -0,0 +1,109 @@ +<?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; + } + +} diff --git a/framework/Core/lib/Horde/Script/Files.php b/framework/Core/lib/Horde/Script/Files.php new file mode 100644 index 000000000..c9c06cba8 --- /dev/null +++ b/framework/Core/lib/Horde/Script/Files.php @@ -0,0 +1,270 @@ +<?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; + } + +} diff --git a/framework/Core/package.xml b/framework/Core/package.xml index 050a4f3b2..7f82604e4 100644 --- a/framework/Core/package.xml +++ b/framework/Core/package.xml @@ -46,22 +46,20 @@ Application Framework. <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"> @@ -114,15 +112,15 @@ Application Framework. </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> diff --git a/framework/Core/test/Horde/Core/url.phpt b/framework/Core/test/Horde/Core/url.phpt new file mode 100644 index 000000000..6a9e0b676 --- /dev/null +++ b/framework/Core/test/Horde/Core/url.phpt @@ -0,0 +1,290 @@ +--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= diff --git a/framework/Core/test/Horde/Framework/url.phpt b/framework/Core/test/Horde/Framework/url.phpt deleted file mode 100644 index 6a9e0b676..000000000 --- a/framework/Core/test/Horde/Framework/url.phpt +++ /dev/null @@ -1,290 +0,0 @@ ---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=