Page MenuHomePhorge

No OneTemporary

Size
100 KB
Referenced Files
None
Subscribers
None
diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php
index ca1753124..f08533127 100644
--- a/program/lib/Roundcube/bootstrap.php
+++ b/program/lib/Roundcube/bootstrap.php
@@ -1,458 +1,490 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| CONTENTS: |
| Roundcube Framework Initialization |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Roundcube Framework Initialization
*
* @package Framework
* @subpackage Core
*/
$config = array(
'error_reporting' => E_ALL & ~E_NOTICE & ~E_STRICT,
'display_errors' => false,
'log_errors' => true,
// Some users are not using Installer, so we'll check some
// critical PHP settings here. Only these, which doesn't provide
// an error/warning in the logs later. See (#1486307).
'mbstring.func_overload' => 0,
);
// check these additional ini settings if not called via CLI
if (php_sapi_name() != 'cli') {
$config += array(
'suhosin.session.encrypt' => false,
'file_uploads' => true,
'session.auto_start' => false,
'zlib.output_compression' => false,
);
}
foreach ($config as $optname => $optval) {
$ini_optval = filter_var(ini_get($optname), is_bool($optval) ? FILTER_VALIDATE_BOOLEAN : FILTER_VALIDATE_INT);
if ($optval != $ini_optval && @ini_set($optname, $optval) === false) {
$optval = !is_bool($optval) ? $optval : ($optval ? 'On' : 'Off');
$error = "ERROR: Wrong '$optname' option value and it wasn't possible to set it to required value ($optval).\n"
. "Check your PHP configuration (including php_admin_flag).";
if (defined('STDERR')) fwrite(STDERR, $error); else echo $error;
exit(1);
}
}
// framework constants
define('RCUBE_VERSION', '1.4-git');
define('RCUBE_CHARSET', 'UTF-8');
define('RCUBE_TEMP_FILE_PREFIX', 'RCMTEMP');
if (!defined('RCUBE_LIB_DIR')) {
define('RCUBE_LIB_DIR', __DIR__ . '/');
}
if (!defined('RCUBE_INSTALL_PATH')) {
define('RCUBE_INSTALL_PATH', RCUBE_LIB_DIR);
}
if (!defined('RCUBE_CONFIG_DIR')) {
define('RCUBE_CONFIG_DIR', RCUBE_INSTALL_PATH . 'config/');
}
if (!defined('RCUBE_PLUGINS_DIR')) {
define('RCUBE_PLUGINS_DIR', RCUBE_INSTALL_PATH . 'plugins/');
}
if (!defined('RCUBE_LOCALIZATION_DIR')) {
define('RCUBE_LOCALIZATION_DIR', RCUBE_INSTALL_PATH . 'localization/');
}
// set internal encoding for mbstring extension
mb_internal_encoding(RCUBE_CHARSET);
mb_regex_encoding(RCUBE_CHARSET);
// make sure the Roundcube lib directory is in the include_path
$rcube_path = realpath(RCUBE_LIB_DIR . '..');
$sep = PATH_SEPARATOR;
$regexp = "!(^|$sep)" . preg_quote($rcube_path, '!') . "($sep|\$)!";
$path = ini_get('include_path');
if (!preg_match($regexp, $path)) {
set_include_path($path . PATH_SEPARATOR . $rcube_path);
}
// Register autoloader
spl_autoload_register('rcube_autoload');
// set PEAR error handling (will also load the PEAR main class)
PEAR::setErrorHandling(PEAR_ERROR_CALLBACK, function($err) { rcube::raise_error($err, true); });
/**
* Similar function as in_array() but case-insensitive with multibyte support.
*
* @param string $needle Needle value
* @param array $heystack Array to search in
*
* @return boolean True if found, False if not
*/
function in_array_nocase($needle, $haystack)
{
// use much faster method for ascii
if (is_ascii($needle)) {
foreach ((array) $haystack as $value) {
if (strcasecmp($value, $needle) === 0) {
return true;
}
}
}
else {
$needle = mb_strtolower($needle);
foreach ((array) $haystack as $value) {
if ($needle === mb_strtolower($value)) {
return true;
}
}
}
return false;
}
/**
* Parse a human readable string for a number of bytes.
*
* @param string $str Input string
*
* @return float Number of bytes
*/
function parse_bytes($str)
{
if (is_numeric($str)) {
return floatval($str);
}
if (preg_match('/([0-9\.]+)\s*([a-z]*)/i', $str, $regs)) {
$bytes = floatval($regs[1]);
switch (strtolower($regs[2])) {
case 'g':
case 'gb':
$bytes *= 1073741824;
break;
case 'm':
case 'mb':
$bytes *= 1048576;
break;
case 'k':
case 'kb':
$bytes *= 1024;
break;
}
}
return floatval($bytes);
}
/**
* Make sure the string ends with a slash
+ *
+ * @param string $str A string
+ *
+ * @return string A string ending with a slash
*/
function slashify($str)
{
return unslashify($str) . '/';
}
/**
* Remove slashes at the end of the string
+ *
+ * @param string $str A string
+ *
+ * @return string A string ending with no slash
*/
function unslashify($str)
{
return rtrim($str, '/');
}
/**
* Returns number of seconds for a specified offset string.
*
* @param string $str String representation of the offset (e.g. 20min, 5h, 2days, 1week)
*
* @return int Number of seconds
*/
function get_offset_sec($str)
{
if (preg_match('/^([0-9]+)\s*([smhdw])/i', $str, $regs)) {
$amount = (int) $regs[1];
$unit = strtolower($regs[2]);
}
else {
$amount = (int) $str;
$unit = 's';
}
switch ($unit) {
case 'w':
$amount *= 7;
case 'd':
$amount *= 24;
case 'h':
$amount *= 60;
case 'm':
$amount *= 60;
}
return $amount;
}
/**
* Create a unix timestamp with a specified offset from now.
*
* @param string $offset_str String representation of the offset (e.g. 20min, 5h, 2days)
* @param int $factor Factor to multiply with the offset
*
* @return int Unix timestamp
*/
function get_offset_time($offset_str, $factor = 1)
{
return time() + get_offset_sec($offset_str) * $factor;
}
/**
* Truncate string if it is longer than the allowed length.
* Replace the middle or the ending part of a string with a placeholder.
*
* @param string $str Input string
* @param int $maxlength Max. length
* @param string $placeholder Replace removed chars with this
* @param bool $ending Set to True if string should be truncated from the end
*
* @return string Abbreviated string
*/
function abbreviate_string($str, $maxlength, $placeholder = '...', $ending = false)
{
$length = mb_strlen($str);
if ($length > $maxlength) {
if ($ending) {
return mb_substr($str, 0, $maxlength) . $placeholder;
}
$placeholder_length = mb_strlen($placeholder);
$first_part_length = floor(($maxlength - $placeholder_length)/2);
$second_starting_location = $length - $maxlength + $first_part_length + $placeholder_length;
$prefix = mb_substr($str, 0, $first_part_length);
$suffix = mb_substr($str, $second_starting_location);
$str = $prefix . $placeholder . $suffix;
}
return $str;
}
/**
* Get all keys from array (recursive).
*
* @param array $array Input array
*
* @return array List of array keys
*/
function array_keys_recursive($array)
{
$keys = array();
if (!empty($array) && is_array($array)) {
foreach ($array as $key => $child) {
$keys[] = $key;
foreach (array_keys_recursive($child) as $val) {
$keys[] = $val;
}
}
}
return $keys;
}
/**
* Remove all non-ascii and non-word chars except ., -, _
+ *
+ * @param string $str A string
+ * @param bool $css_id The result may be used as CSS identifier
+ * @param string $replace_with Replacement character
+ *
+ * @return string Clean string
*/
function asciiwords($str, $css_id = false, $replace_with = '')
{
$allowed = 'a-z0-9\_\-' . (!$css_id ? '\.' : '');
return preg_replace("/[^$allowed]/i", $replace_with, $str);
}
/**
* Check if a string contains only ascii characters
*
* @param string $str String to check
* @param bool $control_chars Includes control characters
*
- * @return bool
+ * @return bool True if the string contains ASCII-only, False otherwise
*/
function is_ascii($str, $control_chars = true)
{
$regexp = $control_chars ? '/[^\x00-\x7F]/' : '/[^\x20-\x7E]/';
return preg_match($regexp, $str) ? false : true;
}
/**
* Compose a valid representation of name and e-mail address
*
* @param string $email E-mail address
* @param string $name Person name
*
* @return string Formatted string
*/
function format_email_recipient($email, $name = '')
{
$email = trim($email);
if ($name && $name != $email) {
// Special chars as defined by RFC 822 need to in quoted string (or escaped).
if (preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) {
$name = '"'.addcslashes($name, '"').'"';
}
return "$name <$email>";
}
return $email;
}
/**
* Format e-mail address
*
* @param string $email E-mail address
*
* @return string Formatted e-mail address
*/
function format_email($email)
{
$email = trim($email);
$parts = explode('@', $email);
$count = count($parts);
if ($count > 1) {
$parts[$count-1] = mb_strtolower($parts[$count-1]);
$email = implode('@', $parts);
}
return $email;
}
/**
* Fix version number so it can be used correctly in version_compare()
*
* @param string $version Version number string
*
* @param return Version number string
*/
function version_parse($version)
{
return str_replace(
array('-stable', '-git'),
array('.0', '.99'),
$version
);
}
-/**
- * intl replacement functions
- */
+// intl replacement functions
if (!function_exists('idn_to_utf8'))
{
+ /**
+ * Convert domain name from IDNA ASCII to Unicode
+ *
+ * @param string $domain Domain to convert in an IDNA ASCII-compatible format.
+ *
+ * @return string|false Unicode domain, False on failure
+ */
function idn_to_utf8($domain)
{
static $idn, $loaded;
if (!$loaded) {
$idn = new Net_IDNA2(array('version' => '2008'));
$loaded = true;
}
if ($idn && $domain && preg_match('/(^|\.)xn--/i', $domain)) {
try {
$domain = $idn->decode($domain);
}
catch (Exception $e) {
+ return false;
}
}
return $domain;
}
}
if (!function_exists('idn_to_ascii'))
{
+ /**
+ * Convert domain name to IDNA ASCII-compatible form Unicode
+ *
+ * @param string $domain The domain to convert, which must be UTF-8 encoded
+ *
+ * @return string|false The domain name encoded in ASCII-compatible form, False on failure
+ */
function idn_to_ascii($domain)
{
static $idn, $loaded;
if (!$loaded) {
$idn = new Net_IDNA2(array('version' => '2008'));
$loaded = true;
}
if ($idn && $domain && preg_match('/[^\x20-\x7E]/', $domain)) {
try {
$domain = $idn->encode($domain);
}
catch (Exception $e) {
+ return false;
}
}
return $domain;
}
}
/**
* Use PHP5 autoload for dynamic class loading
*
+ * @param string $classname Class name
+ *
+ * @return bool True when the class file has been found
+ *
* @todo Make Zend, PEAR etc play with this
* @todo Make our classes conform to a more straight forward CS.
*/
function rcube_autoload($classname)
{
if (strpos($classname, 'rcube') === 0) {
$classname = preg_replace('/^rcube_(cache|db|session|spellchecker)_/', '\\1/', $classname);
$classname = 'Roundcube/' . $classname;
}
else if (strpos($classname, 'html_') === 0 || $classname === 'html') {
$classname = 'Roundcube/html';
}
else if (strpos($classname, 'Mail_') === 0) {
$classname = 'Mail/' . substr($classname, 5);
}
else if (strpos($classname, 'Net_') === 0) {
$classname = 'Net/' . substr($classname, 4);
}
else if (strpos($classname, 'Auth_') === 0) {
$classname = 'Auth/' . substr($classname, 5);
}
// Translate PHP namespaces into directories,
// i.e. use \Sabre\VObject; $vcf = VObject\Reader::read(...)
// -> Sabre/VObject/Reader.php
$classname = str_replace('\\', '/', $classname);
if ($fp = @fopen("$classname.php", 'r', true)) {
fclose($fp);
include_once "$classname.php";
return true;
}
return false;
}
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index 71ca68e3e..c7fb3f9e2 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -1,986 +1,988 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Helper class to create valid XHTML code |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Class for HTML code creation
*
* @package Framework
* @subpackage View
*/
class html
{
protected $tagname;
protected $content;
protected $attrib = array();
protected $allowed = array();
public static $doctype = 'xhtml';
public static $lc_tags = true;
public static $common_attrib = array('id','class','style','title','align','unselectable','tabindex','role');
public static $containers = array('iframe','div','span','p','h1','h2','h3','ul','form','textarea','table','thead','tbody','tr','th','td','style','script','a');
public static $bool_attrib = array('checked','multiple','disabled','selected','autofocus','readonly','required');
/**
* Constructor
*
* @param array $attrib Hash array with tag attributes
*/
public function __construct($attrib = array())
{
if (is_array($attrib)) {
$this->attrib = $attrib;
}
}
/**
* Return the tag code
*
* @return string The finally composed HTML tag
*/
public function show()
{
return self::tag($this->tagname, $this->attrib, $this->content, array_merge(self::$common_attrib, $this->allowed));
}
/****** STATIC METHODS *******/
/**
* Generic method to create a HTML tag
*
* @param string $tagname Tag name
* @param array $attrib Tag attributes as key/value pairs
* @param string $content Optional Tag content (creates a container tag)
* @param array $allowed List with allowed attributes, omit to allow all
*
* @return string The XHTML tag
*/
public static function tag($tagname, $attrib = array(), $content = null, $allowed = null)
{
if (is_string($attrib)) {
$attrib = array('class' => $attrib);
}
$inline_tags = array('a','span','img');
$suffix = $attrib['nl'] || ($content && $attrib['nl'] !== false && !in_array($tagname, $inline_tags)) ? "\n" : '';
$tagname = self::$lc_tags ? strtolower($tagname) : $tagname;
if (isset($content) || in_array($tagname, self::$containers)) {
$suffix = $attrib['noclose'] ? $suffix : '</' . $tagname . '>' . $suffix;
unset($attrib['noclose'], $attrib['nl']);
return '<' . $tagname . self::attrib_string($attrib, $allowed) . '>' . $content . $suffix;
}
else {
return '<' . $tagname . self::attrib_string($attrib, $allowed) . '>' . $suffix;
}
}
/**
* Return DOCTYPE tag of specified type
*
* @param string $type Document type (html5, xhtml, 'xhtml-trans, xhtml-strict)
*/
public static function doctype($type)
{
$doctypes = array(
'html5' => '<!DOCTYPE html>',
'xhtml' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
);
if ($doctypes[$type]) {
self::$doctype = preg_replace('/-\w+$/', '', $type);
return $doctypes[$type];
}
return '';
}
/**
* Derrived method for <div> containers
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Div content
*
* @return string HTML code
* @see html::tag()
*/
public static function div($attr = null, $cont = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('div', $attr, $cont, array_merge(self::$common_attrib, array('onclick')));
}
/**
* Derrived method for <p> blocks
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Paragraph content
*
* @return string HTML code
* @see html::tag()
*/
public static function p($attr = null, $cont = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('p', $attr, $cont, self::$common_attrib);
}
/**
* Derrived method to create <img />
*
* @param mixed $attr Hash array with tag attributes or string with image source (src)
*
* @return string HTML code
* @see html::tag()
*/
public static function img($attr = null)
{
if (is_string($attr)) {
$attr = array('src' => $attr);
}
return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib,
array('src','alt','width','height','border','usemap','onclick','onerror','onload')));
}
/**
* Derrived method for link tags
*
* @param mixed $attr Hash array with tag attributes or string with link location (href)
* @param string $cont Link content
*
* @return string HTML code
* @see html::tag()
*/
public static function a($attr, $cont)
{
if (is_string($attr)) {
$attr = array('href' => $attr);
}
return self::tag('a', $attr, $cont, array_merge(self::$common_attrib,
array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup')));
}
/**
* Derrived method for inline span tags
*
* @param mixed $attr Hash array with tag attributes or string with class name
* @param string $cont Tag content
*
* @return string HTML code
* @see html::tag()
*/
public static function span($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
return self::tag('span', $attr, $cont, self::$common_attrib);
}
/**
* Derrived method for form element labels
*
* @param mixed $attr Hash array with tag attributes or string with 'for' attrib
* @param string $cont Tag content
*
* @return string HTML code
* @see html::tag()
*/
public static function label($attr, $cont)
{
if (is_string($attr)) {
$attr = array('for' => $attr);
}
return self::tag('label', $attr, $cont, array_merge(self::$common_attrib,
array('for','onkeypress')));
}
/**
* Derrived method to create <iframe></iframe>
*
- * @param mixed $attr Hash array with tag attributes or string with frame source (src)
+ * @param mixed $attr Hash array with tag attributes or string with frame source (src)
+ * @param string $cont Tag content
*
* @return string HTML code
* @see html::tag()
*/
public static function iframe($attr = null, $cont = null)
{
if (is_string($attr)) {
$attr = array('src' => $attr);
}
return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib,
array('src','name','width','height','border','frameborder','onload','allowfullscreen')));
}
/**
* Derrived method to create <script> tags
*
* @param mixed $attr Hash array with tag attributes or string with script source (src)
* @param string $cont Javascript code to be placed as tag content
*
* @return string HTML code
* @see html::tag()
*/
public static function script($attr, $cont = null)
{
if (is_string($attr)) {
$attr = array('src' => $attr);
}
if ($cont) {
if (self::$doctype == 'xhtml')
$cont = "\n/* <![CDATA[ */\n" . $cont . "\n/* ]]> */\n";
else
$cont = "\n" . $cont . "\n";
}
return self::tag('script', $attr + array('type' => 'text/javascript', 'nl' => true),
$cont, array_merge(self::$common_attrib, array('src','type','charset')));
}
/**
* Derrived method for line breaks
*
* @param array $attrib Associative arry with tag attributes
*
* @return string HTML code
* @see html::tag()
*/
public static function br($attrib = array())
{
return self::tag('br', $attrib);
}
/**
* Create string with attributes
*
* @param array $attrib Associative array with tag attributes
* @param array $allowed List of allowed attributes
*
* @return string Valid attribute string
*/
public static function attrib_string($attrib = array(), $allowed = null)
{
if (empty($attrib)) {
return '';
}
$allowed_f = array_flip((array)$allowed);
$attrib_arr = array();
foreach ($attrib as $key => $value) {
// skip size if not numeric
if ($key == 'size' && !is_numeric($value)) {
continue;
}
// ignore "internal" or empty attributes
if ($key == 'nl' || $value === null) {
continue;
}
// ignore not allowed attributes, except aria-* and data-*
if (!empty($allowed)) {
$is_data_attr = @substr_compare($key, 'data-', 0, 5) === 0;
$is_aria_attr = @substr_compare($key, 'aria-', 0, 5) === 0;
if (!$is_aria_attr && !$is_data_attr && !isset($allowed_f[$key])) {
continue;
}
}
// skip empty eventhandlers
if (preg_match('/^on[a-z]+/', $key) && !$value) {
continue;
}
// attributes with no value
if (in_array($key, self::$bool_attrib)) {
if ($value) {
$value = $key;
if (self::$doctype == 'xhtml') {
$value .= '="' . $value . '"';
}
$attrib_arr[] = $value;
}
}
else {
$attrib_arr[] = $key . '="' . self::quote($value) . '"';
}
}
return count($attrib_arr) ? ' '.implode(' ', $attrib_arr) : '';
}
/**
* Convert a HTML attribute string attributes to an associative array (name => value)
*
* @param string $str Input string
*
* @return array Key-value pairs of parsed attributes
*/
public static function parse_attrib_string($str)
{
$attrib = array();
$html = '<html>'
. '<head><meta http-equiv="Content-Type" content="text/html; charset=' . RCUBE_CHARSET . '" /></head>'
. '<body><div ' . rtrim($str, '/ ') . ' /></body>'
. '</html>';
$document = new DOMDocument('1.0', RCUBE_CHARSET);
@$document->loadHTML($html);
if ($node = $document->getElementsByTagName('div')->item(0)) {
foreach ($node->attributes as $name => $attr) {
$attrib[strtolower($name)] = $attr->nodeValue;
}
}
return $attrib;
}
/**
* Replacing specials characters in html attribute value
*
* @param string $str Input string
*
* @return string The quoted string
*/
public static function quote($str)
{
return @htmlspecialchars($str, ENT_COMPAT | ENT_SUBSTITUTE, RCUBE_CHARSET);
}
}
/**
* Class to create an HTML input field
*
* @package Framework
* @subpackage View
*/
class html_inputfield extends html
{
protected $tagname = 'input';
protected $type = 'text';
protected $allowed = array(
'type','name','value','size','tabindex','autocapitalize','required',
'autocomplete','checked','onchange','onclick','disabled','readonly',
'spellcheck','results','maxlength','src','multiple','accept',
'placeholder','autofocus','pattern','oninput'
);
/**
* Object constructor
*
* @param array $attrib Associative array with tag attributes
*/
public function __construct($attrib = array())
{
if (is_array($attrib)) {
$this->attrib = $attrib;
}
if ($attrib['type']) {
$this->type = $attrib['type'];
}
}
/**
* Compose input tag
*
* @param string $value Field value
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($value = null, $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
if ($value !== null) {
$this->attrib['value'] = $value;
}
// set type
$this->attrib['type'] = $this->type;
return parent::show();
}
}
/**
* Class to create an HTML password field
*
* @package Framework
* @subpackage View
*/
class html_passwordfield extends html_inputfield
{
protected $type = 'password';
}
/**
* Class to create an hidden HTML input field
*
* @package Framework
* @subpackage View
*/
class html_hiddenfield extends html
{
protected $tagname = 'input';
protected $type = 'hidden';
protected $allowed = array('type','name','value','onchange','disabled','readonly');
protected $fields = array();
/**
* Constructor
*
* @param array $attrib Named tag attributes
*/
public function __construct($attrib = null)
{
if (is_array($attrib)) {
$this->add($attrib);
}
}
/**
* Add a hidden field to this instance
*
* @param array $attrib Named tag attributes
*/
public function add($attrib)
{
$this->fields[] = $attrib;
}
/**
* Create HTML code for the hidden fields
*
* @return string Final HTML code
*/
public function show()
{
$out = '';
foreach ($this->fields as $attrib) {
$out .= self::tag($this->tagname, array('type' => $this->type) + $attrib);
}
return $out;
}
}
/**
* Class to create HTML radio buttons
*
* @package Framework
* @subpackage View
*/
class html_radiobutton extends html_inputfield
{
protected $type = 'radio';
/**
* Get HTML code for this object
*
* @param string $value Value of the checked field
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
$this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
return parent::show();
}
}
/**
* Class to create HTML checkboxes
*
* @package Framework
* @subpackage View
*/
class html_checkbox extends html_inputfield
{
protected $type = 'checkbox';
/**
* Get HTML code for this object
*
* @param string $value Value of the checked field
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// set value attribute
$this->attrib['checked'] = ((string)$value == (string)$this->attrib['value']);
return parent::show();
}
}
/**
* Class to create HTML button
*
* @package Framework
* @subpackage View
*/
class html_button extends html_inputfield
{
protected $tagname = 'button';
protected $type = 'button';
/**
* Get HTML code for this object
*
* @param string $content Text Content of the button
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($content = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
$this->content = $content;
return parent::show();
}
}
/**
* Class to create an HTML textarea
*
* @package Framework
* @subpackage View
*/
class html_textarea extends html
{
protected $tagname = 'textarea';
protected $allowed = array('name','rows','cols','wrap','tabindex',
'onchange','disabled','readonly','spellcheck');
/**
* Get HTML code for this object
*
* @param string $value Textbox value
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($value = '', $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
// take value attribute as content
if (empty($value) && !empty($this->attrib['value'])) {
$value = $this->attrib['value'];
}
// make shure we don't print the value attribute
if (isset($this->attrib['value'])) {
unset($this->attrib['value']);
}
if (!empty($value) && empty($this->attrib['is_escaped'])) {
$value = self::quote($value);
}
return self::tag($this->tagname, $this->attrib, $value,
array_merge(self::$common_attrib, $this->allowed));
}
}
/**
* Builder for HTML drop-down menus
* Syntax:<pre>
* // create instance. arguments are used to set attributes of select-tag
* $select = new html_select(array('name' => 'fieldname'));
*
* // add one option
* $select->add('Switzerland', 'CH');
*
* // add multiple options
* $select->add(array('Switzerland','Germany'), array('CH','DE'));
*
* // generate pulldown with selection 'Switzerland' and return html-code
* // as second argument the same attributes available to instantiate can be used
* print $select->show('CH');
* </pre>
*
* @package Framework
* @subpackage View
*/
class html_select extends html
{
protected $tagname = 'select';
protected $options = array();
protected $allowed = array('name','size','tabindex','autocomplete',
'multiple','onchange','disabled','rel');
/**
* Add a new option to this drop-down
*
* @param mixed $names Option name or array with option names
* @param mixed $values Option value or array with option values
* @param array $attrib Additional attributes for the option entry
*/
public function add($names, $values = null, $attrib = array())
{
if (is_array($names)) {
foreach ($names as $i => $text) {
$this->options[] = array('text' => $text, 'value' => $values[$i]) + $attrib;
}
}
else {
$this->options[] = array('text' => $names, 'value' => $values) + $attrib;
}
}
/**
* Get HTML code for this object
*
* @param string $select Value of the selection option
* @param array $attrib Additional attributes to override
*
* @return string HTML output
*/
public function show($select = array(), $attrib = null)
{
// overwrite object attributes
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
$this->content = "\n";
$select = (array)$select;
foreach ($this->options as $option) {
$attr = array(
'value' => $option['value'],
'selected' => (in_array($option['value'], $select, true) ||
in_array($option['text'], $select, true)) ? 1 : null);
$option_content = $option['text'];
if (empty($this->attrib['is_escaped'])) {
$option_content = self::quote($option_content);
}
$this->content .= self::tag('option', $attr + $option, $option_content, array('value','label','class','style','title','disabled','selected'));
}
return parent::show();
}
}
/**
* Class to build an HTML table
*
* @package Framework
* @subpackage View
*/
class html_table extends html
{
protected $tagname = 'table';
protected $allowed = array('id','class','style','width','summary',
'cellpadding','cellspacing','border');
private $header = array();
private $rows = array();
private $rowindex = 0;
private $colindex = 0;
/**
* Constructor
*
* @param array $attrib Named tag attributes
*/
public function __construct($attrib = array())
{
$default_attrib = self::$doctype == 'xhtml' ? array('summary' => '', 'border' => '0') : array();
$this->attrib = array_merge($attrib, $default_attrib);
if (!empty($attrib['tagname']) && $attrib['tagname'] != 'table') {
$this->tagname = $attrib['tagname'];
$this->allowed = self::$common_attrib;
}
}
/**
* Add a table cell
*
* @param array $attr Cell attributes
* @param string $cont Cell content
*/
public function add($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
$cell = new stdClass;
$cell->attrib = $attr;
$cell->content = $cont;
$this->rows[$this->rowindex]->cells[$this->colindex] = $cell;
$this->colindex += max(1, intval($attr['colspan']));
if ($this->attrib['cols'] && $this->colindex >= $this->attrib['cols']) {
$this->add_row();
}
}
/**
* Add a table header cell
*
* @param array $attr Cell attributes
* @param string $cont Cell content
*/
public function add_header($attr, $cont)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
$cell = new stdClass;
$cell->attrib = $attr;
$cell->content = $cont;
$this->header[] = $cell;
}
/**
* Remove a column from a table
* Useful for plugins making alterations
*
* @param string $class Class name
*/
public function remove_column($class)
{
// Remove the header
foreach ($this->header as $index => $header){
if ($header->attrib['class'] == $class){
unset($this->header[$index]);
break;
}
}
// Remove cells from rows
foreach ($this->rows as $i => $row){
foreach ($row->cells as $j => $cell){
if ($cell->attrib['class'] == $class){
unset($this->rows[$i]->cells[$j]);
break;
}
}
}
}
/**
* Jump to next row
*
* @param array $attr Row attributes
*/
public function add_row($attr = array())
{
$this->rowindex++;
$this->colindex = 0;
$this->rows[$this->rowindex] = new stdClass;
$this->rows[$this->rowindex]->attrib = $attr;
$this->rows[$this->rowindex]->cells = array();
}
/**
* Set row attributes
*
* @param array $attr Row attributes
* @param int $index Optional row index (default current row index)
*/
public function set_row_attribs($attr = array(), $index = null)
{
if (is_string($attr)) {
$attr = array('class' => $attr);
}
if ($index === null) {
$index = $this->rowindex;
}
// make sure row object exists (#1489094)
if (!$this->rows[$index]) {
$this->rows[$index] = new stdClass;
}
$this->rows[$index]->attrib = $attr;
}
/**
* Get row attributes
*
* @param int $index Row index
*
* @return array Row attributes
*/
public function get_row_attribs($index = null)
{
if ($index === null) {
$index = $this->rowindex;
}
return $this->rows[$index] ? $this->rows[$index]->attrib : null;
}
/**
* Build HTML output of the table data
*
* @param array $attrib Table attributes
*
* @return string The final table HTML code
*/
public function show($attrib = null)
{
if (is_array($attrib)) {
$this->attrib = array_merge($this->attrib, $attrib);
}
$thead = '';
$tbody = '';
$col_tagname = $this->_col_tagname();
$row_tagname = $this->_row_tagname();
$head_tagname = $this->_head_tagname();
// include <thead>
if (!empty($this->header)) {
$rowcontent = '';
foreach ($this->header as $c => $col) {
$rowcontent .= self::tag($head_tagname, $col->attrib, $col->content);
}
$thead = $this->tagname == 'table' ? self::tag('thead', null, self::tag('tr', null, $rowcontent, parent::$common_attrib)) :
self::tag($row_tagname, array('class' => 'thead'), $rowcontent, parent::$common_attrib);
}
foreach ($this->rows as $r => $row) {
$rowcontent = '';
foreach ($row->cells as $c => $col) {
if ($row_tagname == 'li' && empty($col->attrib) && count($row->cells) == 1) {
$rowcontent .= $col->content;
}
else {
$rowcontent .= self::tag($col_tagname, $col->attrib, $col->content);
}
}
if ($r < $this->rowindex || count($row->cells)) {
$tbody .= self::tag($row_tagname, $row->attrib, $rowcontent, parent::$common_attrib);
}
}
if ($this->attrib['rowsonly']) {
return $tbody;
}
// add <tbody>
$this->content = $thead . ($this->tagname == 'table' ? self::tag('tbody', null, $tbody) : $tbody);
unset($this->attrib['cols'], $this->attrib['rowsonly']);
+
return parent::show();
}
/**
* Count number of rows
*
- * @return The number of rows
+ * @return int The number of rows
*/
public function size()
{
return count($this->rows);
}
/**
* Remove table body (all rows)
*/
public function remove_body()
{
$this->rows = array();
$this->rowindex = 0;
}
/**
* Getter for the corresponding tag name for table row elements
*/
private function _row_tagname()
{
static $row_tagnames = array('table' => 'tr', 'ul' => 'li', '*' => 'div');
return $row_tagnames[$this->tagname] ?: $row_tagnames['*'];
}
/**
* Getter for the corresponding tag name for table row elements
*/
private function _head_tagname()
{
static $head_tagnames = array('table' => 'th', '*' => 'span');
return $head_tagnames[$this->tagname] ?: $head_tagnames['*'];
}
/**
* Getter for the corresponding tag name for table cell elements
*/
private function _col_tagname()
{
static $col_tagnames = array('table' => 'td', '*' => 'span');
return $col_tagnames[$this->tagname] ?: $col_tagnames['*'];
}
}
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 5047de2fd..2c3b5071e 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -1,1768 +1,1739 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Framework base class providing core functions and holding |
| instances of all 'global' objects like db- and storage-connections |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Base class of the Roundcube Framework
* implemented as singleton
*
* @package Framework
* @subpackage Core
*/
class rcube
{
// Init options
const INIT_WITH_DB = 1;
const INIT_WITH_PLUGINS = 2;
// Request status
const REQUEST_VALID = 0;
const REQUEST_ERROR_URL = 1;
const REQUEST_ERROR_TOKEN = 2;
const DEBUG_LINE_LENGTH = 4096;
- /**
- * Singleton instance of rcube
- *
- * @var rcube
- */
- static protected $instance;
-
- /**
- * Stores instance of rcube_config.
- *
- * @var rcube_config
- */
+ /** @var rcube_config Stores instance of rcube_config */
public $config;
- /**
- * Instance of database class.
- *
- * @var rcube_db
- */
+ /** @var rcube_db Instance of database class */
public $db;
- /**
- * Instance of Memcache class.
- *
- * @var Memcache
- */
+ /** @var Memcache Instance of Memcache class */
public $memcache;
- /**
- * Instance of Memcached class.
- *
- * @var Memcached
- */
+ /** @var Memcached Instance of Memcached class */
public $memcached;
- /**
- * Instance of Redis class.
- *
- * @var Redis
- */
+ /** @var Redis Instance of Redis class */
public $redis;
- /**
- * Instance of rcube_session class.
- *
- * @var rcube_session
- */
+ /** @var rcube_session Instance of rcube_session class */
public $session;
- /**
- * Instance of rcube_smtp class.
- *
- * @var rcube_smtp
- */
+ /** @var rcube_smtp Instance of rcube_smtp class */
public $smtp;
- /**
- * Instance of rcube_storage class.
- *
- * @var rcube_storage
- */
+ /** @var rcube_storage Instance of rcube_storage class */
public $storage;
- /**
- * Instance of rcube_output class.
- *
- * @var rcube_output
- */
+ /** @var rcube_output Instance of rcube_output class */
public $output;
- /**
- * Instance of rcube_plugin_api.
- *
- * @var rcube_plugin_api
- */
+ /** @var rcube_plugin_api Instance of rcube_plugin_api */
public $plugins;
- /**
- * Instance of rcube_user class.
- *
- * @var rcube_user
- */
+ /** @var rcube_user Instance of rcube_user class */
public $user;
- /**
- * Request status
- *
- * @var int
- */
+ /** @var int Request status */
public $request_status = 0;
- /* private/protected vars */
+ /** @var array Localization */
protected $texts;
+
+ /** @var rcube_cache[] Initialized cache objects */
protected $caches = array();
+
+ /** @var array Registered shutdown functions */
protected $shutdown_functions = array();
+ /** @var rcube Singleton instance of rcube */
+ static protected $instance;
+
/**
* This implements the 'singleton' design pattern
*
- * @param integer $mode Options to initialize with this instance. See rcube::INIT_WITH_* constants
- * @param string $env Environment name to run (e.g. live, dev, test)
+ * @param int $mode Options to initialize with this instance. See rcube::INIT_WITH_* constants
+ * @param string $env Environment name to run (e.g. live, dev, test)
*
* @return rcube The one and only instance
*/
static function get_instance($mode = 0, $env = '')
{
if (!self::$instance) {
self::$instance = new rcube($env);
self::$instance->init($mode);
}
return self::$instance;
}
/**
* Private constructor
+ *
+ * @param string $env Environment name to run (e.g. live, dev, test)
*/
protected function __construct($env = '')
{
// load configuration
$this->config = new rcube_config($env);
$this->plugins = new rcube_dummy_plugin_api;
register_shutdown_function(array($this, 'shutdown'));
}
/**
* Initial startup function
+ *
+ * @param int $mode Options to initialize with this instance. See rcube::INIT_WITH_* constants
*/
protected function init($mode = 0)
{
// initialize syslog
if ($this->config->get('log_driver') == 'syslog') {
$syslog_id = $this->config->get('syslog_id', 'roundcube');
$syslog_facility = $this->config->get('syslog_facility', LOG_USER);
openlog($syslog_id, LOG_ODELAY, $syslog_facility);
}
// connect to database
if ($mode & self::INIT_WITH_DB) {
$this->get_dbh();
}
// create plugin API and load plugins
if ($mode & self::INIT_WITH_PLUGINS) {
$this->plugins = rcube_plugin_api::get_instance();
}
}
/**
* Get the current database connection
*
* @return rcube_db Database object
*/
public function get_dbh()
{
if (!$this->db) {
$this->db = rcube_db::factory(
$this->config->get('db_dsnw'),
$this->config->get('db_dsnr'),
$this->config->get('db_persistent')
);
$this->db->set_debug((bool)$this->config->get('sql_debug'));
}
return $this->db;
}
/**
* Get global handle for memcache access
*
* @return object Memcache
*/
public function get_memcache()
{
if (!isset($this->memcache)) {
$this->memcache = rcube_cache_memcache::engine();
}
return $this->memcache;
}
/**
* Get global handle for memcached access
*
* @return object Memcached
*/
public function get_memcached()
{
if (!isset($this->memcached)) {
$this->memcached = rcube_cache_memcached::engine();
}
return $this->memcached;
}
/**
* Get global handle for redis access
*
* @return object Redis
*/
public function get_redis()
{
if (!isset($this->redis)) {
$this->redis = rcube_cache_redis::engine();
}
return $this->redis;
}
/**
* Initialize and get user cache object
*
* @param string $name Cache identifier
* @param string $type Cache type ('db', 'apc', 'memcache', 'redis')
* @param string $ttl Expiration time for cache items
* @param bool $packed Enables/disables data serialization
*
* @return rcube_cache User cache object
*/
public function get_cache($name, $type='db', $ttl=0, $packed=true)
{
if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) {
$this->caches[$name] = rcube_cache::factory($type, $userid, $name, $ttl, $packed);
}
return $this->caches[$name];
}
/**
* Initialize and get shared cache object
*
* @param string $name Cache identifier
* @param bool $packed Enables/disables data serialization
*
* @return rcube_cache Shared cache object
*/
public function get_cache_shared($name, $packed = true)
{
$shared_name = "shared_$name";
if (!array_key_exists($shared_name, $this->caches)) {
$opt = strtolower($name) . '_cache';
$type = $this->config->get($opt);
$ttl = $this->config->get($opt . '_ttl');
if (!$type) {
// cache is disabled
return $this->caches[$shared_name] = null;
}
if ($ttl === null) {
$ttl = $this->config->get('shared_cache_ttl', '10d');
}
$this->caches[$shared_name] = rcube_cache::factory($type, null, $name, $ttl, $packed);
}
return $this->caches[$shared_name];
}
/**
* Create SMTP object and connect to server
*
* @param boolean $connect True if connection should be established
*/
public function smtp_init($connect = false)
{
$this->smtp = new rcube_smtp();
if ($connect) {
$this->smtp->connect();
}
}
/**
* Initialize and get storage object
*
* @return rcube_storage Storage object
*/
public function get_storage()
{
// already initialized
if (!is_object($this->storage)) {
$this->storage_init();
}
return $this->storage;
}
/**
* Initialize storage object
*/
public function storage_init()
{
// already initialized
if (is_object($this->storage)) {
return;
}
$driver = $this->config->get('storage_driver', 'imap');
$driver_class = "rcube_{$driver}";
if (!class_exists($driver_class)) {
self::raise_error(array(
'code' => 700, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Storage driver class ($driver) not found!"),
true, true);
}
// Initialize storage object
$this->storage = new $driver_class;
// for backward compat. (deprecated, will be removed)
$this->imap = $this->storage;
// set class options
$options = array(
'auth_type' => $this->config->get("{$driver}_auth_type", 'check'),
'auth_cid' => $this->config->get("{$driver}_auth_cid"),
'auth_pw' => $this->config->get("{$driver}_auth_pw"),
'debug' => (bool) $this->config->get("{$driver}_debug"),
'force_caps' => (bool) $this->config->get("{$driver}_force_caps"),
'disabled_caps' => $this->config->get("{$driver}_disabled_caps"),
'socket_options' => $this->config->get("{$driver}_conn_options"),
'timeout' => (int) $this->config->get("{$driver}_timeout"),
'skip_deleted' => (bool) $this->config->get('skip_deleted'),
'driver' => $driver,
);
if (!empty($_SESSION['storage_host'])) {
$options['language'] = $_SESSION['language'];
$options['host'] = $_SESSION['storage_host'];
$options['user'] = $_SESSION['username'];
$options['port'] = $_SESSION['storage_port'];
$options['ssl'] = $_SESSION['storage_ssl'];
$options['password'] = $this->decrypt($_SESSION['password']);
$_SESSION[$driver.'_host'] = $_SESSION['storage_host'];
}
$options = $this->plugins->exec_hook("storage_init", $options);
// for backward compat. (deprecated, to be removed)
$options = $this->plugins->exec_hook("imap_init", $options);
$this->storage->set_options($options);
$this->set_storage_prop();
// subscribe to 'storage_connected' hook for session logging
if ($this->config->get('imap_log_session', false)) {
$this->plugins->register_hook('storage_connected', array($this, 'storage_log_session'));
}
}
/**
* Set storage parameters.
*/
protected function set_storage_prop()
{
$storage = $this->get_storage();
// set pagesize from config
$pagesize = $this->config->get('mail_pagesize');
if (!$pagesize) {
$pagesize = $this->config->get('pagesize', 50);
}
$storage->set_pagesize($pagesize);
$storage->set_charset($this->config->get('default_charset', RCUBE_CHARSET));
// enable caching of mail data
$driver = $this->config->get('storage_driver', 'imap');
$storage_cache = $this->config->get("{$driver}_cache");
$messages_cache = $this->config->get('messages_cache');
// for backward compatybility
if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) {
$storage_cache = 'db';
$messages_cache = true;
}
if ($storage_cache) {
$storage->set_caching($storage_cache);
}
if ($messages_cache) {
$storage->set_messages_caching(true);
}
}
/**
* Set special folders type association.
* This must be done AFTER connecting to the server!
*/
protected function set_special_folders()
{
$storage = $this->get_storage();
$folders = $storage->get_special_folders(true);
$prefs = array();
// check SPECIAL-USE flags on IMAP folders
foreach ($folders as $type => $folder) {
$idx = $type . '_mbox';
if ($folder !== $this->config->get($idx)) {
$prefs[$idx] = $folder;
}
}
// Some special folders differ, update user preferences
if (!empty($prefs) && $this->user) {
$this->user->save_prefs($prefs);
}
// create default folders (on login)
if ($this->config->get('create_default_folders')) {
$storage->create_default_folders();
}
}
/**
* Callback for IMAP connection events to log session identifiers
+ *
+ * @param array $args Callback arguments
*/
public function storage_log_session($args)
{
if (!empty($args['session']) && session_id()) {
$this->write_log('imap_session', $args['session']);
}
}
/**
* Create session object and start the session.
*/
public function session_init()
{
// Ignore in CLI mode or when session started (Installer?)
if (empty($_SERVER['REMOTE_ADDR']) || session_id()) {
return;
}
$storage = $this->config->get('session_storage', 'db');
$sess_name = $this->config->get('session_name');
$sess_domain = $this->config->get('session_domain');
$sess_path = $this->config->get('session_path');
$lifetime = $this->config->get('session_lifetime', 0) * 60;
$is_secure = $this->config->get('use_https') || rcube_utils::https_check();
// set session domain
if ($sess_domain) {
ini_set('session.cookie_domain', $sess_domain);
}
// set session path
if ($sess_path) {
ini_set('session.cookie_path', $sess_path);
}
// set session garbage collecting time according to session_lifetime
if ($lifetime) {
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
// set session cookie lifetime so it never expires (#5961)
ini_set('session.cookie_lifetime', 0);
ini_set('session.cookie_secure', $is_secure);
ini_set('session.name', $sess_name ?: 'roundcube_sessid');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
ini_set('session.cookie_httponly', 1);
// Make sure session garbage collector is enabled when using custom handlers (#6560)
// Note: Use session.gc_divisor to control accuracy
if ($storage != 'php' && !ini_get('session.gc_probability')) {
ini_set('session.gc_probability', 1);
}
// Start the session
$this->session = rcube_session::factory($this->config);
$this->session->register_gc_handler(array($this, 'gc'));
$this->session->start();
}
/**
* Garbage collector - cache/temp cleaner
*/
public function gc()
{
rcube_cache::gc();
$this->get_storage()->cache_gc();
$this->gc_temp();
}
/**
* Garbage collector function for temp files.
* Removes temporary files older than temp_dir_ttl.
*/
public function gc_temp()
{
$tmp = unslashify($this->config->get('temp_dir'));
// expire in 48 hours by default
$temp_dir_ttl = $this->config->get('temp_dir_ttl', '48h');
$temp_dir_ttl = get_offset_sec($temp_dir_ttl);
if ($temp_dir_ttl < 6*3600) {
$temp_dir_ttl = 6*3600; // 6 hours sensible lower bound.
}
$expire = time() - $temp_dir_ttl;
if ($tmp && ($dir = opendir($tmp))) {
while (($fname = readdir($dir)) !== false) {
if (strpos($fname, RCUBE_TEMP_FILE_PREFIX) !== 0) {
continue;
}
if (@filemtime("$tmp/$fname") < $expire) {
@unlink("$tmp/$fname");
}
}
closedir($dir);
}
}
/**
* Runs garbage collector with probability based on
* session settings. This is intended for environments
* without a session.
*/
public function gc_run()
{
$probability = (int) ini_get('session.gc_probability');
$divisor = (int) ini_get('session.gc_divisor');
if ($divisor > 0 && $probability > 0) {
$random = mt_rand(1, $divisor);
if ($random <= $probability) {
$this->gc();
}
}
}
/**
* Get localized text in the desired language
*
* @param mixed $attrib Named parameters array or label name
* @param string $domain Label domain (plugin) name
*
* @return string Localized text
*/
public function gettext($attrib, $domain = null)
{
// load localization files if not done yet
if (empty($this->texts)) {
$this->load_language();
}
// extract attributes
if (is_string($attrib)) {
$attrib = array('name' => $attrib);
}
$name = (string) $attrib['name'];
// attrib contain text values: use them from now
if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) {
$this->texts[$name] = $setval;
}
// check for text with domain
if ($domain && ($text = $this->texts[$domain.'.'.$name])) {
}
// text does not exist
else if (!($text = $this->texts[$name])) {
return "[$name]";
}
// replace vars in text
if (is_array($attrib['vars'])) {
foreach ($attrib['vars'] as $var_key => $var_value) {
$text = str_replace($var_key[0] != '$' ? '$'.$var_key : $var_key, $var_value, $text);
}
}
// replace \n with real line break
$text = strtr($text, array('\n' => "\n"));
// case folding
if (($attrib['uppercase'] && strtolower($attrib['uppercase']) == 'first') || $attrib['ucfirst']) {
$case_mode = MB_CASE_TITLE;
}
else if ($attrib['uppercase']) {
$case_mode = MB_CASE_UPPER;
}
else if ($attrib['lowercase']) {
$case_mode = MB_CASE_LOWER;
}
if (isset($case_mode)) {
$text = mb_convert_case($text, $case_mode);
}
return $text;
}
/**
* Check if the given text label exists
*
- * @param string $name Label name
- * @param string $domain Label domain (plugin) name or '*' for all domains
- * @param string $ref_domain Sets domain name if label is found
+ * @param string $name Label name
+ * @param string $domain Label domain (plugin) name or '*' for all domains
+ * @param string &$ref_domain Sets domain name if label is found
*
* @return boolean True if text exists (either in the current language or in en_US)
*/
public function text_exists($name, $domain = null, &$ref_domain = null)
{
// load localization files if not done yet
if (empty($this->texts)) {
$this->load_language();
}
if (isset($this->texts[$name])) {
$ref_domain = '';
return true;
}
// any of loaded domains (plugins)
if ($domain == '*') {
foreach ($this->plugins->loaded_plugins() as $domain) {
if (isset($this->texts[$domain.'.'.$name])) {
$ref_domain = $domain;
return true;
}
}
}
// specified domain
else if ($domain && isset($this->texts[$domain.'.'.$name])) {
$ref_domain = $domain;
return true;
}
return false;
}
/**
* Load a localization package
*
* @param string $lang Language ID
* @param array $add Additional text labels/messages
* @param array $merge Additional text labels/messages to merge
*/
public function load_language($lang = null, $add = array(), $merge = array())
{
$lang = $this->language_prop($lang ?: $_SESSION['language']);
// load localized texts
if (empty($this->texts) || $lang != $_SESSION['language']) {
$this->texts = array();
// handle empty lines after closing PHP tag in localization files
ob_start();
// get english labels (these should be complete)
@include(RCUBE_LOCALIZATION_DIR . 'en_US/labels.inc');
@include(RCUBE_LOCALIZATION_DIR . 'en_US/messages.inc');
if (is_array($labels))
$this->texts = $labels;
if (is_array($messages))
$this->texts = array_merge($this->texts, $messages);
// include user language files
if ($lang != 'en' && $lang != 'en_US' && is_dir(RCUBE_LOCALIZATION_DIR . $lang)) {
include_once(RCUBE_LOCALIZATION_DIR . $lang . '/labels.inc');
include_once(RCUBE_LOCALIZATION_DIR . $lang . '/messages.inc');
if (is_array($labels))
$this->texts = array_merge($this->texts, $labels);
if (is_array($messages))
$this->texts = array_merge($this->texts, $messages);
}
ob_end_clean();
$_SESSION['language'] = $lang;
}
// append additional texts (from plugin)
if (is_array($add) && !empty($add)) {
$this->texts += $add;
}
// merge additional texts (from plugin)
if (is_array($merge) && !empty($merge)) {
$this->texts = array_merge($this->texts, $merge);
}
}
/**
* Read localized texts from an additional location (plugins, skins).
* Then you can use the result as 2nd arg to load_language().
*
* @param string $dir Directory to search in
*
* @return array Localization texts
*/
public function read_localization($dir)
{
$lang = $_SESSION['language'];
$langs = array_unique(array('en_US', $lang));
$locdir = slashify($dir);
$texts = array();
// Language aliases used to find localization in similar lang, see below
$aliases = array(
'de_CH' => 'de_DE',
'es_AR' => 'es_ES',
'fa_AF' => 'fa_IR',
'nl_BE' => 'nl_NL',
'pt_BR' => 'pt_PT',
'zh_CN' => 'zh_TW',
);
// use buffering to handle empty lines/spaces after closing PHP tag
ob_start();
foreach ($langs as $lng) {
$fpath = $locdir . $lng . '.inc';
if (is_file($fpath) && is_readable($fpath)) {
include $fpath;
$texts = (array) $labels + (array) $messages + (array) $texts;
}
else if ($lng != 'en_US') {
// Find localization in similar language (#1488401)
$alias = null;
if (!empty($aliases[$lng])) {
$alias = $aliases[$lng];
}
else if ($key = array_search($lng, $aliases)) {
$alias = $key;
}
if (!empty($alias)) {
$fpath = $locdir . $alias . '.inc';
if (is_file($fpath) && is_readable($fpath)) {
include $fpath;
$texts = (array) $labels + (array) $messages + (array) $texts;
}
}
}
}
ob_end_clean();
return $texts;
}
/**
* Check the given string and return a valid language code
*
* @param string $lang Language code
*
* @return string Valid language code
*/
protected function language_prop($lang)
{
static $rcube_languages, $rcube_language_aliases;
// user HTTP_ACCEPT_LANGUAGE if no language is specified
if (empty($lang) || $lang == 'auto') {
$accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
$lang = $accept_langs[0];
if (preg_match('/^([a-z]+)[_-]([a-z]+)$/i', $lang, $m)) {
$lang = $m[1] . '_' . strtoupper($m[2]);
}
}
if (empty($rcube_languages)) {
@include(RCUBE_LOCALIZATION_DIR . 'index.inc');
}
// check if we have an alias for that language
if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) {
$lang = $rcube_language_aliases[$lang];
}
// try the first two chars
else if (!isset($rcube_languages[$lang])) {
$short = substr($lang, 0, 2);
// check if we have an alias for the short language code
if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) {
$lang = $rcube_language_aliases[$short];
}
// expand 'nn' to 'nn_NN'
else if (!isset($rcube_languages[$short])) {
$lang = $short.'_'.strtoupper($short);
}
}
if (!isset($rcube_languages[$lang]) || !is_dir(RCUBE_LOCALIZATION_DIR . $lang)) {
$lang = 'en_US';
}
return $lang;
}
/**
* Read directory program/localization and return a list of available languages
*
* @return array List of available localizations
*/
public function list_languages()
{
static $sa_languages = array();
if (!count($sa_languages)) {
@include(RCUBE_LOCALIZATION_DIR . 'index.inc');
if ($dh = @opendir(RCUBE_LOCALIZATION_DIR)) {
while (($name = readdir($dh)) !== false) {
if ($name[0] == '.' || !is_dir(RCUBE_LOCALIZATION_DIR . $name)) {
continue;
}
if ($label = $rcube_languages[$name]) {
$sa_languages[$name] = $label;
}
}
closedir($dh);
}
}
return $sa_languages;
}
/**
* Encrypt a string
*
- * @param string $clear Clear text input
- * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key'
- * @param boolean $base64 Whether or not to base64_encode() the result before returning
+ * @param string $clear Clear text input
+ * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key'
+ * @param bool $base64 Whether or not to base64_encode() the result before returning
*
* @return string Encrypted text
*/
public function encrypt($clear, $key = 'des_key', $base64 = true)
{
if (!is_string($clear) || !strlen($clear)) {
return '';
}
$ckey = $this->config->get_crypto_key($key);
$method = $this->config->get_crypto_method();
$opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
$iv = rcube_utils::random_bytes(openssl_cipher_iv_length($method), true);
$cipher = $iv . openssl_encrypt($clear, $method, $ckey, $opts, $iv);
return $base64 ? base64_encode($cipher) : $cipher;
}
/**
* Decrypt a string
*
- * @param string $cipher Encrypted text
- * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key'
- * @param boolean $base64 Whether or not input is base64-encoded
+ * @param string $cipher Encrypted text
+ * @param string $key Encryption key to retrieve from the configuration, defaults to 'des_key'
+ * @param boo $base64 Whether or not input is base64-encoded
*
* @return string Decrypted text
*/
public function decrypt($cipher, $key = 'des_key', $base64 = true)
{
if (!$cipher) {
return '';
}
$cipher = $base64 ? base64_decode($cipher) : $cipher;
$ckey = $this->config->get_crypto_key($key);
$method = $this->config->get_crypto_method();
$opts = defined('OPENSSL_RAW_DATA') ? OPENSSL_RAW_DATA : true;
$iv_size = openssl_cipher_iv_length($method);
$iv = substr($cipher, 0, $iv_size);
// session corruption? (#1485970)
if (strlen($iv) < $iv_size) {
return '';
}
$cipher = substr($cipher, $iv_size);
$clear = openssl_decrypt($cipher, $method, $ckey, $opts, $iv);
return $clear;
}
/**
* Returns session token for secure URLs
*
* @param bool $generate Generate token if not exists in session yet
*
* @return string|bool Token string, False when disabled
*/
public function get_secure_url_token($generate = false)
{
if ($len = $this->config->get('use_secure_urls')) {
if (empty($_SESSION['secure_token']) && $generate) {
// generate x characters long token
$length = $len > 1 ? $len : 16;
$token = rcube_utils::random_bytes($length);
$plugin = $this->plugins->exec_hook('secure_token',
array('value' => $token, 'length' => $length));
$_SESSION['secure_token'] = $plugin['value'];
}
return $_SESSION['secure_token'];
}
return false;
}
/**
* Generate a unique token to be used in a form request
*
* @return string The request token
*/
public function get_request_token()
{
if (empty($_SESSION['request_token'])) {
$plugin = $this->plugins->exec_hook('request_token', array(
'value' => rcube_utils::random_bytes(32)));
$_SESSION['request_token'] = $plugin['value'];
}
return $_SESSION['request_token'];
}
/**
* Check if the current request contains a valid token.
* Empty requests aren't checked until use_secure_urls is set.
*
* @param int $mode Request method
*
- * @return boolean True if request token is valid false if not
+ * @return bool True if request token is valid false if not
*/
public function check_request($mode = rcube_utils::INPUT_POST)
{
// check secure token in URL if enabled
if ($token = $this->get_secure_url_token()) {
foreach (explode('/', preg_replace('/[?#&].*$/', '', $_SERVER['REQUEST_URI'])) as $tok) {
if ($tok == $token) {
return true;
}
}
$this->request_status = self::REQUEST_ERROR_URL;
return false;
}
$sess_tok = $this->get_request_token();
// ajax requests
if (rcube_utils::request_header('X-Roundcube-Request') === $sess_tok) {
return true;
}
// skip empty requests
if (($mode == rcube_utils::INPUT_POST && empty($_POST))
|| ($mode == rcube_utils::INPUT_GET && empty($_GET))
) {
return true;
}
// default method of securing requests
$token = rcube_utils::get_input_value('_token', $mode);
$sess_id = $_COOKIE[ini_get('session.name')];
if (empty($sess_id) || $token !== $sess_tok) {
$this->request_status = self::REQUEST_ERROR_TOKEN;
return false;
}
return true;
}
/**
* Build a valid URL to this instance of Roundcube
*
* @param mixed $p Either a string with the action or url parameters as key-value pairs
*
* @return string Valid application URL
*/
public function url($p)
{
// STUB: should be overloaded by the application
return '';
}
/**
* Function to be executed in script shutdown
* Registered with register_shutdown_function()
*/
public function shutdown()
{
foreach ($this->shutdown_functions as $function) {
call_user_func($function);
}
// write session data as soon as possible and before
// closing database connection, don't do this before
// registered shutdown functions, they may need the session
// Note: this will run registered gc handlers (ie. cache gc)
if ($_SERVER['REMOTE_ADDR'] && is_object($this->session)) {
$this->session->write_close();
}
if (is_object($this->smtp)) {
$this->smtp->disconnect();
}
foreach ($this->caches as $cache) {
if (is_object($cache)) {
$cache->close();
}
}
if (is_object($this->storage)) {
$this->storage->close();
}
if ($this->config->get('log_driver') == 'syslog') {
closelog();
}
}
/**
* Registers shutdown function to be executed on shutdown.
* The functions will be executed before destroying any
* objects like smtp, imap, session, etc.
*
* @param callback $function Function callback
*/
public function add_shutdown_function($function)
{
$this->shutdown_functions[] = $function;
}
/**
* When you're going to sleep the script execution for a longer time
* it is good to close all external connections (sql, memcache, redis, SMTP, IMAP).
*
* No action is required on wake up, all connections will be
* re-established automatically.
*/
public function sleep()
{
foreach ($this->caches as $cache) {
if (is_object($cache)) {
$cache->close();
}
}
if ($this->storage) {
$this->storage->close();
}
if ($this->db) {
$this->db->closeConnection();
}
if ($this->memcache) {
$this->memcache->close();
}
if ($this->memcached) {
$this->memcached->quit();
}
if ($this->smtp) {
$this->smtp->disconnect();
}
if ($this->redis) {
$this->redis->close();
}
}
/**
* Quote a given string.
* Shortcut function for rcube_utils::rep_specialchars_output()
*
+ * @param string $str A string to quote
+ * @param string $mode Replace mode for tags: show|remove|strict
+ * @param bool $newlines Convert newlines
+ *
* @return string HTML-quoted string
*/
public static function Q($str, $mode = 'strict', $newlines = true)
{
return rcube_utils::rep_specialchars_output($str, 'html', $mode, $newlines);
}
/**
* Quote a given string for javascript output.
* Shortcut function for rcube_utils::rep_specialchars_output()
*
+ * @param string $str A string to quote
+ *
* @return string JS-quoted string
*/
public static function JQ($str)
{
return rcube_utils::rep_specialchars_output($str, 'js');
}
/**
* Construct shell command, execute it and return output as string.
* Keywords {keyword} are replaced with arguments
*
- * @param $cmd Format string with {keywords} to be replaced
- * @param $values (zero, one or more arrays can be passed)
+ * @param string $cmd Format string with {keywords} to be replaced
+ * @param mixed $values,... (zero, one or more arrays can be passed)
*
- * @return output of command. shell errors not detectable
+ * @return string Output of command. Shell errors not detectable
*/
public static function exec(/* $cmd, $values1 = array(), ... */)
{
$args = func_get_args();
$cmd = array_shift($args);
$values = $replacements = array();
// merge values into one array
foreach ($args as $arg) {
$values += (array)$arg;
}
preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER);
foreach ($matches as $tags) {
list(, $tag, $option, $key) = $tags;
$parts = array();
if ($option) {
foreach ((array)$values["-$key"] as $key => $value) {
if ($value === true || $value === false || $value === null) {
$parts[] = $value ? $key : "";
}
else {
foreach ((array)$value as $val) {
$parts[] = "$key " . escapeshellarg($val);
}
}
}
}
else {
foreach ((array)$values[$key] as $value) {
$parts[] = escapeshellarg($value);
}
}
$replacements[$tag] = implode(' ', $parts);
}
// use strtr behaviour of going through source string once
$cmd = strtr($cmd, $replacements);
- return (string)shell_exec($cmd);
+ return (string) shell_exec($cmd);
}
/**
* Print or write debug messages
*
* @param mixed Debug message or data
*/
public static function console()
{
$args = func_get_args();
if (class_exists('rcube', false)) {
$rcube = self::get_instance();
$plugin = $rcube->plugins->exec_hook('console', array('args' => $args));
if ($plugin['abort']) {
return;
}
$args = $plugin['args'];
}
$msg = array();
foreach ($args as $arg) {
$msg[] = !is_string($arg) ? var_export($arg, true) : $arg;
}
self::write_log('console', implode(";\n", $msg));
}
/**
* Append a line to a logfile in the logs directory.
* Date will be added automatically to the line.
*
* @param string $name Name of the log file
* @param mixed $line Line to append
*
* @return bool True on success, False on failure
*/
public static function write_log($name, $line)
{
if (!is_string($line)) {
$line = var_export($line, true);
}
$date_format = $log_driver = $session_key = null;
if (self::$instance) {
$date_format = self::$instance->config->get('log_date_format');
$log_driver = self::$instance->config->get('log_driver');
$session_key = intval(self::$instance->config->get('log_session_id', 8));
}
$date = rcube_utils::date_format($date_format);
// trigger logging hook
if (is_object(self::$instance) && is_object(self::$instance->plugins)) {
$log = self::$instance->plugins->exec_hook('write_log',
array('name' => $name, 'date' => $date, 'line' => $line));
$name = $log['name'];
$line = $log['line'];
$date = $log['date'];
if ($log['abort']) {
return true;
}
}
// add session ID to the log
if ($session_key > 0 && ($sess = session_id())) {
$line = '<' . substr($sess, 0, $session_key) . '> ' . $line;
}
if ($log_driver == 'syslog') {
$prio = $name == 'errors' ? LOG_ERR : LOG_INFO;
return syslog($prio, $line);
}
// write message with file name when configured to log to STDOUT
if ($log_driver == 'stdout') {
$stdout = "php://stdout";
$line = "$name: $line";
return file_put_contents($stdout, $line, FILE_APPEND) !== false;
}
// log_driver == 'file' is assumed here
$line = sprintf("[%s]: %s\n", $date, $line);
// per-user logging is activated
if (self::$instance && self::$instance->config->get('per_user_logging')
&& self::$instance->get_user_id()
&& !in_array($name, array('userlogins', 'sendmail'))
) {
$log_dir = self::$instance->get_user_log_dir();
if (empty($log_dir) && $name !== 'errors') {
return false;
}
}
if (empty($log_dir)) {
if (!empty($log['dir'])) {
$log_dir = $log['dir'];
}
else if (self::$instance) {
$log_dir = self::$instance->config->get('log_dir');
}
}
if (empty($log_dir)) {
$log_dir = RCUBE_INSTALL_PATH . 'logs';
}
if (self::$instance) {
$name .= self::$instance->config->get('log_file_ext', '.log');
}
else {
$name .= '.log';
}
return file_put_contents("$log_dir/$name", $line, FILE_APPEND) !== false;
}
/**
* Throw system error (and show error page).
*
* @param array $arg Named parameters
* - code: Error code (required)
* - type: Error type [php|db|imap|javascript]
* - message: Error message
* - file: File where error occurred
* - line: Line where error occurred
- * @param boolean $log True to log the error
- * @param boolean $terminate Terminate script execution
+ * @param bool $log True to log the error
+ * @param bool $terminate Terminate script execution
*/
public static function raise_error($arg = array(), $log = false, $terminate = false)
{
// handle PHP exceptions
if ($arg instanceof Exception) {
$arg = array(
'code' => $arg->getCode(),
'line' => $arg->getLine(),
'file' => $arg->getFile(),
'message' => $arg->getMessage(),
);
}
else if ($arg instanceof PEAR_Error) {
$info = $arg->getUserInfo();
$arg = array(
'code' => $arg->getCode(),
'message' => $arg->getMessage() . ($info ? ': ' . $info : ''),
);
}
else if (is_string($arg)) {
$arg = array('message' => $arg);
}
if (empty($arg['code'])) {
$arg['code'] = 500;
}
$cli = php_sapi_name() == 'cli';
$arg['cli'] = $cli;
$arg['log'] = $log;
$arg['terminate'] = $terminate;
// send error to external error tracking tool
$arg = self::$instance->plugins->exec_hook('raise_error', $arg);
// installer
if (!$cli && class_exists('rcmail_install', false)) {
$rci = rcmail_install::get_instance();
$rci->raise_error($arg);
return;
}
if (($log || $terminate) && !$cli && $arg['message']) {
$arg['fatal'] = $terminate;
self::log_bug($arg);
}
if ($cli) {
fwrite(STDERR, 'ERROR: ' . trim($arg['message']) . "\n");
}
else if ($terminate && is_object(self::$instance->output)) {
self::$instance->output->raise_error($arg['code'], $arg['message']);
}
// terminate script
if ($terminate) {
exit(1);
}
}
/**
* Log an error
*
* @param array $arg_arr Named parameters
* @see self::raise_error()
*/
public static function log_bug($arg_arr)
{
$program = strtoupper($arg_arr['type'] ?: 'php');
// write error to local log file
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
foreach (array('_task', '_action') as $arg) {
if ($_POST[$arg] && !$_GET[$arg]) {
$post_query[$arg] = $_POST[$arg];
}
}
if (!empty($post_query)) {
$post_query = (strpos($_SERVER['REQUEST_URI'], '?') != false ? '&' : '?')
. http_build_query($post_query, '', '&');
}
}
$log_entry = sprintf("%s Error: %s%s (%s %s)",
$program,
$arg_arr['message'],
$arg_arr['file'] ? sprintf(' in %s on line %d', $arg_arr['file'], $arg_arr['line']) : '',
$_SERVER['REQUEST_METHOD'],
$_SERVER['REQUEST_URI'] . $post_query);
if (!self::write_log('errors', $log_entry)) {
// send error to PHPs error handler if write_log didn't succeed
trigger_error($arg_arr['message'], E_USER_WARNING);
}
}
/**
* Write debug info to the log
*
* @param string $engine Engine type - file name (memcache, apc, redis)
* @param string $data Data string to log
* @param bool $result Operation result
*/
public static function debug($engine, $data, $result = null)
{
static $debug_counter;
$line = '[' . (++$debug_counter[$engine]) . '] ' . $data;
if (($len = strlen($line)) > self::DEBUG_LINE_LENGTH) {
$diff = $len - self::DEBUG_LINE_LENGTH;
$line = substr($line, 0, self::DEBUG_LINE_LENGTH) . "... [truncated $diff bytes]";
}
if ($result !== null) {
$line .= ' [' . ($result ? 'TRUE' : 'FALSE') . ']';
}
self::write_log($engine, $line);
}
/**
* Returns current time (with microseconds).
*
* @return float Current time in seconds since the Unix
*/
public static function timer()
{
return microtime(true);
}
/**
* Logs time difference according to provided timer
*
* @param float $timer Timer (self::timer() result)
* @param string $label Log line prefix
* @param string $dest Log file name
*
* @see self::timer()
*/
public static function print_timer($timer, $label = 'Timer', $dest = 'console')
{
static $print_count = 0;
$print_count++;
$now = self::timer();
$diff = $now - $timer;
if (empty($label)) {
$label = 'Timer '.$print_count;
}
self::write_log($dest, sprintf("%s: %0.4f sec", $label, $diff));
}
/**
* Setter for system user object
*
* @param rcube_user Current user instance
*/
public function set_user($user)
{
if (is_object($user)) {
$this->user = $user;
// overwrite config with user preferences
$this->config->set_user_prefs((array)$this->user->get_prefs());
}
}
/**
* Getter for logged user ID.
*
* @return mixed User identifier
*/
public function get_user_id()
{
if (is_object($this->user)) {
return $this->user->ID;
}
else if (isset($_SESSION['user_id'])) {
return $_SESSION['user_id'];
}
}
/**
* Getter for logged user name.
*
* @return string User name
*/
public function get_user_name()
{
if (is_object($this->user)) {
return $this->user->get_username();
}
else if (isset($_SESSION['username'])) {
return $_SESSION['username'];
}
}
/**
* Getter for logged user email (derived from user name not identity).
*
* @return string User email address
*/
public function get_user_email()
{
if (is_object($this->user)) {
return $this->user->get_username('mail');
}
}
/**
* Getter for logged user password.
*
* @return string User password
*/
public function get_user_password()
{
if ($this->password) {
return $this->password;
}
else if ($_SESSION['password']) {
return $this->decrypt($_SESSION['password']);
}
}
/**
* Get the per-user log directory
+ *
+ * @return string|false Per-user log directory if it exists and is writable, False otherwise
*/
protected function get_user_log_dir()
{
$log_dir = $this->config->get('log_dir', RCUBE_INSTALL_PATH . 'logs');
$user_name = $this->get_user_name();
$user_log_dir = $log_dir . '/' . $user_name;
return !empty($user_name) && is_writable($user_log_dir) ? $user_log_dir : false;
}
/**
* Getter for logged user language code.
*
* @return string User language code
*/
public function get_user_language()
{
if (is_object($this->user)) {
return $this->user->language;
}
else if (isset($_SESSION['language'])) {
return $_SESSION['language'];
}
}
/**
* Unique Message-ID generator.
*
* @param string $sender Optional sender e-mail address
*
* @return string Message-ID
*/
public function gen_message_id($sender = null)
{
$local_part = md5(uniqid('rcube'.mt_rand(), true));
$domain_part = '';
if ($sender && preg_match('/@([^\s]+\.[a-z0-9-]+)/', $sender, $m)) {
$domain_part = $m[1];
}
else {
$domain_part = $this->user->get_username('domain');
}
// Try to find FQDN, some spamfilters doesn't like 'localhost' (#1486924)
if (!preg_match('/\.[a-z0-9-]+$/i', $domain_part)) {
foreach (array($_SERVER['HTTP_HOST'], $_SERVER['SERVER_NAME']) as $host) {
$host = preg_replace('/:[0-9]+$/', '', $host);
if ($host && preg_match('/\.[a-z]+$/i', $host)) {
$domain_part = $host;
break;
}
}
}
return sprintf('<%s@%s>', $local_part, $domain_part);
}
/**
* Send the given message using the configured method.
*
- * @param object $message Reference to Mail_MIME object
- * @param string $from Sender address string
- * @param array|string $mailto Either a comma-separated list of recipients (RFC822 compliant),
- * or an array of recipients, each RFC822 valid
- * @param array $error SMTP error array (reference)
- * @param string $body_file Location of file with saved message body (reference),
- * used when delay_file_io is enabled
- * @param array $options SMTP options (e.g. DSN request)
- * @param bool $disconnect Close SMTP connection ASAP
- *
- * @return boolean Send status.
+ * @param Mail_Mime &$message Reference to Mail_MIME object
+ * @param string $from Sender address string
+ * @param array|string $mailto Either a comma-separated list of recipients (RFC822 compliant),
+ * or an array of recipients, each RFC822 valid
+ * @param array &$error SMTP error array (reference)
+ * @param string &$body_file Location of file with saved message body (reference),
+ * used when delay_file_io is enabled
+ * @param array $options SMTP options (e.g. DSN request)
+ * @param bool $disconnect Close SMTP connection ASAP
+ *
+ * @return bool Send status.
*/
public function deliver_message(&$message, $from, $mailto, &$error,
&$body_file = null, $options = null, $disconnect = false)
{
$plugin = $this->plugins->exec_hook('message_before_send', array(
'message' => $message,
'from' => $from,
'mailto' => $mailto,
'options' => $options,
));
if ($plugin['abort']) {
if (!empty($plugin['error'])) {
$error = $plugin['error'];
}
if (!empty($plugin['body_file'])) {
$body_file = $plugin['body_file'];
}
return isset($plugin['result']) ? $plugin['result'] : false;
}
$from = $plugin['from'];
$mailto = $plugin['mailto'];
$options = $plugin['options'];
$message = $plugin['message'];
$headers = $message->headers();
// generate list of recipients
$a_recipients = (array) $mailto;
if (strlen($headers['Cc'])) {
$a_recipients[] = $headers['Cc'];
}
if (strlen($headers['Bcc'])) {
$a_recipients[] = $headers['Bcc'];
}
// remove Bcc header and get the whole head of the message as string
$smtp_headers = $message->txtHeaders(array('Bcc' => null), true);
if ($message->getParam('delay_file_io')) {
// use common temp dir
$body_file = rcube_utils::temp_filename('msg');
$mime_result = $message->saveMessageBody($body_file);
if (is_a($mime_result, 'PEAR_Error')) {
self::raise_error(array('code' => 650, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not create message: ".$mime_result->getMessage()),
true, false);
return false;
}
$msg_body = fopen($body_file, 'r');
}
else {
$msg_body = $message->get();
}
// initialize SMTP connection
if (!is_object($this->smtp)) {
$this->smtp_init(true);
}
// send message
$sent = $this->smtp->send_mail($from, $a_recipients, $smtp_headers, $msg_body, $options);
$response = $this->smtp->get_response();
$error = $this->smtp->get_error();
if (!$sent) {
self::raise_error(array('code' => 800, 'type' => 'smtp',
'line' => __LINE__, 'file' => __FILE__,
'message' => implode("\n", $response)), true, false);
// allow plugins to catch sending errors with the same parameters as in 'message_before_send'
$this->plugins->exec_hook('message_send_error', $plugin + array('error' => $error));
}
else {
$this->plugins->exec_hook('message_sent', array('headers' => $headers, 'body' => $msg_body, 'message' => $message));
// remove MDN headers after sending
unset($headers['Return-Receipt-To'], $headers['Disposition-Notification-To']);
if ($this->config->get('smtp_log')) {
// get all recipient addresses
$mailto = implode(',', $a_recipients);
$mailto = rcube_mime::decode_address_list($mailto, null, false, null, true);
self::write_log('sendmail', sprintf("User %s [%s]; Message %s for %s; %s",
$this->user->get_username(),
rcube_utils::remote_addr(),
$headers['Message-ID'],
implode(', ', $mailto),
!empty($response) ? implode('; ', $response) : ''));
}
}
if (is_resource($msg_body)) {
fclose($msg_body);
}
if ($disconnect) {
$this->smtp->disconnect();
}
// Add Bcc header back
if (!empty($headers['Bcc'])) {
$message->headers(array('Bcc' => $headers['Bcc']), true);
}
return $sent;
}
}
/**
* Lightweight plugin API class serving as a dummy if plugins are not enabled
*
* @package Framework
* @subpackage Core
*/
class rcube_dummy_plugin_api
{
/**
* Triggers a plugin hook.
+ *
+ * @param string $hook Hook name
+ * @param array $args Hook arguments
+ *
+ * @return array Hook arguments
* @see rcube_plugin_api::exec_hook()
*/
public function exec_hook($hook, $args = array())
{
return $args;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 8, 11:44 PM (1 d, 6 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1052271
Default Alt Text
(100 KB)

Event Timeline