Files
server/usr/share/psa-pear/pear/php/Horde.php
2026-01-07 20:52:11 +01:00

1350 lines
45 KiB
PHP

<?php
/**
* Copyright 1999-2017 Horde LLC (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.horde.org/licenses/lgpl21.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Jon Parise <jon@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Core
*/
/**
* Provides the base functionality shared by all Horde applications.
*
* @author Chuck Hagenbuch <chuck@horde.org>
* @author Jon Parise <jon@horde.org>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL
* @package Core
*/
class Horde
{
const SSL_NEVER = 0;
const SSL_ALWAYS = 1;
const SSL_AUTO = 2;
const SSL_ONLY_LOGIN = 3;
/**
* The current buffer level.
*
* @var integer
*/
protected static $_bufferLevel = 0;
/**
* Has content been sent at the base buffer level?
*
* @var boolean
*/
protected static $_contentSent = false;
/**
* The labels already used in this page.
*
* @var array
*/
protected static $_labels = array();
/**
* Are accesskeys supported on this system.
*
* @var boolean
*/
protected static $_noAccessKey;
/**
* The access keys already used in this page.
*
* @var array
*/
protected static $_used = array();
/**
* Shortcut to logging method.
*
* @see Horde_Core_Log_Logger
*/
public static function log($event, $priority = null,
array $options = array())
{
$options['trace'] = isset($options['trace'])
? ($options['trace'] + 1)
: 1;
$log_ob = new Horde_Core_Log_Object($event, $priority, $options);
/* Chicken/egg: we must wait until we have basic framework setup
* before we can start logging. Otherwise, queue entries. */
if (isset($GLOBALS['injector']) &&
Horde_Core_Factory_Logger::available()) {
$GLOBALS['injector']->getInstance('Horde_Log_Logger')->logObject($log_ob);
} else {
Horde_Core_Factory_Logger::queue($log_ob);
}
}
/**
* Shortcut to logging method.
*
* @deprecated Use log() instead
* @see log()
*/
public static function logMessage($event, $priority = null,
array $options = array())
{
$options['trace'] = isset($options['trace'])
? ($options['trace'] + 1)
: 1;
self::log($event, $priority, $options);
}
/**
* Debug method. Allows quick shortcut to produce debug output into a
* temporary file.
*
* @param mixed $event Item to log.
* @param string $fname Filename to log to. If empty, logs to
* 'horde_debug.txt' in the PHP temporary
* directory.
* @param boolean $backtrace Include backtrace information?
*/
public static function debug($event = null, $fname = null,
$backtrace = true)
{
if (is_null($fname)) {
$fname = self::getTempDir() . '/horde_debug.txt';
}
try {
$logger = new Horde_Log_Logger(new Horde_Log_Handler_Stream($fname));
} catch (Exception $e) {
return;
}
$html_ini = ini_set('html_errors', 'Off');
self::startBuffer();
if (!is_null($event)) {
echo "Variable information:\n";
var_dump($event);
echo "\n";
}
if (is_resource($event)) {
echo "Stream contents:\n";
rewind($event);
fpassthru($event);
echo "\n";
}
if ($backtrace) {
echo "Backtrace:\n";
echo strval(new Horde_Support_Backtrace());
}
$logger->log(self::endBuffer(), Horde_Log::DEBUG);
ini_set('html_errors', $html_ini);
}
/**
* Adds a signature + timestamp to a URL and returns the signed URL.
*
* @since Horde_Core 2.30.0
*
* @param string|Horde_Url $url The URL to sign.
* @param integer $now The timestamp at which to sign. Leave
* blank for generating signatures; specify
* when testing.
*
* @return string|Horde_Url The signed URL.
*/
public static function signUrl($url, $now = null)
{
global $conf;
if (!isset($conf['secret_key'])) {
return $url;
}
if (is_null($now)) {
$now = time();
}
if ($url instanceof Horde_Url) {
$url->setRaw(true)->add(array('_t' => $now, '_h' => ''));
$url->add(
'_h',
Horde_Url::uriB64Encode(
hash_hmac('sha1', $url . '=', $conf['secret_key'], true)
)
);
return $url;
}
if (!$url) {
return $url;
}
if (strpos($url, '?')) {
$url .= '&';
} else {
$url .= '?';
}
$url .= '_t=' . $now . '&_h=';
$url .= Horde_Url::uriB64Encode(
hash_hmac('sha1', $url, $conf['secret_key'], true)
);
return $url;
}
/**
* Verifies a signature and timestamp on a URL.
*
* @since Horde_Core 2.30.0
*
* @param string $data The signed URL.
* @param integer $now The current time (can override for testing).
*
* @return string|boolean The URL stripped off the signature, of false if
* not verified.
*/
public static function verifySignedUrl($data, $now = null)
{
global $conf;
if (is_null($now)) {
$now = time();
}
$pos = strrpos($data, '&_h=');
if ($pos === false) {
return false;
}
$pos += 4;
$url = substr($data, 0, $pos);
$hmac = substr($data, $pos);
if ($hmac != Horde_Url::uriB64Encode(hash_hmac('sha1', $url, $conf['secret_key'], true))) {
return false;
}
// String was not tampered with; now validate timestamp
parse_str(parse_url($url, PHP_URL_QUERY), $values);
if ($values['_t'] + $conf['urls']['hmac_lifetime'] * 60 < $now) {
return false;
}
$pos = strrpos($data, '&_t=');
if ($pos === false) {
$pos = strrpos($data, '?_t=');
}
if ($pos === false) {
return false;
}
return substr($data, 0, $pos);
}
/**
* Adds a signature + timestamp to a query string and returns the signed
* query string.
*
* @param mixed $queryString The query string (or Horde_Url object)
* to sign.
* @param integer $now The timestamp at which to sign. Leave blank
* for generating signatures; specify when
* testing.
*
* @return mixed The signed query string (or Horde_Url object).
*/
public static function signQueryString($queryString, $now = null)
{
if (!isset($GLOBALS['conf']['secret_key'])) {
return $queryString;
}
if (is_null($now)) {
$now = time();
}
if ($queryString instanceof Horde_Url) {
$queryString->setRaw(true)->add(array('_t' => $now, '_h' => ''));
$parse_url = parse_url($queryString);
$queryString->add('_h', Horde_Url::uriB64Encode(hash_hmac('sha1', $parse_url['query'] . '=', $GLOBALS['conf']['secret_key'], true)));
return $queryString;
}
$queryString .= '&_t=' . $now . '&_h=';
return $queryString . Horde_Url::uriB64Encode(hash_hmac('sha1', $queryString, $GLOBALS['conf']['secret_key'], true));
}
/**
* Verifies 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.
*/
public static 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_Url::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);
}
/**
* Do necessary escaping to output JSON.
*
* @param mixed $data The data to JSON-ify.
* @param array $options Additional options:
* - nodelimit: (boolean) Don't add security delimiters?
* DEFAULT: false
* - urlencode: (boolean) URL encode the json string
* DEFAULT: false
*
* @return string The escaped string.
*/
public static function escapeJson($data, array $options = array())
{
$json = Horde_Serialize::serialize($data, Horde_Serialize::JSON);
if (empty($options['nodelimit'])) {
$json = '/*-secure-' . $json . '*/';
}
return empty($options['urlencode'])
? $json
: '\'' . rawurlencode($json) . '\'';
}
/**
* Is the current HTTP connection considered secure?
* @TODO Move this to the request classes!
*
* @return boolean
*/
public static function isConnectionSecure()
{
global $browser, $conf, $registry;
if ($browser->usingSSLConnection()) {
return true;
}
if (!empty($conf['safe_ips'])) {
if (reset($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. */
$remote = $registry->remoteHost();
if (!$remote->proxy) {
foreach ($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
*/
public static function requireSecureConnection()
{
if (!self::isConnectionSecure()) {
throw new Horde_Exception(Horde_Core_Translation::t("The encryption features require a secure web connection."));
}
}
/**
* 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. If null, will not merge with
* base config.
*
* @return array The connection parameters.
*/
public static function getDriverConfig($backend, $type = 'sql')
{
global $conf;
if (!is_null($type)) {
$type = Horde_String::lower($type);
}
if (is_array($backend)) {
$c = Horde_Array::getElement($conf, $backend);
} elseif (isset($conf[$backend])) {
$c = $conf[$backend];
} else {
$c = null;
}
if (!is_null($c) && isset($c['params'])) {
$c['params']['umask'] = $conf['umask'];
$result = (!is_null($type) && isset($conf[$type]))
? array_merge($conf[$type], $c['params'])
: $c['params'];
// HOTFIX for Bug #14547. If using different protocols for the
// base SQL config and the explicit driver we are creating, we
// need to remove the not-used connection config since they use
// different keys.
if ((!isset($c['params']['driverconfig']) ||
$c['params']['driverconfig'] != 'horde') &&
!is_null($type) && $type == 'sql') {
if ($c['params']['protocol'] == 'unix') {
unset($result['hostspec'], $result['port']);
} else {
unset($result['socket']);
}
}
return $result;
}
return (!is_null($type) && isset($conf[$type]))
? $conf[$type]
: array();
}
/**
* 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.
*
* @throws Horde_Exception
*/
public static 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)) {
throw new Horde_Exception(
sprintf(Horde_Core_Translation::t("No configuration information specified for %s."), $name) . "\n\n" .
sprintf(Horde_Core_Translation::t("The file %s should contain some %s settings."),
$fileroot . '/config/' . $file,
sprintf("%s['%s']['params']", $variable, $driver)));
}
foreach ($fields as $field) {
if (!isset($params[$field])) {
throw new Horde_Exception(
sprintf(Horde_Core_Translation::t("Required \"%s\" not specified in %s configuration."), $field, $name) . "\n\n" .
sprintf(Horde_Core_Translation::t("The file %s should contain a %s setting."),
$fileroot . '/config/' . $file,
sprintf("%s['%s']['params']['%s']", $variable, $driver, $field)));
}
}
}
/**
* Returns a session-id-ified version of $uri.
* If a full URL is requested, all parameter separators get converted to
* "&", otherwise to "&amp;".
*
* @param mixed $uri The URI to be modified (either a string or any
* object with a __toString() function).
* @param boolean $full Generate a full (http://server/path/) URL.
* @param mixed $opts Additional options. If a string/integer, it is
* taken to be the 'append_session' option. If an
* array, one of the following:
* - app: (string) Use this app for the webroot.
* DEFAULT: current application
* - append_session: (integer) 0 = only if needed [DEFAULT], 1 = always,
* -1 = never.
* - force_ssl: (boolean) Ignore $conf['use_ssl'] and force creation of
* a SSL URL?
* DEFAULT: false
*
* @return Horde_Url The URL with the session id appended (if needed).
*/
public static function url($uri, $full = false, $opts = array())
{
if (is_array($opts)) {
$append_session = isset($opts['append_session'])
? $opts['append_session']
: 0;
if (!empty($opts['force_ssl'])) {
$full = true;
}
} else {
$append_session = $opts;
$opts = array();
}
$puri = parse_url($uri);
/* @todo Fix for PHP < 5.3.6 */
if (isset($puri['fragment']) && !isset($puri['path'])) {
$pos = strpos(
$uri,
'/',
strpos($uri, $puri['host']) + strlen($puri['host'])
);
$puri['path'] = substr($uri, $pos, strpos($uri, '#', $pos) - $pos);
}
/* End fix */
$url = '';
$schemeRegexp = '|^([a-zA-Z][a-zA-Z0-9+.-]{0,19})://|';
$webroot = ltrim(
$GLOBALS['registry']->get(
'webroot',
empty($opts['app']) ? null : $opts['app']
),
'/'
);
if ($full &&
!isset($puri['scheme']) &&
!preg_match($schemeRegexp, $webroot)) {
/* Store connection parameters in local variables. */
$server_name = $GLOBALS['conf']['server']['name'];
$server_port = isset($GLOBALS['conf']['server']['port'])
? $GLOBALS['conf']['server']['port']
: '';
$protocol = 'http';
switch ($GLOBALS['conf']['use_ssl']) {
case self::SSL_ALWAYS:
$protocol = 'https';
break;
case self::SSL_AUTO:
if ($GLOBALS['browser']->usingSSLConnection()) {
$protocol = 'https';
}
break;
case self::SSL_ONLY_LOGIN:
if (!empty($opts['force_ssl'])) {
$protocol = 'https';
$server_port = '';
}
break;
}
/* If using a non-standard port, add to the URL. */
if (!empty($server_port) &&
((($protocol == 'http') && ($server_port != 80)) ||
(($protocol == 'https') && ($server_port != 443)))) {
$server_name .= ':' . $server_port;
}
$url = $protocol . '://' . $server_name;
} elseif (isset($puri['scheme'])) {
$url = $puri['scheme'] . '://' . $puri['host'];
/* If using a non-standard port, add to the URL. */
if (isset($puri['port']) &&
((($puri['scheme'] == 'http') && ($puri['port'] != 80)) ||
(($puri['scheme'] == 'https') && ($puri['port'] != 443)))) {
$url .= ':' . $puri['port'];
}
}
if (isset($puri['path']) &&
(substr($puri['path'], 0, 1) == '/') &&
(!preg_match($schemeRegexp, $webroot) ||
(preg_match($schemeRegexp, $webroot) && isset($puri['scheme'])))) {
$url .= $puri['path'];
} elseif (isset($puri['path']) && preg_match($schemeRegexp, $webroot)) {
if (substr($puri['path'], 0, 1) == '/') {
$pwebroot = parse_url($webroot);
$url = $pwebroot['scheme'] . '://' . $pwebroot['host']
. $puri['path'];
} else {
$url = $webroot . '/' . $puri['path'];
}
} else {
$url .= '/' . ($webroot ? $webroot . '/' : '') . (isset($puri['path']) ? $puri['path'] : '');
}
if (isset($puri['query'])) {
$url .= '?' . $puri['query'];
}
if (isset($puri['fragment'])) {
$url .= '#' . $puri['fragment'];
}
$ob = new Horde_Url($url, $full);
if (empty($GLOBALS['conf']['session']['use_only_cookies']) &&
(($append_session == 1) ||
(($append_session == 0) && !isset($_COOKIE[session_name()])))) {
$ob->add(session_name(), session_id());
}
return $ob;
}
/**
* Returns an external link passed through the dereferrer to strip session
* IDs from the referrer.
*
* @param string $url The external URL to link to.
* @param boolean $tag If true, a complete <a> tag is returned, only the
* url otherwise.
*
* @return string The link to the dereferrer script.
*/
public static 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::signQueryString($GLOBALS['registry']->getServiceLink('go', 'horde')->add('url', $url));
}
if ($tag) {
$ext = self::link($ext, $url, '', '_blank');
}
return $ext;
}
/**
* Returns an anchor tag with the relevant parameters
*
* @param Horde_Url|string $url The full URL to be linked to.
* @param string $title The link title/description.
* @param string $class The CSS class of the link.
* @param string $target The window target to point to.
* @param string $onclick JavaScript action for the 'onclick' event.
* @param string $title2 The link title (tooltip) (deprecated - just
* use $title).
* @param string $accesskey The access key to use.
* @param array $attributes Any other name/value pairs to add to the
* <a> tag.
* @param boolean $escape Whether to escape special characters in the
* title attribute.
*
* @return string The full <a href> tag.
*/
public static function link($url = '', $title = '', $class = '',
$target = '', $onclick = '', $title2 = '',
$accesskey = '', $attributes = array(),
$escape = true)
{
if (!($url instanceof Horde_Url)) {
$url = new Horde_Url($url);
}
if (!empty($title2)) {
$title = $title2;
}
if (!empty($onclick)) {
$attributes['onclick'] = $onclick;
}
if (!empty($class)) {
$attributes['class'] = $class;
}
if (!empty($target)) {
$attributes['target'] = $target;
}
if (!empty($accesskey)) {
$attributes['accesskey'] = $accesskey;
}
if (!empty($title)) {
if ($escape) {
$title = str_replace(
array("\r", "\n"), '',
htmlspecialchars(nl2br(htmlspecialchars($title))));
/* Remove double encoded entities. */
$title = preg_replace('/&amp;([a-z]+|(#\d+));/i', '&\\1;', $title);
}
$attributes['title.raw'] = $title;
}
return $url->link($attributes);
}
/**
* 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). Most not contain
* HTML data other than &lt;br&gt;, which will
* be converted to a linebreak.
* @param string $accesskey The access key to use.
* @param array $attributes Any other name/value pairs to add to the
* &lt;a&gt; tag.
*
* @return string The full <a href> tag.
*/
public static function linkTooltip(
$url, $status = '', $class = '', $target = '', $onclick = '',
$title = '', $accesskey = '', $attributes = array()
)
{
if (strlen($title)) {
$attributes['nicetitle'] = Horde_Serialize::serialize(
preg_split(
'/\r?\n/',
preg_replace('/<br\s*\/?\s*>/', "\n", $title)
),
Horde_Serialize::JSON
);
$title = null;
$GLOBALS['injector']->getInstance('Horde_PageOutput')
->addScriptFile('tooltips.js', 'horde');
}
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 array $params A hash with widget options (other options will be
* passed as attributes to the link tag):
* - url: (string) The full URL to be linked to.
* - title: (string) The link title/description.
* - nocheck: (boolean, optional) Don't check if the accesskey already
* already has been used.
* Defaults to false (= check).
*
* @return string The full <a href>Title</a> sequence.
*/
public static function widget($params)
{
$params = array_merge(
array(
'class' => '',
'target' => '',
'onclick' => '',
'nocheck' => false),
$params
);
$url = ($params['url'] instanceof Horde_Url)
? $params['url']
: new Horde_Url($params['url']);
$title = $params['title'];
$params['accesskey'] = self::getAccessKey($title, $params['nocheck']);
unset($params['url'], $params['title'], $params['nocheck']);
return $url->link($params)
. self::highlightAccessKey($title, $params['accesskey'])
. '</a>';
}
/**
* Returns a session-id-ified version of $SCRIPT_NAME resp. $PHP_SELF.
*
* @param boolean $script_params Include script parameters like
* QUERY_STRING and PATH_INFO?
* (Deprecated: use Horde::selfUrlParams()
* instead.)
* @param boolean $nocache Include a cache-buster 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 Horde_Url The requested URL.
*/
public static 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 (isset($_SERVER['REQUEST_URI'])) {
$url = Horde_String::common($_SERVER['REQUEST_URI'], $url);
}
if (substr($url, -9) == 'index.php') {
$url = substr($url, 0, -9);
}
if ($script_params) {
$url = new Horde_Url($url);
if ($pathInfo = Horde_Util::getPathInfo()) {
$url->pathInfo = ltrim($pathInfo, '/');
}
if (!empty($_SERVER['QUERY_STRING'])) {
parse_str($_SERVER['QUERY_STRING'], $args);
$url->add($args);
}
}
$url = self::url($url, $full, array('force_ssl' => $force_ssl));
return ($nocache && $GLOBALS['browser']->hasQuirk('cache_same_url'))
? $url->unique()
: $url;
}
/**
* Create a self URL of the current page, building the parameter list from
* the current Horde_Variables object (or via another Variables object
* passed as an optional argument) rather than the original request data.
*
* @since 2.3.0
*
* @param array $opts Additional options:
* - force_ssl: (boolean) Force creation of an SSL URL?
* DEFAULT: false
* - full: (boolean) Return a full URL?
* DEFAULT: false
* - nocache: (boolean) Include a cache-buster parameter in the URL?
* DEFAULT: true
* - vars: (Horde_Variables) Use this Horde_Variables object instead of
* the Horde global object.
* DEFAULT: Use the Horde global object.
*
* @return Horde_Url The self URL.
*/
public static function selfUrlParams(array $opts = array())
{
$vars = isset($opts['vars'])
? $opts['vars']
: $GLOBALS['injector']->createInstance('Horde_Variables');
$url = self::selfUrl(
false,
(!array_key_exists('nocache', $opts) || empty($opts['nocache'])),
!empty($opts['full']),
!empty($opts['force_ssl'])
)->add(iterator_to_array($vars));
if (!isset($opts['vars'])) {
$url->remove(array_keys($_COOKIE));
}
return $url;
}
/**
* Determines the location of the system temporary directory. If a specific
* configuration 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.
*/
public static function getTempDir()
{
global $conf;
/* If one has been specifically set, then use that */
if (!empty($conf['tmpdir'])) {
$tmp = $conf['tmpdir'];
}
/* Next, try sys_get_temp_dir(). */
if (empty($tmp)) {
$tmp = sys_get_temp_dir();
}
/* 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?
* @param boolean $session_remove Delete this file when session is
* destroyed?
*
* @return string Returns the full path-name to the temporary file or
* false if a temporary file could not be created.
*/
public static function getTempFile($prefix = 'Horde', $delete = true,
$dir = '', $secure = false,
$session_remove = false)
{
if (empty($dir) || !is_dir($dir)) {
$dir = self::getTempDir();
}
$tmpfile = Horde_Util::getTempFile($prefix, $delete, $dir, $secure);
if ($session_remove) {
$gcfiles = $GLOBALS['session']->get('horde', 'gc_tempfiles', Horde_Session::TYPE_ARRAY);
$gcfiles[] = $tmpfile;
$GLOBALS['session']->set('horde', 'gc_tempfiles', $gcfiles);
}
return $tmpfile;
}
/**
* 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()
*/
public static function webServerID()
{
switch (PHP_SAPI) {
case 'apache':
return 'apache1';
case 'apache2filter':
case 'apache2handler':
return 'apache2';
default:
return PHP_SAPI;
}
}
/**
* 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?
* @param boolean $shutdown Is this called as a shutdown function?
*
* @return string A single lower case character access key, or an empty
* string if no key can be found.
*/
public static 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::log('Access key information for ' . $script);
self::log('Used labels: ' . implode(',', $labels));
self::log('Used keys: ' . implode('', $used));
self::log('Free keys: ' . $remaining);
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('/_(\w)/u', $label, $match)) {
return '';
}
$key = Horde_String_Transliterate::toAscii($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.
*/
public static function stripAccessKey($label)
{
$replace = $GLOBALS['registry']->nlsconfig->curr_multibyte &&
preg_match('/[\x80-\xff]/', $label)
? ''
: '$1';
return preg_replace('/_(\w)/u', $replace, $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.
*/
public static function highlightAccessKey($label, $accessKey)
{
$stripped_label = self::stripAccessKey($label);
if (empty($accessKey)) {
return $stripped_label;
}
if ($GLOBALS['registry']->nlsconfig->curr_multibyte) {
/* Prefix parenthesis with the UTF-8 representation of the LRO
* (Left-to-Right-Override) Unicode codepoint U+202D. */
return $stripped_label . "\xe2\x80\xad" .
'(<span class="accessKey">' . strtoupper($accessKey) .
'</span>' . ')';
}
return preg_replace(
'/_(\w)/u',
'<span class="accessKey">$1</span>',
$label
);
}
/**
* Returns the appropriate "accesskey" and "title" attributes for an HTML
* tag and the given label.
*
* @param string $label The title of an HTML element
* @param boolean $nocheck Don't check if the access key already has
* been used?
* @param boolean $return_array Return attributes as a hash?
*
* @return string The title, and if appropriate, the accesskey attributes
* for the element.
*/
public static function getAccessKeyAndTitle($label, $nocheck = false,
$return_array = false)
{
$ak = self::getAccessKey($label, $nocheck);
$attributes = array('title' => self::stripAccessKey($label));
if (!empty($ak)) {
$attributes['title'] .= sprintf(Horde_Core_Translation::t(" (Accesskey %s)"), strtoupper($ak));
$attributes['accesskey'] = $ak;
}
if ($return_array) {
return $attributes;
}
$html = '';
foreach ($attributes as $attribute => $value) {
$html .= sprintf(' %s="%s"', $attribute, $value);
}
return $html;
}
/**
* 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.
*/
public static function label($for, $label, $ak = null)
{
if (is_null($ak)) {
$ak = self::getAccessKey($label, 1);
}
$label = self::highlightAccessKey($label, $ak);
return sprintf('<label for="%s"%s>%s</label>',
$for,
!empty($ak) ? ' accesskey="' . $ak . '"' : '',
$label);
}
/**
* Print inline javascript to output buffer after wrapping with necessary
* javascript tags.
*
* @param array $script The script to output.
*
* @return string The script with the necessary HTML javascript tags
* appended.
*/
public static function wrapInlineScript($script)
{
return '<script type="text/javascript">//<![CDATA[' . "\n" . implode('', $script) . "\n//]]></script>\n";
}
/**
* Creates a URL for cached data.
*
* @param string $type The cache type ('app', 'css', 'js').
* @param array $params Optional parameters:
* - app: REQUIRED for $type == 'app'. Identifies the application to
* call the 'cacheOutput' API call, which is passed in the
* value of the entire $params array (which may include parameters
* other than those listed here). The return from cacheOutput
* should be a 2-element array: 'data' (the cached data) and
* 'type' (the content-type of the data).
* - cid: REQUIRED for $type == 'css' || 'js'. The cacheid of the
* data (stored in Horde_Cache).
* - nocache: If true, sets the cache limiter to 'nocache' instead of
* the default 'public'.
*
* @return Horde_Url The URL to the cache page.
*/
public static function getCacheUrl($type, $params = array())
{
$url = $GLOBALS['registry']
->getserviceLink('cache', 'horde')
->add('cache', $type);
foreach ($params as $key => $val) {
$url .= '/' . $key . '=' . rawurlencode(strval($val));
}
return self::url($url, true, array('append_session' => -1));
}
/**
* Output the javascript needed to call the popup JS function.
*
* @param string|Horde_Url $url The page to load.
* @param array $options Additional options:
* - height: (integer) The height of the popup window.
* DEFAULT: 650px
* - menu: (boolean) Show the browser menu in the popup window?
* DEFAULT: false
* - onload: (string) A JS function to call after the popup window is
* fully loaded.
* DEFAULT: None
* - params: (array) Additional parameters to pass to the URL.
* DEFAULT: None
* - urlencode: (boolean) URL encode the json string?
* DEFAULT: false
* - width: (integer) The width of the popup window.
* DEFAULT: 700 px
*
* @return string The javascript needed to call the popup code.
*/
public static function popupJs($url, $options = array())
{
$GLOBALS['page_output']->addScriptPackage('Horde_Core_Script_Package_Popup');
$params = new stdClass;
if (!$url instanceof Horde_Url) {
$url = new Horde_Url($url);
}
$params->url = $url->url;
if (!empty($url->parameters)) {
if (!isset($options['params'])) {
$options['params'] = array();
}
foreach (array_merge($url->parameters, $options['params']) as $key => $val) {
$options['params'][$key] = addcslashes($val, '"');
}
}
if (!empty($options['menu'])) {
$params->menu = 1;
}
foreach (array('height', 'onload', 'params', 'width') as $key) {
if (!empty($options[$key])) {
$params->$key = $options[$key];
}
}
return 'void(HordePopup.popup(' . self::escapeJson($params, array('nodelimit' => true, 'urlencode' => !empty($options['urlencode']))) . '));';
}
/**
* Start buffering output.
*/
public static function startBuffer()
{
if (!self::$_bufferLevel) {
self::$_contentSent = self::contentSent();
}
++self::$_bufferLevel;
ob_start();
}
/**
* End buffering output.
*
* @return string The buffered output.
*/
public static function endBuffer()
{
if (self::$_bufferLevel) {
--self::$_bufferLevel;
return ob_get_clean();
}
return '';
}
/**
* Has any content been sent to the browser?
*
* @return boolean True if content has been sent.
*/
public static function contentSent()
{
return ((self::$_bufferLevel && self::$_contentSent) ||
(!self::$_bufferLevel && (ob_get_length() || headers_sent())));
}
/**
* Returns the sidebar for the current application.
*
* @param string $app The application to generate the menu for. Defaults
* to the current app.
*
* @return Horve_View_Sidebar The sidebar.
*/
public static function sidebar($app = null)
{
global $registry;
if (empty($app)) {
$app = $registry->getApp();
}
$menu = new Horde_Menu();
$registry->callAppMethod($app, 'menu', array(
'args' => array($menu)
));
$sidebar = $menu->render();
$registry->callAppMethod($app, 'sidebar', array(
'args' => array($sidebar)
));
return $sidebar;
}
/**
* Process a permission denied error, running a user-defined hook if
* necessary.
*
* @param string $app Application name.
* @param string $perm Permission name.
* @param string $error An error message to output via the notification
* system.
*/
public static function permissionDeniedError($app, $perm, $error = null)
{
try {
$GLOBALS['injector']->getInstance('Horde_Core_Hooks')
->callHook('perms_denied', 'horde', array($app, $perm));
} catch (Horde_Exception_HookNotSet $e) {}
if (!is_null($error)) {
$GLOBALS['notification']->push($error, 'horde.warning');
}
}
/**
* Handle deprecated methods (located in Horde_Deprecated).
*/
public static function __callStatic($name, $arguments)
{
return call_user_func_array(
array('Horde_Deprecated', $name),
$arguments
);
}
}