Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2513204
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
255 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/program/lib/Roundcube/cache/apc.php b/program/lib/Roundcube/cache/apc.php
index 88f106136..7335009a6 100644
--- a/program/lib/Roundcube/cache/apc.php
+++ b/program/lib/Roundcube/cache/apc.php
@@ -1,140 +1,140 @@
<?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: |
| Caching engine - APC |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing APC cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache_apc extends rcube_cache
{
/**
* Indicates if APC module is enabled and in a required version
*
* @var bool
*/
protected $enabled;
/**
* Object constructor.
*
* @see rcube_cache::__construct()
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
parent::__construct($userid, $prefix, $ttl, $packed, $indexed);
$rcube = rcube::get_instance();
$this->type = 'apc';
$this->enabled = function_exists('apc_exists'); // APC 3.1.4 required
$this->debug = $rcube->config->get('apc_debug');
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
// No need for GC, entries are expunged automatically
}
/**
* Remove expired records of all caches
*/
public static function gc()
{
// No need for GC, entries are expunged automatically
}
/**
* Reads cache entry.
*
* @param string $key Cache internal key name
*
* @return mixed Cached value
*/
protected function get_item($key)
{
if (!$this->enabled) {
return false;
}
$data = apc_fetch($key);
if ($this->debug) {
$this->debug('get', $key, $data);
}
return $data;
}
/**
* Adds entry into memcache/apc/redis DB.
*
* @param string $key Cache internal key name
* @param mixed $data Serialized cache data
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function add_item($key, $data)
{
if (!$this->enabled) {
return false;
}
if (apc_exists($key)) {
apc_delete($key);
}
$result = apc_store($key, $data, $this->ttl);
if ($this->debug) {
$this->debug('set', $key, $data, $result);
}
return $result;
}
/**
* Deletes entry from memcache/apc/redis DB.
*
* @param string $key Cache internal key name
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function delete_item($key)
{
if (!$this->enabled) {
return false;
}
$result = apc_delete($key);
if ($this->debug) {
$this->debug('delete', $key, null, $result);
}
return $result;
}
}
diff --git a/program/lib/Roundcube/cache/db.php b/program/lib/Roundcube/cache/db.php
index fff6fd204..4b908cc5c 100644
--- a/program/lib/Roundcube/cache/db.php
+++ b/program/lib/Roundcube/cache/db.php
@@ -1,242 +1,242 @@
<?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: |
| Caching engine - SQL DB |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing SQL Database cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache_db extends rcube_cache
{
/**
* Instance of database handler
*
* @var rcube_db
*/
protected $db;
/**
* (Escaped) Cache table name (cache or cache_shared)
*
* @var string
*/
protected $table;
- protected $existing = array();
+ protected $existing = [];
/**
* Object constructor.
*
* @see rcube_cache::__construct()
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
parent::__construct($userid, $prefix, $ttl, $packed, $indexed);
$rcube = rcube::get_instance();
$this->type = 'db';
$this->db = $rcube->get_dbh();
$this->table = $this->db->table_name($userid ? 'cache' : 'cache_shared', true);
$this->refresh_time *= 2;
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
if ($this->ttl) {
$this->db->query(
"DELETE FROM {$this->table} WHERE "
. ($this->userid ? "`user_id` = {$this->userid} AND " : "")
. "`cache_key` LIKE ?"
. " AND `expires` < " . $this->db->now(),
$this->prefix . '.%');
}
}
/**
* Remove expired records of all caches
*/
public static function gc()
{
$rcube = rcube::get_instance();
$db = $rcube->get_dbh();
$db->query("DELETE FROM " . $db->table_name('cache', true) . " WHERE `expires` < " . $db->now());
$db->query("DELETE FROM " . $db->table_name('cache_shared', true) . " WHERE `expires` < " . $db->now());
}
/**
* Reads cache entry.
*
* @param string $key Cache key name
*
* @return mixed Cached value
*/
protected function read_record($key)
{
$sql_result = $this->db->query(
"SELECT `data`, `cache_key` FROM {$this->table} WHERE "
. ($this->userid ? "`user_id` = {$this->userid} AND " : "")
."`cache_key` = ?",
$this->prefix . '.' . $key);
$data = null;
if ($sql_arr = $this->db->fetch_assoc($sql_result)) {
if (strlen($sql_arr['data']) > 0) {
$data = $this->unserialize($sql_arr['data']);
}
$this->db->reset();
$this->existing[] = $key;
}
if (!$this->indexed) {
$this->cache[$key] = $data;
}
return $data;
}
/**
* Writes single cache record into DB.
*
* @param string $key Cache key name
* @param mixed $data Serialized cache data
* @param DateTime $ts Timestamp
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function store_record($key, $data, $ts = null)
{
$value = $this->serialize($data);
$size = strlen($value);
// don't attempt to write too big data sets
if ($size > $this->max_packet_size()) {
trigger_error("rcube_cache: max_packet_size ($this->max_packet) exceeded for key $key. Tried to write $size bytes", E_USER_WARNING);
return false;
}
$db_key = $this->prefix . '.' . $key;
// Remove NULL rows (here we don't need to check if the record exist)
if ($value == 'N;') {
$result = $this->db->query(
"DELETE FROM {$this->table} WHERE "
. ($this->userid ? "`user_id` = {$this->userid} AND " : "")
."`cache_key` = ?",
$db_key);
return !$this->db->is_error($result);
}
$expires = $this->db->param($this->ttl ? $this->db->now($this->ttl) : 'NULL', rcube_db::TYPE_SQL);
- $pkey = array('cache_key' => $db_key);
+ $pkey = ['cache_key' => $db_key];
if ($this->userid) {
$pkey['user_id'] = $this->userid;
}
$result = $this->db->insert_or_update(
- $this->table, $pkey, array('expires', 'data'), array($expires, $value)
+ $this->table, $pkey, ['expires', 'data'], [$expires, $value]
);
$count = $this->db->affected_rows($result);
return $count > 0;
}
/**
* Deletes the cache record(s).
*
- * @param string $key Cache key name or pattern
- * @param boolean $prefix_mode Enable it to clear all keys starting
- * with prefix specified in $key
+ * @param string $key Cache key name or pattern
+ * @param bool $prefix_mode Enable it to clear all keys starting
+ * with prefix specified in $key
*/
protected function remove_record($key = null, $prefix_mode = false)
{
// Remove all keys (in specified cache)
if ($key === null) {
$where = "`cache_key` LIKE " . $this->db->quote($this->prefix . '.%');
- $this->cache = array();
+ $this->cache = [];
}
// Remove keys by name prefix
else if ($prefix_mode) {
$where = "`cache_key` LIKE " . $this->db->quote($this->prefix . '.' . $key . '%');
foreach (array_keys($this->cache) as $k) {
if (strpos($k, $key) === 0) {
$this->cache[$k] = null;
}
}
}
// Remove one key by name
else {
$where = "`cache_key` = " . $this->db->quote($this->prefix . '.' . $key);
$this->cache[$key] = null;
}
$this->db->query(
"DELETE FROM {$this->table} WHERE "
. ($this->userid ? "`user_id` = {$this->userid} AND " : "") . $where
);
}
/**
* Serializes data for storing
*/
protected function serialize($data)
{
return $this->db->encode($data, $this->packed);
}
/**
* Unserializes serialized data
*/
protected function unserialize($data)
{
return $this->db->decode($data, $this->packed);
}
/**
* Determine the maximum size for cache data to be written
*/
protected function max_packet_size()
{
if ($this->max_packet < 0) {
$this->max_packet = 2097152; // default/max is 2 MB
if ($value = $this->db->get_variable('max_allowed_packet', $this->max_packet)) {
$this->max_packet = $value;
}
$this->max_packet -= 2000;
}
return $this->max_packet;
}
}
diff --git a/program/lib/Roundcube/cache/memcache.php b/program/lib/Roundcube/cache/memcache.php
index 374d7ed0c..645933dc6 100644
--- a/program/lib/Roundcube/cache/memcache.php
+++ b/program/lib/Roundcube/cache/memcache.php
@@ -1,211 +1,212 @@
<?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: |
| Caching engine - Memcache |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing Memcache cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache_memcache extends rcube_cache
{
/**
* Instance of memcache handler
*
* @var Memcache
*/
protected static $memcache;
/**
* Object constructor.
*
* @see rcube_cache::__construct()
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
parent::__construct($userid, $prefix, $ttl, $packed, $indexed);
$this->type = 'memcache';
$this->debug = rcube::get_instance()->config->get('memcache_debug');
self::engine();
}
/**
* Get global handle for memcache access
*
* @return object Memcache
*/
public static function engine()
{
if (self::$memcache !== null) {
return self::$memcache;
}
// no memcache support in PHP
if (!class_exists('Memcache')) {
self::$memcache = false;
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604,
'type' => 'memcache',
'line' => __LINE__,
'file' => __FILE__,
'message' => "Failed to find Memcache. Make sure php-memcache is included"
- ),
+ ],
true, true);
}
// add all configured hosts to pool
$rcube = rcube::get_instance();
$pconnect = $rcube->config->get('memcache_pconnect', true);
$timeout = $rcube->config->get('memcache_timeout', 1);
$retry_interval = $rcube->config->get('memcache_retry_interval', 15);
- $seen = array();
+ $seen = [];
$available = 0;
// Callback for memcache failure
$error_callback = function($host, $port) use ($seen, $available) {
// only report once
if (!$seen["$host:$port"]++) {
$available--;
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604, 'type' => 'memcache',
'line' => __LINE__, 'file' => __FILE__,
- 'message' => "Memcache failure on host $host:$port"),
+ 'message' => "Memcache failure on host $host:$port"
+ ],
true, false);
}
};
self::$memcache = new Memcache;
foreach ((array) $rcube->config->get('memcache_hosts') as $host) {
if (substr($host, 0, 7) != 'unix://') {
list($host, $port) = explode(':', $host);
if (!$port) $port = 11211;
}
else {
$port = 0;
}
$available += intval(self::$memcache->addServer(
$host, $port, $pconnect, 1, $timeout, $retry_interval, false, $error_callback));
}
// test connection and failover (will result in $available == 0 on complete failure)
self::$memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist
if (!$available) {
self::$memcache = false;
}
return self::$memcache;
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
// No need for GC, entries are expunged automatically
}
/**
* Remove expired records of all caches
*/
public static function gc()
{
// No need for GC, entries are expunged automatically
}
/**
* Reads cache entry.
*
* @param string $key Cache internal key name
*
* @return mixed Cached value
*/
protected function get_item($key)
{
if (!self::$memcache) {
return false;
}
$data = self::$memcache->get($key);
if ($this->debug) {
$this->debug('get', $key, $data);
}
return $data;
}
/**
* Adds entry into the cache.
*
* @param string $key Cache internal key name
* @param mixed $data Serialized cache data
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function add_item($key, $data)
{
if (!self::$memcache) {
return false;
}
$result = self::$memcache->replace($key, $data, MEMCACHE_COMPRESSED, $this->ttl);
if (!$result) {
$result = self::$memcache->set($key, $data, MEMCACHE_COMPRESSED, $this->ttl);
}
if ($this->debug) {
$this->debug('set', $key, $data, $result);
}
return $result;
}
/**
* Deletes entry from the cache
*
* @param string $key Cache internal key name
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function delete_item($key)
{
if (!self::$memcache) {
return false;
}
// #1488592: use 2nd argument
$result = self::$memcache->delete($key, 0);
if ($this->debug) {
$this->debug('delete', $key, null, $result);
}
return $result;
}
}
diff --git a/program/lib/Roundcube/cache/memcached.php b/program/lib/Roundcube/cache/memcached.php
index 036e5ab7c..7be99b73b 100644
--- a/program/lib/Roundcube/cache/memcached.php
+++ b/program/lib/Roundcube/cache/memcached.php
@@ -1,212 +1,212 @@
<?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: |
| Caching engine - Memcache |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing Memcached cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache_memcached extends rcube_cache
{
/**
* Instance of memcached handler
*
* @var Memcached
*/
protected static $memcache;
/**
* Object constructor.
*
* @see rcube_cache::__construct()
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
parent::__construct($userid, $prefix, $ttl, $packed, $indexed);
$this->type = 'memcache';
$this->debug = rcube::get_instance()->config->get('memcache_debug');
// Maximum TTL is 30 days, bigger values are treated by Memcached
// as unix timestamp which is not what we want
if ($this->ttl > 60*60*24*30) {
$this->ttl = 60*60*24*30;
}
self::engine();
}
/**
* Get global handle for memcache access
*
* @return object Memcache
*/
public static function engine()
{
if (self::$memcache !== null) {
return self::$memcache;
}
// no memcache support in PHP
if (!class_exists('Memcached')) {
self::$memcache = false;
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604, 'type' => 'memcache', 'line' => __LINE__, 'file' => __FILE__,
'message' => "Failed to find Memcached. Make sure php-memcached is installed"
- ),
+ ],
true, true);
}
// add all configured hosts to pool
$rcube = rcube::get_instance();
$pconnect = $rcube->config->get('memcache_pconnect', true);
$timeout = $rcube->config->get('memcache_timeout', 1);
$retry_interval = $rcube->config->get('memcache_retry_interval', 15);
$hosts = $rcube->config->get('memcache_hosts');
$persistent_id = $pconnect ? ('rc' . md5(serialize($hosts))) : null;
self::$memcache = new Memcached($persistent_id);
- self::$memcache->setOptions(array(
+ self::$memcache->setOptions([
Memcached::OPT_CONNECT_TIMEOUT => $timeout * 1000,
Memcached::OPT_RETRY_TIMEOUT => $timeout,
Memcached::OPT_DISTRIBUTION => Memcached::DISTRIBUTION_CONSISTENT,
Memcached::OPT_COMPRESSION => true,
- ));
+ ]);
if (!$pconnect || !count(self::$memcache->getServerList())) {
foreach ((array) $hosts as $host) {
if (substr($host, 0, 7) != 'unix://') {
list($host, $port) = explode(':', $host);
if (!$port) $port = 11211;
}
else {
$host = substr($host, 7);
$port = 0;
}
self::$memcache->addServer($host, $port);
}
}
// test connection
$result = self::$memcache->increment('__CONNECTIONTEST__');
if ($result === false && ($res_code = self::$memcache->getResultCode()) !== Memcached::RES_NOTFOUND) {
self::$memcache = false;
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604, 'type' => 'memcache', 'line' => __LINE__, 'file' => __FILE__,
'message' => "Memcache connection failure (code: $res_code)."
- ),
+ ],
true, false);
}
return self::$memcache;
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
// No need for GC, entries are expunged automatically
}
/**
* Remove expired records of all caches
*/
public static function gc()
{
// No need for GC, entries are expunged automatically
}
/**
* Reads cache entry.
*
* @param string $key Cache internal key name
*
* @return mixed Cached value
*/
protected function get_item($key)
{
if (!self::$memcache) {
return false;
}
$data = self::$memcache->get($key);
if ($this->debug) {
$this->debug('get', $key, $data);
}
return $data;
}
/**
* Adds entry into the cache.
*
* @param string $key Cache internal key name
* @param mixed $data Serialized cache data
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function add_item($key, $data)
{
if (!self::$memcache) {
return false;
}
$result = self::$memcache->set($key, $data, $this->ttl);
if ($this->debug) {
$this->debug('set', $key, $data, $result);
}
return $result;
}
/**
* Deletes entry from the cache
*
* @param string $key Cache internal key name
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function delete_item($key)
{
if (!self::$memcache) {
return false;
}
// #1488592: use 2nd argument
$result = self::$memcache->delete($key, 0);
if ($this->debug) {
$this->debug('delete', $key, null, $result);
}
return $result;
}
}
diff --git a/program/lib/Roundcube/cache/redis.php b/program/lib/Roundcube/cache/redis.php
index b89a90842..8a27ba9d3 100644
--- a/program/lib/Roundcube/cache/redis.php
+++ b/program/lib/Roundcube/cache/redis.php
@@ -1,263 +1,263 @@
<?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: |
| Caching engine - Redis |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing Redis cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache_redis extends rcube_cache
{
/**
* Instance of Redis object
*
* @var Redis
*/
protected static $redis;
/**
* Object constructor.
*
* @see rcube_cache::__construct()
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
parent::__construct($userid, $prefix, $ttl, $packed, $indexed);
$rcube = rcube::get_instance();
$this->type = 'redis';
$this->debug = $rcube->config->get('redis_debug');
self::engine();
}
/**
* Get global handle for redis access
*
* @return object Redis
*/
public static function engine()
{
if (self::$redis !== null) {
return self::$redis;
}
if (!class_exists('Redis')) {
self::$redis = false;
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604,
'type' => 'redis',
'line' => __LINE__,
'file' => __FILE__,
'message' => "Failed to find Redis. Make sure php-redis is included"
- ),
+ ],
true, true);
}
$rcube = rcube::get_instance();
$hosts = $rcube->config->get('redis_hosts');
// host config is wrong
if (!is_array($hosts) || empty($hosts)) {
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604,
'type' => 'redis',
'line' => __LINE__,
'file' => __FILE__,
'message' => "Redis host not configured"
- ),
+ ],
true, true);
}
// only allow 1 host for now until we support clustering
if (count($hosts) > 1) {
- rcube::raise_error(array(
+ rcube::raise_error([
'code' => 604,
'type' => 'redis',
'line' => __LINE__,
'file' => __FILE__,
'message' => "Redis cluster not yet supported"
- ),
+ ],
true, true);
}
self::$redis = new Redis;
$failures = 0;
foreach ($hosts as $redis_host) {
// explode individual fields
list($host, $port, $database, $password) = array_pad(explode(':', $redis_host, 4), 4, null);
if (substr($redis_host, 0, 7) === 'unix://') {
$host = substr($port, 2);
$port = null;
}
else {
// set default values if not set
$host = $host ?: '127.0.0.1';
$port = $port ?: 6379;
}
try {
if (self::$redis->connect($host, $port) === false) {
throw new Exception("Could not connect to Redis server. Please check host and port.");
}
if ($password !== null && self::$redis->auth($password) === false) {
throw new Exception("Could not authenticate with Redis server. Please check password.");
}
if ($database !== null && self::$redis->select($database) === false) {
throw new Exception("Could not select Redis database. Please check database setting.");
}
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
$failures++;
}
}
if (count($hosts) === $failures) {
self::$redis = false;
}
if (self::$redis) {
try {
$ping = self::$redis->ping();
if ($ping !== true && $ping !== "+PONG") {
throw new Exception("Redis connection failure. Ping failed.");
}
}
catch (Exception $e) {
self::$redis = false;
rcube::raise_error($e, true, false);
}
}
return self::$redis;
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
// No need for GC, entries are expunged automatically
}
/**
* Remove expired records
*/
public static function gc()
{
// No need for GC, entries are expunged automatically
}
/**
* Reads cache entry.
*
* @param string $key Cache internal key name
*
* @return mixed Cached value
*/
protected function get_item($key)
{
if (!self::$redis) {
return false;
}
try {
$data = self::$redis->get($key);
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return false;
}
if ($this->debug) {
$this->debug('get', $key, $data);
}
return $data;
}
/**
* Adds entry into Redis.
*
* @param string $key Cache internal key name
* @param mixed $data Serialized cache data
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function add_item($key, $data)
{
if (!self::$redis) {
return false;
}
try {
$result = self::$redis->setEx($key, $this->ttl, $data);
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return false;
}
if ($this->debug) {
$this->debug('set', $key, $data, $result);
}
return $result;
}
/**
* Deletes entry from Redis.
*
* @param string $key Cache internal key name
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function delete_item($key)
{
if (!self::$redis) {
return false;
}
try {
$fname = method_exists(self::$redis, 'del') ? 'del' : 'delete';
$result = self::$redis->$fname($key);
}
catch (Exception $e) {
rcube::raise_error($e, true, false);
return false;
}
if ($this->debug) {
$this->debug('delete', $key, null, $result);
}
return $result;
}
}
diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php
index 88b24c313..d50f578cc 100644
--- a/program/lib/Roundcube/rcube_addressbook.php
+++ b/program/lib/Roundcube/rcube_addressbook.php
@@ -1,747 +1,741 @@
<?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: |
| Interface to the local address book database |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Abstract skeleton of an address book/repository
*
* @package Framework
* @subpackage Addressbook
*/
abstract class rcube_addressbook
{
// constants for error reporting
const ERROR_READ_ONLY = 1;
const ERROR_NO_CONNECTION = 2;
const ERROR_VALIDATE = 3;
const ERROR_SAVING = 4;
const ERROR_SEARCH = 5;
// search modes
const SEARCH_ALL = 0;
const SEARCH_STRICT = 1;
const SEARCH_PREFIX = 2;
const SEARCH_GROUPS = 4;
// contact types, note: some of these are used as addressbook source identifiers
const TYPE_CONTACT = 0;
const TYPE_RECIPIENT = 1;
const TYPE_TRUSTED_SENDER = 2;
const TYPE_DEFAULT = 4;
const TYPE_WRITEABLE = 8;
const TYPE_READONLY = 16;
// public properties (mandatory)
public $primary_key;
public $groups = false;
public $export_groups = true;
public $readonly = true;
public $searchonly = false;
public $undelete = false;
public $ready = false;
public $group_id = null;
public $list_page = 1;
public $page_size = 10;
public $sort_col = 'name';
public $sort_order = 'ASC';
- public $date_cols = array();
- public $coltypes = array(
- 'name' => array('limit'=>1),
- 'firstname' => array('limit'=>1),
- 'surname' => array('limit'=>1),
- 'email' => array('limit'=>1)
- );
+ public $date_cols = [];
+ public $coltypes = [
+ 'name' => ['limit' => 1],
+ 'firstname' => ['limit' => 1],
+ 'surname' => ['limit' => 1],
+ 'email' => ['limit' => 1]
+ ];
/**
* vCard additional fields mapping
*/
public $vcard_map = [];
protected $error;
/**
* Returns addressbook name (e.g. for addressbooks listing)
*/
abstract function get_name();
/**
* Save a search string for future listings
*
* @param mixed $filter Search params to use in listing method, obtained by get_search_set()
*/
abstract function set_search_set($filter);
/**
* Getter for saved search properties
*
* @return mixed Search properties used by this class
*/
abstract function get_search_set();
/**
* Reset saved results and search parameters
*/
abstract function reset();
/**
* Refresh saved search set after data has changed
*
* @return mixed New search set
*/
function refresh_search()
{
return $this->get_search_set();
}
/**
* List the current set of contact records
*
* @param array $cols List of cols to show
* @param int $subset Only return this number of records, use negative values for tail
*
* @return rcube_result_set Indexed list of contact records, each a hash array
*/
- abstract function list_records($cols=null, $subset=0);
+ abstract function list_records($cols = null, $subset = 0);
/**
* Search records
*
- * @param array $fields List of fields to search in
- * @param string $value Search value
- * @param int $mode Search mode. Sum of self::SEARCH_*.
- * @param boolean $select True if results are requested, False if count only
- * @param boolean $nocount True to skip the count query (select only)
- * @param array $required List of fields that cannot be empty
+ * @param array $fields List of fields to search in
+ * @param string $value Search value
+ * @param int $mode Search mode. Sum of self::SEARCH_*.
+ * @param bool $select True if results are requested, False if count only
+ * @param bool $nocount True to skip the count query (select only)
+ * @param array $required List of fields that cannot be empty
*
* @return object rcube_result_set List of contact records and 'count' value
*/
- abstract function search($fields, $value, $mode=0, $select=true, $nocount=false, $required=array());
+ abstract function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = []);
/**
* Count number of available contacts in database
*
* @return rcube_result_set Result set with values for 'count' and 'first'
*/
abstract function count();
/**
* Return the last result set
*
* @return ?rcube_result_set Current result set or NULL if nothing selected yet
*/
abstract function get_result();
/**
* Get a specific contact record
*
- * @param mixed $id Record identifier(s)
- * @param boolean $assoc True to return record as associative array, otherwise a result set is returned
+ * @param mixed $id Record identifier(s)
+ * @param bool $assoc True to return record as associative array, otherwise a result set is returned
*
* @return rcube_result_set|array Result object with all record fields
*/
- abstract function get_record($id, $assoc=false);
+ abstract function get_record($id, $assoc = false);
/**
* Returns the last error occurred (e.g. when updating/inserting failed)
*
* @return array Hash array with the following fields: type, message
*/
function get_error()
{
return $this->error;
}
/**
* Setter for errors for internal use
*
* @param int $type Error type (one of this class' error constants)
* @param string $message Error message (name of a text label)
*/
protected function set_error($type, $message)
{
- $this->error = array('type' => $type, 'message' => $message);
+ $this->error = ['type' => $type, 'message' => $message];
}
/**
* Close connection to source
* Called on script shutdown
*/
function close() { }
/**
* Set internal list page
*
- * @param number $page Page number to list
+ * @param int $page Page number to list
*/
function set_page($page)
{
- $this->list_page = (int)$page;
+ $this->list_page = (int) $page;
}
/**
* Set internal page size
*
- * @param number $size Number of messages to display on one page
+ * @param int $size Number of messages to display on one page
*/
function set_pagesize($size)
{
$this->page_size = (int) $size;
}
/**
* Set internal sort settings
*
* @param string $sort_col Sort column
* @param string $sort_order Sort order
*/
function set_sort_order($sort_col, $sort_order = null)
{
- if ($sort_col != null && in_array($sort_col, $this->coltypes)) {
+ if ($sort_col && in_array($sort_col, $this->coltypes)) {
$this->sort_col = $sort_col;
}
- if ($sort_order != null) {
+
+ if ($sort_order) {
$this->sort_order = strtoupper($sort_order) == 'DESC' ? 'DESC' : 'ASC';
}
}
/**
* Check the given data before saving.
* If input isn't valid, the message to display can be fetched using get_error()
*
- * @param array &$save_data Associative array with data to save
- * @param boolean $autofix Attempt to fix/complete record automatically
+ * @param array &$save_data Associative array with data to save
+ * @param bool $autofix Attempt to fix/complete record automatically
*
- * @return boolean True if input is valid, False if not.
+ * @return bool True if input is valid, False if not.
*/
public function validate(&$save_data, $autofix = false)
{
$rcube = rcube::get_instance();
$valid = true;
// check validity of email addresses
foreach ($this->get_col_values('email', $save_data, true) as $email) {
if (strlen($email)) {
if (!rcube_utils::check_email(rcube_utils::idn_to_ascii($email))) {
- $error = $rcube->gettext(array('name' => 'emailformaterror', 'vars' => array('email' => $email)));
+ $error = $rcube->gettext(['name' => 'emailformaterror', 'vars' => ['email' => $email]]);
$this->set_error(self::ERROR_VALIDATE, $error);
$valid = false;
break;
}
}
}
// allow plugins to do contact validation and auto-fixing
- $plugin = $rcube->plugins->exec_hook('contact_validate', array(
- 'record' => $save_data,
- 'autofix' => $autofix,
- 'valid' => $valid,
- ));
+ $plugin = $rcube->plugins->exec_hook('contact_validate', [
+ 'record' => $save_data,
+ 'autofix' => $autofix,
+ 'valid' => $valid,
+ ]);
if ($valid && !$plugin['valid']) {
$this->set_error(self::ERROR_VALIDATE, $plugin['error']);
}
if (is_array($plugin['record'])) {
$save_data = $plugin['record'];
}
return $plugin['valid'];
}
/**
* Create a new contact record
*
* @param array $save_data Associative array with save data
- * Keys: Field name with optional section in the form FIELD:SECTION
- * Values: Field value. Can be either a string or an array of strings for multiple values
- * @param boolean $check True to check for duplicates first
+ * Keys: Field name with optional section in the form FIELD:SECTION
+ * Values: Field value. Can be either a string or an array of strings for multiple values
+ * @param bool $check True to check for duplicates first
*
* @return mixed The created record ID on success, False on error
*/
function insert($save_data, $check = false)
{
- /* empty for read-only address books */
+ // empty for read-only address books
}
/**
* Create new contact records for every item in the record set
*
* @param rcube_result_set $recset Recordset to insert
- * @param boolean $check True to check for duplicates first
+ * @param bool $check True to check for duplicates first
*
* @return array List of created record IDs
*/
function insertMultiple($recset, $check = false)
{
- $ids = array();
+ $ids = [];
if ($recset instanceof rcube_result_set) {
while ($row = $recset->next()) {
- if ($insert = $this->insert($row, $check))
+ if ($insert = $this->insert($row, $check)) {
$ids[] = $insert;
+ }
}
}
return $ids;
}
/**
* Update a specific contact record
*
* @param mixed $id Record identifier
* @param array $save_cols Associative array with save data
- * Keys: Field name with optional section in the form FIELD:SECTION
- * Values: Field value. Can be either a string or an array of strings for multiple values
+ * Keys: Field name with optional section in the form FIELD:SECTION
+ * Values: Field value. Can be either a string or an array of strings for multiple values
*
* @return mixed On success if ID has been changed returns ID, otherwise True, False on error
*/
function update($id, $save_cols)
{
- /* empty for read-only address books */
+ // empty for read-only address books
}
/**
* Mark one or more contact records as deleted
*
* @param array $ids Record identifiers
* @param bool $force Remove records irreversible (see self::undelete)
*/
function delete($ids, $force = true)
{
- /* empty for read-only address books */
+ // empty for read-only address books
}
/**
* Unmark delete flag on contact record(s)
*
* @param array $ids Record identifiers
*/
function undelete($ids)
{
- /* empty for read-only address books */
+ // empty for read-only address books
}
/**
* Mark all records in database as deleted
*
* @param bool $with_groups Remove also groups
*/
function delete_all($with_groups = false)
{
- /* empty for read-only address books */
+ // empty for read-only address books
}
/**
* Setter for the current group
* (empty, has to be re-implemented by extending class)
*/
function set_group($group_id) { }
/**
* List all active contact groups of this source
*
* @param string $search Optional search string to match group name
* @param int $mode Search mode. Sum of self::SEARCH_*
*
- * @return array Indexed list of contact groups, each a hash array
+ * @return array Indexed list of contact groups, each a hash array
*/
function list_groups($search = null, $mode = 0)
{
- /* empty for address books don't supporting groups */
- return array();
+ // empty for address books don't supporting groups
+ return [];
}
/**
* Get group properties such as name and email address(es)
*
* @param string $group_id Group identifier
*
* @return array Group properties as hash array
*/
function get_group($group_id)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
}
/**
* Create a contact group with the given name
*
* @param string $name The group name
*
* @return array|false False on error, array with record props in success
*/
function create_group($name)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
return false;
}
/**
* Delete the given group and all linked group members
*
* @param string $group_id Group identifier
*
- * @return boolean True on success, false if no data was changed
+ * @return bool True on success, false if no data was changed
*/
function delete_group($group_id)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
return false;
}
/**
* Rename a specific contact group
*
* @param string $group_id Group identifier
* @param string $newname New name to set for this group
* @param string &$newid New group identifier (if changed, otherwise don't set)
*
* @return string|false New name on success, false if no data was changed
*/
function rename_group($group_id, $newname, &$newid)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
return false;
}
/**
* Add the given contact records the a certain group
*
* @param string $group_id Group identifier
* @param array|string $ids List of contact identifiers to be added
*
* @return int Number of contacts added
*/
function add_to_group($group_id, $ids)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
return 0;
}
/**
* Remove the given contact records from a certain group
*
* @param string $group_id Group identifier
* @param array|string $ids List of contact identifiers to be removed
*
* @return int Number of deleted group members
*/
function remove_from_group($group_id, $ids)
{
- /* empty for address books don't supporting groups */
+ // empty for address books don't supporting groups
return 0;
}
/**
* Get group assignments of a specific contact record
*
- * @param mixed Record identifier
+ * @param mixed $id Record identifier
*
- * @return array $id List of assigned groups as ID=>Name pairs
+ * @return array List of assigned groups as ID => Name pairs
* @since 0.5-beta
*/
function get_record_groups($id)
{
- /* empty for address books don't supporting groups */
- return array();
+ // empty for address books don't supporting groups
+ return [];
}
/**
* Utility function to return all values of a certain data column
* either as flat list or grouped by subtype
*
* @param string $col Col name
* @param array $data Record data array as used for saving
* @param bool $flat True to return one array with all values,
* False for hash array with values grouped by type
*
* @return array List of column values
*/
public static function get_col_values($col, $data, $flat = false)
{
- $out = array();
- foreach ((array)$data as $c => $values) {
+ $out = [];
+ foreach ((array) $data as $c => $values) {
if ($c === $col || strpos($c, $col.':') === 0) {
if ($flat) {
- $out = array_merge($out, (array)$values);
+ $out = array_merge($out, (array) $values);
}
else {
list(, $type) = explode(':', $c);
if (isset($out[$type])) {
$out[$type] = array_merge((array) $out[$type], (array) $values);
}
else {
$out[$type] = (array) $values;
}
}
}
}
// remove duplicates
if ($flat && !empty($out)) {
$out = array_unique($out);
}
return $out;
}
- /**
- * Normalize the given string for fulltext search.
- * Currently only optimized for Latin-1 characters; to be extended
- *
- * @param string $str Input string (UTF-8)
- * @return string Normalized string
- * @deprecated since 0.9-beta
- */
- protected static function normalize_string($str)
- {
- return rcube_utils::normalize_string($str);
- }
-
/**
* Compose a valid display name from the given structured contact data
*
* @param array $contact Hash array with contact data as key-value pairs
* @param bool $full_email Don't attempt to extract components from the email address
*
* @return string Display name
*/
public static function compose_display_name($contact, $full_email = false)
{
$contact = rcube::get_instance()->plugins->exec_hook('contact_displayname', $contact);
$fn = isset($contact['name']) ? $contact['name'] : '';
// default display name composition according to vcard standard
if (!$fn) {
$keys = ['prefix', 'firstname', 'middlename', 'surname', 'suffix'];
$fn = implode(' ', array_filter(array_intersect_key($contact, array_flip($keys))));
$fn = trim(preg_replace('/\s+/u', ' ', $fn));
}
// use email address part for name
$email = self::get_col_values('email', $contact, true);
$email = isset($email[0]) ? $email[0] : null;
if ($email && (empty($fn) || $fn == $email)) {
// return full email
if ($full_email) {
return $email;
}
list($emailname) = explode('@', $email);
if (preg_match('/(.*)[\.\-\_](.*)/', $emailname, $match)) {
$fn = trim(ucfirst($match[1]).' '.ucfirst($match[2]));
}
else {
$fn = ucfirst($emailname);
}
}
return $fn;
}
/**
* Compose the name to display in the contacts list for the given contact record.
* This respects the settings parameter how to list conacts.
*
* @param array $contact Hash array with contact data as key-value pairs
*
* @return string List name
*/
public static function compose_list_name($contact)
{
static $compose_mode;
if (!isset($compose_mode)) {
$compose_mode = rcube::get_instance()->config->get('addressbook_name_listing', 0);
}
switch ($compose_mode) {
case 3:
- $fn = implode(' ', array($contact['surname'] . ',', $contact['firstname'], $contact['middlename']));
+ $fn = implode(' ', [$contact['surname'] . ',', $contact['firstname'], $contact['middlename']]);
break;
case 2:
- $fn = implode(' ', array($contact['surname'], $contact['firstname'], $contact['middlename']));
+ $fn = implode(' ', [$contact['surname'], $contact['firstname'], $contact['middlename']]);
break;
case 1:
- $fn = implode(' ', array($contact['firstname'], $contact['middlename'], $contact['surname']));
+ $fn = implode(' ', [$contact['firstname'], $contact['middlename'], $contact['surname']]);
break;
case 0:
- $fn = $contact['name'] ?: implode(' ', array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix']));
+ if (!empty($contact['name'])) {
+ $fn = $contact['name'];
+ }
+ else {
+ $fn = implode(' ', [$contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix']]);
+ }
break;
default:
- $plugin = rcube::get_instance()->plugins->exec_hook('contact_listname', array('contact' => $contact));
+ $plugin = rcube::get_instance()->plugins->exec_hook('contact_listname', ['contact' => $contact]);
$fn = $plugin['fn'];
}
$fn = trim($fn, ', ');
$fn = preg_replace('/\s+/u', ' ', $fn);
// fallbacks...
if ($fn === '') {
// ... display name
if ($name = trim($contact['name'])) {
$fn = $name;
}
// ... organization
else if ($org = trim($contact['organization'])) {
$fn = $org;
}
// ... email address
else if (($email = self::get_col_values('email', $contact, true)) && !empty($email)) {
$fn = $email[0];
}
}
return $fn;
}
/**
* Build contact display name for autocomplete listing
*
* @param array $contact Hash array with contact data as key-value pairs
* @param string $email Optional email address
* @param string $name Optional name (self::compose_list_name() result)
* @param string $templ Optional template to use (defaults to the 'contact_search_name' config option)
*
* @return string Display name
*/
public static function compose_search_name($contact, $email = null, $name = null, $templ = null)
{
static $template;
if (empty($templ) && !isset($template)) { // cache this
$template = rcube::get_instance()->config->get('contact_search_name');
if (empty($template)) {
$template = '{name} <{email}>';
}
}
$result = $templ ?: $template;
if (preg_match_all('/\{[a-z]+\}/', $result, $matches)) {
foreach ($matches[0] as $key) {
$key = trim($key, '{}');
$value = '';
switch ($key) {
case 'name':
$value = $name ?: self::compose_list_name($contact);
// If name(s) are undefined compose_list_name() may return an email address
// here we prevent from returning the same name and email
if ($name === $email && strpos($result, '{email}') !== false) {
$value = '';
}
break;
case 'email':
$value = $email;
break;
}
if (empty($value)) {
$value = strpos($key, ':') ? $contact[$key] : self::get_col_values($key, $contact, true);
if (is_array($value)) {
$value = $value[0];
}
}
$result = str_replace('{' . $key . '}', $value, $result);
}
}
$result = preg_replace('/\s+/u', ' ', $result);
$result = preg_replace('/\s*(<>|\(\)|\[\])/u', '', $result);
$result = trim($result, '/ ');
return $result;
}
/**
* Create a unique key for sorting contacts
*
* @param array $contact Contact record
* @param string $sort_col Sorting column name
*
* @return string Unique key
*/
public static function compose_contact_key($contact, $sort_col)
{
$key = $contact[$sort_col];
// add email to a key to not skip contacts with the same name (#1488375)
if (($email = self::get_col_values('email', $contact, true)) && !empty($email)) {
$key .= ':' . implode(':', (array)$email);
}
// Make the key really unique (as we e.g. support contacts with no email)
$key .= ':' . $contact['sourceid'] . ':' . $contact['ID'];
return $key;
}
/**
* Compare search value with contact data
*
* @param string $colname Data name
* @param string|array $value Data value
* @param string $search Search value
* @param int $mode Search mode
*
* @return bool Comparison result
*/
protected function compare_search_value($colname, $value, $search, $mode)
{
// The value is a date string, for date we'll
// use only strict comparison (mode = 1)
// @TODO: partial search, e.g. match only day and month
if (in_array($colname, $this->date_cols)) {
return (($value = rcube_utils::anytodatetime($value))
&& ($search = rcube_utils::anytodatetime($search))
&& $value->format('Ymd') == $search->format('Ymd'));
}
// Gender is a special value, must use strict comparison (#5757)
if ($colname == 'gender') {
$mode = self::SEARCH_STRICT;
}
// composite field, e.g. address
- foreach ((array)$value as $val) {
+ foreach ((array) $value as $val) {
$val = mb_strtolower($val);
if ($mode & self::SEARCH_STRICT) {
$got = ($val == $search);
}
else if ($mode & self::SEARCH_PREFIX) {
$got = ($search == substr($val, 0, strlen($search)));
}
else {
$got = (strpos($val, $search) !== false);
}
if ($got) {
return true;
}
}
return false;
}
}
diff --git a/program/lib/Roundcube/rcube_addresses.php b/program/lib/Roundcube/rcube_addresses.php
index c69d942f5..d056d7216 100644
--- a/program/lib/Roundcube/rcube_addresses.php
+++ b/program/lib/Roundcube/rcube_addresses.php
@@ -1,406 +1,409 @@
<?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: |
| Interface to the collected addresses database |
+-----------------------------------------------------------------------+
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Collected addresses database
*
* @package Framework
* @subpackage Addressbook
*/
class rcube_addresses extends rcube_contacts
{
protected $db_name = 'collected_addresses';
protected $type = 0;
- protected $table_cols = array('name', 'email');
- protected $fulltext_cols = array('name');
+ protected $table_cols = ['name', 'email'];
+ protected $fulltext_cols = ['name'];
// public properties
public $primary_key = 'address_id';
public $readonly = true;
public $groups = false;
public $undelete = false;
public $deletable = true;
- public $coltypes = array('name', 'email');
- public $date_cols = array();
+ public $coltypes = ['name', 'email'];
+ public $date_cols = [];
/**
* Object constructor
*
- * @param object $dbconn Instance of the rcube_db class
- * @param integer $user User-ID
- * @param int $type Type of the address (1 - recipient, 2 - trusted sender)
+ * @param object $dbconn Instance of the rcube_db class
+ * @param int $user User-ID
+ * @param int $type Type of the address (1 - recipient, 2 - trusted sender)
*/
public function __construct($dbconn, $user, $type)
{
$this->db = $dbconn;
$this->user_id = $user;
$this->type = $type;
$this->ready = $this->db && !$this->db->is_error();
}
/**
* Returns addressbook name
+ *
+ * @return string
*/
public function get_name()
{
if ($this->type == self::TYPE_RECIPIENT) {
return rcube::get_instance()->gettext('collectedrecipients');
}
if ($this->type == self::TYPE_TRUSTED_SENDER) {
return rcube::get_instance()->gettext('trustedsenders');
}
return '';
}
/**
* List the current set of contact records
*
- * @param array List of cols to show, Null means all
- * @param int Only return this number of records, use negative values for tail
- * @param boolean True to skip the count query (select only)
+ * @param array $cols List of cols to show, Null means all
+ * @param int $subset Only return this number of records, use negative values for tail
+ * @param bool $nocount True to skip the count query (select only)
*
* @return array Indexed list of contact records, each a hash array
*/
public function list_records($cols = null, $subset = 0, $nocount = false)
{
if ($nocount || $this->list_page <= 1) {
// create dummy result, we don't need a count now
$this->result = new rcube_result_set();
- } else {
+ }
+ else {
// count all records
$this->result = $this->count();
}
$start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first;
$length = $subset != 0 ? abs($subset) : $this->page_size;
$sql_result = $this->db->limitquery(
"SELECT * FROM " . $this->db->table_name($this->db_name, true)
. " WHERE `user_id` = ? AND `type` = ?"
. ($this->filter ? " AND ".$this->filter : "")
. " ORDER BY `name` " . $this->sort_order . ", `email` " . $this->sort_order,
$start_row,
$length,
$this->user_id,
$this->type
);
while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$sql_arr['ID'] = $sql_arr[$this->primary_key];
$this->result->add($sql_arr);
}
$cnt = count($this->result->records);
// update counter
if ($nocount) {
$this->result->count = $cnt;
}
else if ($this->list_page <= 1) {
if ($cnt < $this->page_size && $subset == 0) {
$this->result->count = $cnt;
}
else if (isset($this->cache['count'])) {
$this->result->count = $this->cache['count'];
}
else {
$this->result->count = $this->_count();
}
}
return $this->result;
}
/**
* Search contacts
*
- * @param mixed $fields The field name or array of field names to search in
- * @param mixed $value Search value (or array of values when $fields is array)
- * @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*
- * @param boolean $select True if results are requested, False if count only
- * @param boolean $nocount True to skip the count query (select only)
- * @param array $required List of fields that cannot be empty
+ * @param mixed $fields The field name or array of field names to search in
+ * @param mixed $value Search value (or array of values when $fields is array)
+ * @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*
+ * @param bool $select True if results are requested, False if count only
+ * @param bool $nocount True to skip the count query (select only)
+ * @param array $required List of fields that cannot be empty
*
* @return object rcube_result_set Contact records and 'count' value
*/
- public function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = array())
+ public function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = [])
{
if (!is_array($required) && !empty($required)) {
- $required = array($required);
+ $required = [$required];
}
- $where = $post_search = array();
+ $where = $post_search = [];
$mode = intval($mode);
// direct ID search
if ($fields == 'ID' || $fields == $this->primary_key) {
$ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value;
$ids = $this->db->array2list($ids, 'integer');
$where[] = $this->primary_key . ' IN (' . $ids . ')';
}
else if (is_array($value)) {
foreach ((array) $fields as $idx => $col) {
$val = $value[$idx];
if (!strlen($val)) {
continue;
}
// table column
if ($col == 'email' && ($mode & rcube_addressbook::SEARCH_STRICT)) {
$where[] = $this->db->ilike($col, $val);
}
else if (in_array($col, $this->table_cols)) {
$where[] = $this->fulltext_sql_where($val, $mode, $col);
}
else {
$where[] = '1 = 0'; // unsupported column
}
}
}
else {
// fulltext search in all fields
if ($fields == '*') {
- $fields = array('name', 'email');
+ $fields = ['name', 'email'];
}
// require each word in to be present in one of the fields
- $words = ($mode & rcube_addressbook::SEARCH_STRICT) ? array($value) : rcube_utils::tokenize_string($value, 1);
+ $words = ($mode & rcube_addressbook::SEARCH_STRICT) ? [$value] : rcube_utils::tokenize_string($value, 1);
foreach ($words as $word) {
- $groups = array();
+ $groups = [];
foreach ((array) $fields as $idx => $col) {
if ($col == 'email' && ($mode & rcube_addressbook::SEARCH_STRICT)) {
$groups[] = $this->db->ilike($col, $word);
}
else if (in_array($col, $this->table_cols)) {
$groups[] = $this->fulltext_sql_where($word, $mode, $col);
}
}
$where[] = '(' . implode(' OR ', $groups) . ')';
}
}
foreach (array_intersect($required, $this->table_cols) as $col) {
$where[] = $this->db->quote_identifier($col) . ' <> ' . $this->db->quote('');
}
if (!empty($where)) {
// use AND operator for advanced searches
$where = implode(' AND ', $where);
$this->set_search_set($where);
if ($select) {
$this->list_records(null, 0, $nocount);
}
else {
$this->result = $this->count();
}
}
return $this->result;
}
/**
* Count number of available contacts in database
*
* @return int Contacts count
*/
protected function _count()
{
// count contacts for this user
$sql_result = $this->db->query(
"SELECT COUNT(`address_id`) AS cnt"
. " FROM " . $this->db->table_name($this->db_name, true)
. " WHERE `user_id` = ? AND `type` = ?"
. ($this->filter ? " AND (" . $this->filter . ")" : ""),
$this->user_id,
$this->type
);
$sql_arr = $this->db->fetch_assoc($sql_result);
$this->cache['count'] = (int) $sql_arr['cnt'];
return $this->cache['count'];
}
/**
* Get a specific contact record
*
* @param mixed $id Record identifier(s)
* @param bool $assoc Enables returning associative array
*
* @return rcube_result_set|array Result object with all record fields
*/
function get_record($id, $assoc = false)
{
// return cached result
if ($this->result && ($first = $this->result->first()) && $first[$this->primary_key] == $id) {
return $assoc ? $first : $this->result;
}
$this->db->query(
"SELECT * FROM " . $this->db->table_name($this->db_name, true)
. " WHERE `address_id` = ? AND `user_id` = ?",
$id,
$this->user_id
);
$this->result = null;
if ($record = $this->db->fetch_assoc()) {
$record['ID'] = $record['address_id'];
$this->result = new rcube_result_set(1);
$this->result->add($record);
}
return $assoc && !empty($record) ? $record : $this->result;
}
/**
* Check the given data before saving.
* If input not valid, the message to display can be fetched using get_error()
*
- * @param array &$save_data Associative array with data to save
- * @param boolean $autofix Try to fix/complete record automatically
+ * @param array &$save_data Associative array with data to save
+ * @param bool $autofix Try to fix/complete record automatically
*
- * @return boolean True if input is valid, False if not.
+ * @return bool True if input is valid, False if not.
*/
public function validate(&$save_data, $autofix = false)
{
$email = array_filter($this->get_col_values('email', $save_data, true));
// require email
if (empty($email) || count($email) > 1) {
$this->set_error(self::ERROR_VALIDATE, 'noemailwarning');
return false;
}
$email = $email[0];
// check validity of the email address
if (!rcube_utils::check_email(rcube_utils::idn_to_ascii($email))) {
$rcube = rcube::get_instance();
$error = $rcube->gettext(['name' => 'emailformaterror', 'vars' => ['email' => $email]]);
$this->set_error(self::ERROR_VALIDATE, $error);
return false;
}
return true;
}
/**
* Create a new contact record
*
* @param array $save_data Associative array with save data
* @param bool $check Enables validity checks
*
- * @return integer|bool The created record ID on success, False on error
+ * @return int|bool The created record ID on success, False on error
*/
function insert($save_data, $check = false)
{
if (!is_array($save_data)) {
return false;
}
if ($check && ($existing = $this->search('email', $save_data['email'], false, false))) {
if ($existing->count) {
return false;
}
}
$this->cache = null;
$this->db->query(
"INSERT INTO " . $this->db->table_name($this->db_name, true)
. " (`user_id`, `changed`, `type`, `name`, `email`)"
. " VALUES (?, " . $this->db->now() . ", ?, ?, ?)",
$this->user_id,
$this->type,
$save_data['name'],
$save_data['email']
);
return $this->db->insert_id($this->db_name);
}
/**
* Update a specific contact record
*
* @param mixed $id Record identifier
* @param array $save_cols Associative array with save data
*
- * @return boolean True on success, False on error
+ * @return bool True on success, False on error
*/
function update($id, $save_cols)
{
return false;
}
/**
* Delete one or more contact records
*
- * @param array $ids Record identifiers
- * @param boolean $force Remove record(s) irreversible (unsupported)
+ * @param array $ids Record identifiers
+ * @param bool $force Remove record(s) irreversible (unsupported)
*
* @return int Number of removed records
*/
function delete($ids, $force = true)
{
if (!is_array($ids)) {
$ids = explode(self::SEPARATOR, $ids);
}
$ids = $this->db->array2list($ids, 'integer');
// flag record as deleted (always)
$this->db->query(
"DELETE FROM " . $this->db->table_name($this->db_name, true)
. " WHERE `user_id` = ? AND `type` = ? AND `address_id` IN ($ids)",
$this->user_id, $this->type
);
$this->cache = null;
return $this->db->affected_rows();
}
/**
* Remove all records from the database
*
* @param bool $with_groups Remove also groups
*
* @return int Number of removed records
*/
function delete_all($with_groups = false)
{
$this->db->query("DELETE FROM " . $this->db->table_name($this->db_name, true)
. " WHERE `user_id` = ? AND `type` = ?",
$this->user_id, $this->type
);
$this->cache = null;
return $this->db->affected_rows();
}
}
diff --git a/program/lib/Roundcube/rcube_base_replacer.php b/program/lib/Roundcube/rcube_base_replacer.php
index 5a3413915..dbffc3fb1 100644
--- a/program/lib/Roundcube/rcube_base_replacer.php
+++ b/program/lib/Roundcube/rcube_base_replacer.php
@@ -1,123 +1,123 @@
<?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: |
| Provide basic functions for base URL replacement |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Helper class to turn relative urls into absolute ones
* using a predefined base
*
* @package Framework
* @subpackage Utils
*/
class rcube_base_replacer
{
private $base_url;
/**
* Class constructor
*
* @param string $base Base URL
*/
public function __construct($base)
{
$this->base_url = $base;
}
/**
* Replace callback
*
* @param array $matches Matching entries
*
* @return string Replaced text with absolute URL
*/
public function callback($matches)
{
return $matches[1] . '="' . self::absolute_url($matches[3], $this->base_url) . '"';
}
/**
* Convert base URLs to absolute ones
*
* @param string $body Text body
*
* @return string Replaced text
*/
public function replace($body)
{
- $regexp = array(
+ $regexp = [
'/(src|background|href)=(["\']?)([^"\'\s>]+)(\2|\s|>)/i',
'/(url\s*\()(["\']?)([^"\'\)\s]+)(\2)\)/i',
- );
+ ];
- return preg_replace_callback($regexp, array($this, 'callback'), $body);
+ return preg_replace_callback($regexp, [$this, 'callback'], $body);
}
/**
* Convert paths like ../xxx to an absolute path using a base url
*
* @param string $path Relative path
* @param string $base_url Base URL
*
* @return string Absolute URL
*/
public static function absolute_url($path, $base_url)
{
// check if path is an absolute URL
if (preg_match('/^[fhtps]+:\/\//', $path)) {
return $path;
}
// check if path is a content-id scheme
if (strpos($path, 'cid:') === 0) {
return $path;
}
$host_url = $base_url;
$abs_path = $path;
// cut base_url to the last directory
if (strrpos($base_url, '/') > 7) {
$host_url = substr($base_url, 0, strpos($base_url, '/', 7));
$base_url = substr($base_url, 0, strrpos($base_url, '/'));
}
// $path is absolute
if ($path && $path[0] == '/') {
$abs_path = $host_url . $path;
}
else {
// strip './' because its the same as ''
$path = preg_replace('/^\.\//', '', $path);
if (preg_match_all('/\.\.\//', $path, $matches, PREG_SET_ORDER)) {
$cnt = count($matches);
while ($cnt--) {
if ($pos = strrpos($base_url, '/')) {
$base_url = substr($base_url, 0, $pos);
}
$path = substr($path, 3);
}
}
$abs_path = $base_url.'/'.$path;
}
return $abs_path;
}
}
diff --git a/program/lib/Roundcube/rcube_cache.php b/program/lib/Roundcube/rcube_cache.php
index 259198806..3808da731 100644
--- a/program/lib/Roundcube/rcube_cache.php
+++ b/program/lib/Roundcube/rcube_cache.php
@@ -1,635 +1,638 @@
<?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: |
| Caching engine |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Interface class for accessing Roundcube cache
*
* @package Framework
* @subpackage Cache
*/
class rcube_cache
{
protected $type;
protected $userid;
protected $prefix;
protected $ttl;
protected $packed;
protected $indexed;
protected $index;
protected $index_update;
- protected $cache = array();
- protected $updates = array();
- protected $exp_records = array();
+ protected $cache = [];
+ protected $updates = [];
+ protected $exp_records = [];
protected $refresh_time = 0.5; // how often to refresh/save the index and cache entries
protected $debug = false;
protected $max_packet = -1;
const MAX_EXP_LEVEL = 2;
const DATE_FORMAT = 'Y-m-d H:i:s.u';
const DATE_FORMAT_REGEX = '[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\.[0-9]{1,6}';
/**
* Object factory
*
* @param string $type Engine type ('db', 'memcache', 'apc', 'redis')
* @param int $userid User identifier
* @param string $prefix Key name prefix
* @param string $ttl Expiration time of memcache/apc items
* @param bool $packed Enables/disabled data serialization.
* It's possible to disable data serialization if you're sure
* stored data will be always a safe string
* @param bool $indexed Use indexed cache. Indexed cache is more appropriate for
* storing big data with possibility to remove it by a key prefix.
* Non-indexed cache does not remove data, but flags it for expiration,
* also stores it in memory until close() method is called.
*
* @param rcube_cache Cache object
*/
public static function factory($type, $userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
$driver = strtolower($type) ?: 'db';
$class = "rcube_cache_$driver";
if (!$driver || !class_exists($class)) {
- rcube::raise_error(array('code' => 600, 'type' => 'db',
- 'line' => __LINE__, 'file' => __FILE__,
- 'message' => "Configuration error. Unsupported cache driver: $driver"),
- true, true);
+ rcube::raise_error([
+ 'code' => 600, 'type' => 'db',
+ 'line' => __LINE__, 'file' => __FILE__,
+ 'message' => "Configuration error. Unsupported cache driver: $driver"
+ ],
+ true, true
+ );
}
return new $class($userid, $prefix, $ttl, $packed, $indexed);
}
/**
* Object constructor.
*
* @param int $userid User identifier
* @param string $prefix Key name prefix
* @param string $ttl Expiration time of memcache/apc items
* @param bool $packed Enables/disabled data serialization.
* It's possible to disable data serialization if you're sure
* stored data will be always a safe string
* @param bool $indexed Use indexed cache. Indexed cache is more appropriate for
* storing big data with possibility to remove it by key prefix.
* Non-indexed cache does not remove data, but flags it for expiration,
* also stores it in memory until close() method is called.
*/
public function __construct($userid, $prefix = '', $ttl = 0, $packed = true, $indexed = false)
{
$this->userid = (int) $userid;
$this->ttl = min(get_offset_sec($ttl), 2592000);
$this->prefix = $prefix;
$this->packed = $packed;
$this->indexed = $indexed;
}
/**
* Returns cached value.
*
* @param string $key Cache key name
*
* @return mixed Cached value
*/
public function get($key)
{
if (array_key_exists($key, $this->cache)) {
return $this->cache[$key];
}
return $this->read_record($key);
}
/**
* Sets (add/update) value in cache.
*
* @param string $key Cache key name
* @param mixed $data Cache data
*
- * @return boolean True on success, False on failure
+ * @return bool True on success, False on failure
*/
public function set($key, $data)
{
return $this->write_record($key, $data);
}
/**
* @deprecated Use self::get()
*/
public function read($key)
{
return $this->get($key);
}
/**
* @deprecated Use self::set()
*/
public function write($key, $data)
{
return $this->set($key, $data);
}
/**
* Clears the cache.
*
- * @param string $key Cache key name or pattern
- * @param boolean $prefix_mode Enable it to clear all keys starting
- * with prefix specified in $key
+ * @param string $key Cache key name or pattern
+ * @param bool $prefix_mode Enable it to clear all keys starting
+ * with prefix specified in $key
*/
public function remove($key = null, $prefix_mode = false)
{
// Remove record(s) from the backend
$this->remove_record($key, $prefix_mode);
}
/**
* Remove cache records older than ttl
*/
public function expunge()
{
// to be overwritten by engine class
}
/**
* Remove expired records of all caches
*/
public static function gc()
{
// Only DB cache requires an action to remove expired entries
rcube_cache_db::gc();
}
/**
* Writes the cache back to the DB.
*/
public function close()
{
$this->write_index(true);
$this->index = null;
- $this->cache = array();
- $this->updates = array();
+ $this->cache = [];
+ $this->updates = [];
}
/**
* A helper to build cache key for specified parameters.
*
* @param string $prefix Key prefix (Max. length 64 characters)
* @param array $params Additional parameters
*
* @return string Key name
*/
- public static function key_name($prefix, $params = array())
+ public static function key_name($prefix, $params = [])
{
$cache_key = $prefix;
if (!empty($params)) {
- $func = function($v) {
+ $func = function($v) {
if (is_array($v)) {
sort($v);
}
return is_string($v) ? $v : serialize($v);
};
$params = array_map($func, $params);
$cache_key .= '.' . md5(implode(':', $params));
}
return $cache_key;
}
/**
* Reads cache entry.
*
* @param string $key Cache key name
*
* @return mixed Cached value
*/
protected function read_record($key)
{
$this->load_index();
// Consistency check (#1490390)
if (is_array($this->index) && !in_array($key, $this->index)) {
// we always check if the key exist in the index
// to have data in consistent state. Keeping the index consistent
// is needed for keys delete operation when we delete all keys or by prefix.
return;
}
$ckey = $this->ckey($key);
$data = $this->get_item($ckey);
if ($this->indexed) {
return $data !== false ? $this->unserialize($data) : null;
}
if ($data !== false) {
$timestamp = 0;
$utc = new DateTimeZone('UTC');
// Extract timestamp from the data entry
if (preg_match('/^(' . self::DATE_FORMAT_REGEX . '):/', $data, $matches)) {
try {
$timestamp = new DateTime($matches[1], $utc);
$data = substr($data, strlen($matches[1]) + 1);
}
catch (Exception $e) {
// invalid date = no timestamp
}
}
// Check if the entry is still valid by comparing with EXP timestamps
// For example for key 'mailboxes.123456789' we check entries:
// 'EXP:*', 'EXP:mailboxes' and 'EXP:mailboxes.123456789'.
if ($timestamp) {
$path = explode('.', "*.$key");
$path_len = min(self::MAX_EXP_LEVEL + 1, count($path));
for ($x = 1; $x <= $path_len; $x++) {
$prefix = implode('.', array_slice($path, 0, $x));
if ($x > 1) {
$prefix = substr($prefix, 2); // remove "*." prefix
}
if (($ts = $this->get_exp_timestamp($prefix)) && $ts > $timestamp) {
$timestamp = 0;
break;
}
}
}
$data = $timestamp ? $this->unserialize($data) : null;
}
else {
$data = null;
}
return $this->cache[$key] = $data;
}
/**
* Writes single cache record into DB.
*
* @param string $key Cache key name
* @param mixed $data Serialized cache data
*
- * @return boolean True on success, False on failure
+ * @return bool True on success, False on failure
*/
protected function write_record($key, $data)
{
if ($this->indexed) {
$result = $this->store_record($key, $data);
if ($result) {
$this->load_index();
$this->index[] = $key;
if (!$this->index_update) {
$this->index_update = time();
}
}
}
else {
// In this mode we do not save the entry to the database immediately
// It's because we have cases where the same entry is updated
// multiple times in one request (e.g. 'messagecount' entry rcube_imap).
$this->updates[$key] = new DateTime('now', new DateTimeZone('UTC'));
$this->cache[$key] = $data;
$result = true;
}
$this->write_index();
return $result;
}
/**
* Deletes the cache record(s).
*
* @param string $key Cache key name or pattern
* @param boolean $prefix_mode Enable it to clear all keys starting
* with prefix specified in $key
*/
protected function remove_record($key = null, $prefix_mode = false)
{
if ($this->indexed) {
return $this->remove_record_indexed($key, $prefix_mode);
}
// "Remove" all keys
if ($key === null) {
$ts = new DateTime('now', new DateTimeZone('UTC'));
$this->add_item($this->ekey('*'), $ts->format(self::DATE_FORMAT));
- $this->cache = array();
+ $this->cache = [];
}
// "Remove" keys by name prefix
else if ($prefix_mode) {
$ts = new DateTime('now', new DateTimeZone('UTC'));
$prefix = implode('.', array_slice(explode('.', trim($key, '. ')), 0, self::MAX_EXP_LEVEL));
$this->add_item($this->ekey($prefix), $ts->format(self::DATE_FORMAT));
foreach (array_keys($this->cache) as $k) {
if (strpos($k, $key) === 0) {
$this->cache[$k] = null;
}
}
}
// Remove one key by name
else {
$this->delete_item($this->ckey($key));
$this->cache[$key] = null;
}
}
/**
* @see self::remove_record()
*/
protected function remove_record_indexed($key = null, $prefix_mode = false)
{
$this->load_index();
// Remove all keys
if ($key === null) {
foreach ($this->index as $key) {
$this->delete_item($this->ckey($key));
if (!$this->index_update) {
$this->index_update = time();
}
}
- $this->index = array();
+ $this->index = [];
}
// Remove keys by name prefix
else if ($prefix_mode) {
foreach ($this->index as $idx => $k) {
if (strpos($k, $key) === 0) {
$this->delete_item($this->ckey($k));
unset($this->index[$idx]);
if (!$this->index_update) {
$this->index_update = time();
}
}
}
}
// Remove one key by name
else {
$this->delete_item($this->ckey($key));
if (($idx = array_search($key, $this->index)) !== false) {
unset($this->index[$idx]);
if (!$this->index_update) {
$this->index_update = time();
}
}
}
$this->write_index();
}
/**
* Writes the index entry as well as updated entries into memcache/apc/redis DB.
*/
protected function write_index($force = null)
{
// Write updated/new entries when needed
if (!$this->indexed) {
$need_update = $force === true;
if (!$need_update && !empty($this->updates)) {
$now = new DateTime('now', new DateTimeZone('UTC'));
$need_update = floatval(min($this->updates)->format('U.u')) < floatval($now->format('U.u')) - $this->refresh_time;
}
if ($need_update) {
foreach ($this->updates as $key => $ts) {
if (isset($this->cache[$key])) {
$this->store_record($key, $this->cache[$key], $ts);
}
}
- $this->updates = array();
+ $this->updates = [];
}
}
// Write index entry when needed
else {
$need_update = $this->index_update && $this->index !== null
&& ($force === true || $this->index_update > time() - $this->refresh_time);
if ($need_update) {
$index = serialize(array_values(array_unique($this->index)));
$this->add_item($this->ikey(), $index);
$this->index_update = null;
$this->index = null;
}
}
}
/**
* Gets the index entry from memcache/apc/redis DB.
*/
protected function load_index()
{
if (!$this->indexed) {
return;
}
if ($this->index !== null) {
return;
}
$data = $this->get_item($this->ikey());
- $this->index = $data ? unserialize($data) : array();
+ $this->index = $data ? unserialize($data) : [];
}
/**
* Write data entry into cache
*/
protected function store_record($key, $data, $ts = null)
{
$value = $this->serialize($data);
if (!$this->indexed) {
if (!$ts) {
$ts = new DateTime('now', new DateTimeZone('UTC'));
}
$value = $ts->format(self::DATE_FORMAT) . ':' . $value;
}
$size = strlen($value);
// don't attempt to write too big data sets
if ($size > $this->max_packet_size()) {
trigger_error("rcube_cache: max_packet_size ($this->max_packet) exceeded for key $key. Tried to write $size bytes", E_USER_WARNING);
return false;
}
return $this->add_item($this->ckey($key), $value);
}
/**
* Fetches cache entry.
*
* @param string $key Cache internal key name
*
* @return mixed Cached value
*/
protected function get_item($key)
{
// to be overwritten by engine class
}
/**
* Adds entry into memcache/apc/redis DB.
*
* @param string $key Cache internal key name
* @param mixed $data Serialized cache data
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function add_item($key, $data)
{
// to be overwritten by engine class
}
/**
* Deletes entry from memcache/apc/redis DB.
*
* @param string $key Cache internal key name
*
- * @param boolean True on success, False on failure
+ * @param bool True on success, False on failure
*/
protected function delete_item($key)
{
// to be overwritten by engine class
}
/**
* Get EXP:<key> record value from cache
*/
protected function get_exp_timestamp($key)
{
if (!array_key_exists($key, $this->exp_records)) {
$data = $this->get_item($this->ekey($key));
$this->exp_records[$key] = $data ? new DateTime($data, new DateTimeZone('UTC')) : null;
}
return $this->exp_records[$key];
}
/**
* Creates per-user index cache key name (for memcache, apc, redis)
*
* @return string Cache key
*/
protected function ikey()
{
$key = $this->prefix . 'INDEX';
if ($this->userid) {
$key = $this->userid . ':' . $key;
}
return $key;
}
/**
* Creates per-user cache key name (for memcache, apc, redis)
*
* @param string $key Cache key name
*
* @return string Cache key
*/
protected function ckey($key)
{
$key = $this->prefix . ':' . $key;
if ($this->userid) {
$key = $this->userid . ':' . $key;
}
return $key;
}
/**
* Creates per-user cache key name for expiration time entry
*
* @param string $key Cache key name
*
* @return string Cache key
*/
protected function ekey($key, $prefix = null)
{
$key = $this->prefix . 'EXP:' . $key;
if ($this->userid) {
$key = $this->userid . ':' . $key;
}
return $key;
}
/**
* Serializes data for storing
*/
protected function serialize($data)
{
return $this->packed ? serialize($data) : $data;
}
/**
* Unserializes serialized data
*/
protected function unserialize($data)
{
return $this->packed ? @unserialize($data) : $data;
}
/**
* Determine the maximum size for cache data to be written
*/
protected function max_packet_size()
{
if ($this->max_packet < 0) {
$config = rcube::get_instance()->config;
$max_packet = $config->get($this->type . '_max_allowed_packet');
$this->max_packet = parse_bytes($max_packet) ?: 2097152; // default/max is 2 MB
}
return $this->max_packet;
}
/**
* Write memcache/apc/redis debug info to the log
*/
protected function debug($type, $key, $data = null, $result = null)
{
$line = strtoupper($type) . ' ' . $key;
if ($data !== null) {
$line .= ' ' . ($this->packed ? $data : serialize($data));
}
rcube::debug($this->type, $line, $result);
}
}
diff --git a/program/lib/Roundcube/rcube_charset.php b/program/lib/Roundcube/rcube_charset.php
index e5d3e407a..311ba20ce 100644
--- a/program/lib/Roundcube/rcube_charset.php
+++ b/program/lib/Roundcube/rcube_charset.php
@@ -1,554 +1,556 @@
<?php
/**
+-----------------------------------------------------------------------+
| This file is part of the Roundcube Webmail client |
| |
| Copyright (C) The Roundcube Dev Team |
| Copyright (C) Kolab Systems AG |
| Copyright (C) 2000 Edmund Grimley Evans <edmundo@rano.org> |
| |
| 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: |
| Provide charset conversion functionality |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
| Author: Edmund Grimley Evans <edmundo@rano.org> |
+-----------------------------------------------------------------------+
*/
/**
* Character sets conversion functionality
*
* @package Framework
* @subpackage Core
*/
class rcube_charset
{
// Aliases: some of them from HTML5 spec.
- static public $aliases = array(
+ static public $aliases = [
'USASCII' => 'WINDOWS-1252',
'ANSIX31101983' => 'WINDOWS-1252',
'ANSIX341968' => 'WINDOWS-1252',
'UNKNOWN8BIT' => 'ISO-8859-15',
'UNKNOWN' => 'ISO-8859-15',
'USERDEFINED' => 'ISO-8859-15',
'KSC56011987' => 'EUC-KR',
'GB2312' => 'GBK',
'GB231280' => 'GBK',
'UNICODE' => 'UTF-8',
'UTF7IMAP' => 'UTF7-IMAP',
'TIS620' => 'WINDOWS-874',
'ISO88599' => 'WINDOWS-1254',
'ISO885911' => 'WINDOWS-874',
'MACROMAN' => 'MACINTOSH',
'77' => 'MAC',
'128' => 'SHIFT-JIS',
'129' => 'CP949',
'130' => 'CP1361',
'134' => 'GBK',
'136' => 'BIG5',
'161' => 'WINDOWS-1253',
'162' => 'WINDOWS-1254',
'163' => 'WINDOWS-1258',
'177' => 'WINDOWS-1255',
'178' => 'WINDOWS-1256',
'186' => 'WINDOWS-1257',
'204' => 'WINDOWS-1251',
'222' => 'WINDOWS-874',
'238' => 'WINDOWS-1250',
'MS950' => 'CP950',
'WINDOWS949' => 'UHC',
'WINDOWS1257' => 'ISO-8859-13',
'ISO2022JP' => 'ISO-2022-JP-MS',
- );
+ ];
/**
* Windows codepages
*
* @var array
*/
- static public $windows_codepages = array(
+ static public $windows_codepages = [
37 => 'IBM037', // IBM EBCDIC US-Canada
437 => 'IBM437', // OEM United States
500 => 'IBM500', // IBM EBCDIC International
708 => 'ASMO-708', // Arabic (ASMO 708)
720 => 'DOS-720', // Arabic (Transparent ASMO); Arabic (DOS)
737 => 'IBM737', // OEM Greek (formerly 437G); Greek (DOS)
775 => 'IBM775', // OEM Baltic; Baltic (DOS)
850 => 'IBM850', // OEM Multilingual Latin 1; Western European (DOS)
852 => 'IBM852', // OEM Latin 2; Central European (DOS)
855 => 'IBM855', // OEM Cyrillic (primarily Russian)
857 => 'IBM857', // OEM Turkish; Turkish (DOS)
858 => 'IBM00858', // OEM Multilingual Latin 1 + Euro symbol
860 => 'IBM860', // OEM Portuguese; Portuguese (DOS)
861 => 'IBM861', // OEM Icelandic; Icelandic (DOS)
862 => 'DOS-862', // OEM Hebrew; Hebrew (DOS)
863 => 'IBM863', // OEM French Canadian; French Canadian (DOS)
864 => 'IBM864', // OEM Arabic; Arabic (864)
865 => 'IBM865', // OEM Nordic; Nordic (DOS)
866 => 'cp866', // OEM Russian; Cyrillic (DOS)
869 => 'IBM869', // OEM Modern Greek; Greek, Modern (DOS)
870 => 'IBM870', // IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2
874 => 'windows-874', // ANSI/OEM Thai (ISO 8859-11); Thai (Windows)
875 => 'cp875', // IBM EBCDIC Greek Modern
932 => 'shift_jis', // ANSI/OEM Japanese; Japanese (Shift-JIS)
936 => 'gb2312', // ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312)
950 => 'big5', // ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5)
1026 => 'IBM1026', // IBM EBCDIC Turkish (Latin 5)
1047 => 'IBM01047', // IBM EBCDIC Latin 1/Open System
1140 => 'IBM01140', // IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro)
1141 => 'IBM01141', // IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro)
1142 => 'IBM01142', // IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro)
1143 => 'IBM01143', // IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro)
1144 => 'IBM01144', // IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro)
1145 => 'IBM01145', // IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro)
1146 => 'IBM01146', // IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro)
1147 => 'IBM01147', // IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro)
1148 => 'IBM01148', // IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro)
1149 => 'IBM01149', // IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro)
1200 => 'UTF-16', // Unicode UTF-16, little endian byte order (BMP of ISO 10646); available only to managed applications
1201 => 'UTF-16BE', // Unicode UTF-16, big endian byte order; available only to managed applications
1250 => 'windows-1250', // ANSI Central European; Central European (Windows)
1251 => 'windows-1251', // ANSI Cyrillic; Cyrillic (Windows)
1252 => 'windows-1252', // ANSI Latin 1; Western European (Windows)
1253 => 'windows-1253', // ANSI Greek; Greek (Windows)
1254 => 'windows-1254', // ANSI Turkish; Turkish (Windows)
1255 => 'windows-1255', // ANSI Hebrew; Hebrew (Windows)
1256 => 'windows-1256', // ANSI Arabic; Arabic (Windows)
1257 => 'windows-1257', // ANSI Baltic; Baltic (Windows)
1258 => 'windows-1258', // ANSI/OEM Vietnamese; Vietnamese (Windows)
10000 => 'macintosh', // MAC Roman; Western European (Mac)
12000 => 'UTF-32', // Unicode UTF-32, little endian byte order; available only to managed applications
12001 => 'UTF-32BE', // Unicode UTF-32, big endian byte order; available only to managed applications
20127 => 'US-ASCII', // US-ASCII (7-bit)
20273 => 'IBM273', // IBM EBCDIC Germany
20277 => 'IBM277', // IBM EBCDIC Denmark-Norway
20278 => 'IBM278', // IBM EBCDIC Finland-Sweden
20280 => 'IBM280', // IBM EBCDIC Italy
20284 => 'IBM284', // IBM EBCDIC Latin America-Spain
20285 => 'IBM285', // IBM EBCDIC United Kingdom
20290 => 'IBM290', // IBM EBCDIC Japanese Katakana Extended
20297 => 'IBM297', // IBM EBCDIC France
20420 => 'IBM420', // IBM EBCDIC Arabic
20423 => 'IBM423', // IBM EBCDIC Greek
20424 => 'IBM424', // IBM EBCDIC Hebrew
20838 => 'IBM-Thai', // IBM EBCDIC Thai
20866 => 'koi8-r', // Russian (KOI8-R); Cyrillic (KOI8-R)
20871 => 'IBM871', // IBM EBCDIC Icelandic
20880 => 'IBM880', // IBM EBCDIC Cyrillic Russian
20905 => 'IBM905', // IBM EBCDIC Turkish
20924 => 'IBM00924', // IBM EBCDIC Latin 1/Open System (1047 + Euro symbol)
20932 => 'EUC-JP', // Japanese (JIS 0208-1990 and 0212-1990)
20936 => 'cp20936', // Simplified Chinese (GB2312); Chinese Simplified (GB2312-80)
20949 => 'cp20949', // Korean Wansung
21025 => 'cp1025', // IBM EBCDIC Cyrillic Serbian-Bulgarian
21866 => 'koi8-u', // Ukrainian (KOI8-U); Cyrillic (KOI8-U)
28591 => 'iso-8859-1', // ISO 8859-1 Latin 1; Western European (ISO)
28592 => 'iso-8859-2', // ISO 8859-2 Central European; Central European (ISO)
28593 => 'iso-8859-3', // ISO 8859-3 Latin 3
28594 => 'iso-8859-4', // ISO 8859-4 Baltic
28595 => 'iso-8859-5', // ISO 8859-5 Cyrillic
28596 => 'iso-8859-6', // ISO 8859-6 Arabic
28597 => 'iso-8859-7', // ISO 8859-7 Greek
28598 => 'iso-8859-8', // ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
28599 => 'iso-8859-9', // ISO 8859-9 Turkish
28603 => 'iso-8859-13', // ISO 8859-13 Estonian
28605 => 'iso-8859-15', // ISO 8859-15 Latin 9
38598 => 'iso-8859-8-i', // ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
50220 => 'iso-2022-jp', // ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
50221 => 'csISO2022JP', // ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana)
50222 => 'iso-2022-jp', // ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI)
50225 => 'iso-2022-kr', // ISO 2022 Korean
51932 => 'EUC-JP', // EUC Japanese
51936 => 'EUC-CN', // EUC Simplified Chinese; Chinese Simplified (EUC)
51949 => 'EUC-KR', // EUC Korean
52936 => 'hz-gb-2312', // HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ)
54936 => 'GB18030', // Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030)
65000 => 'UTF-7',
65001 => 'UTF-8',
- );
+ ];
/**
* Catch an error and throw an exception.
*
* @param int $errno Level of the error
* @param string $errstr Error message
+ *
+ * @throws ErrorException
*/
public static function error_handler($errno, $errstr)
{
throw new ErrorException($errstr, 0, $errno);
}
/**
* Parse and validate charset name string.
* Sometimes charset string is malformed, there are also charset aliases,
* but we need strict names for charset conversion (specially utf8 class)
*
* @param string $input Input charset name
*
* @return string The validated charset name
*/
public static function parse_charset($input)
{
- static $charsets = array();
+ static $charsets = [];
$charset = strtoupper($input);
if (isset($charsets[$input])) {
return $charsets[$input];
}
- $charset = preg_replace(array(
+ $charset = preg_replace([
'/^[^0-9A-Z]+/', // e.g. _ISO-8859-JP$SIO
'/\$.*$/', // e.g. _ISO-8859-JP$SIO
'/UNICODE-1-1-*/', // RFC1641/1642
'/^X-/', // X- prefix (e.g. X-ROMAN8 => ROMAN8)
'/\*.*$/' // lang code according to RFC 2231.5
- ), '', $charset);
+ ], '', $charset);
if ($charset == 'BINARY') {
return $charsets[$input] = null;
}
// allow A-Z and 0-9 only
$str = preg_replace('/[^A-Z0-9]/', '', $charset);
$result = $charset;
if (isset(self::$aliases[$str])) {
$result = self::$aliases[$str];
}
// UTF
else if (preg_match('/U[A-Z][A-Z](7|8|16|32)(BE|LE)*/', $str, $m)) {
$result = 'UTF-' . $m[1] . (!empty($m[2]) ? $m[2] : '');
}
// ISO-8859
else if (preg_match('/ISO8859([0-9]{0,2})/', $str, $m)) {
$iso = 'ISO-8859-' . ($m[1] ?: 1);
// some clients sends windows-1252 text as latin1,
// it is safe to use windows-1252 for all latin1
$result = $iso == 'ISO-8859-1' ? 'WINDOWS-1252' : $iso;
}
// handle broken charset names e.g. WINDOWS-1250HTTP-EQUIVCONTENT-TYPE
else if (preg_match('/(WIN|WINDOWS)([0-9]+)/', $str, $m)) {
$result = 'WINDOWS-' . $m[2];
}
// LATIN
else if (preg_match('/LATIN(.*)/', $str, $m)) {
- $aliases = array('2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
+ $aliases = ['2' => 2, '3' => 3, '4' => 4, '5' => 9, '6' => 10,
'7' => 13, '8' => 14, '9' => 15, '10' => 16,
'ARABIC' => 6, 'CYRILLIC' => 5, 'GREEK' => 7, 'GREEK1' => 7, 'HEBREW' => 8
- );
+ ];
// some clients sends windows-1252 text as latin1,
// it is safe to use windows-1252 for all latin1
if ($m[1] == 1) {
$result = 'WINDOWS-1252';
}
// we need ISO labels
else if (!empty($aliases[$m[1]])) {
$result = 'ISO-8859-'.$aliases[$m[1]];
}
}
$charsets[$input] = $result;
return $result;
}
/**
* Convert a string from one charset to another.
*
* @param string $str Input string
* @param string $from Suspected charset of the input string
* @param string $to Target charset to convert to; defaults to RCUBE_CHARSET
*
* @return string Converted string
*/
public static function convert($str, $from, $to = null)
{
$to = empty($to) ? RCUBE_CHARSET : self::parse_charset($to);
$from = self::parse_charset($from);
// It is a common case when UTF-16 charset is used with US-ASCII content (#1488654)
// In that case we can just skip the conversion (use UTF-8)
if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) {
$from = 'UTF-8';
}
if ($from == $to || empty($str) || empty($from)) {
return $str;
}
// Ignore invalid characters
$mbstring_sc = mb_substitute_character();
mb_substitute_character('none');
// throw an exception if mbstring reports an illegal character in input
// using mb_check_encoding() is much slower
set_error_handler(array('rcube_charset', 'error_handler'), E_WARNING);
try {
$out = mb_convert_encoding($str, $to, $from);
}
catch (ErrorException $e) {
$out = false;
}
restore_error_handler();
mb_substitute_character($mbstring_sc);
if ($out !== false) {
return $out;
}
// return the original string
return $str;
}
/**
* Converts string from standard UTF-7 (RFC 2152) to UTF-8.
*
* @param string $str Input string (UTF-7)
*
* @return string Converted string (UTF-8)
* @deprecated use self::convert()
*/
public static function utf7_to_utf8($str)
{
return self::convert($str, 'UTF-7', 'UTF-8');
}
/**
* Converts string from UTF-16 to UTF-8 (helper for utf-7 to utf-8 conversion)
*
* @param string $str Input string
*
* @return string The converted string
* @deprecated use self::convert()
*/
public static function utf16_to_utf8($str)
{
return self::convert($str, 'UTF-16BE', 'UTF-8');
}
/**
* Convert the data ($str) from RFC 2060's UTF-7 to UTF-8.
* If input data is invalid, return the original input string.
* RFC 2060 obviously intends the encoding to be unique (see
* point 5 in section 5.1.3), so we reject any non-canonical
* form, such as &ACY- (instead of &-) or &AMA-&AMA- (instead
* of &AMAAwA-).
*
* @param string $str Input string (UTF7-IMAP)
*
* @return string Output string (UTF-8)
* @deprecated use self::convert()
*/
public static function utf7imap_to_utf8($str)
{
return self::convert($str, 'UTF7-IMAP', 'UTF-8');
}
/**
* Convert the data ($str) from UTF-8 to RFC 2060's UTF-7.
* Unicode characters above U+FFFF are replaced by U+FFFE.
* If input data is invalid, return an empty string.
*
* @param string $str Input string (UTF-8)
*
* @return string Output string (UTF7-IMAP)
* @deprecated use self::convert()
*/
public static function utf8_to_utf7imap($str)
{
return self::convert($str, 'UTF-8', 'UTF7-IMAP');
}
/**
* A method to guess character set of a string.
*
* @param string $string String
* @param string $failover Default result for failover
* @param string $language User language
*
* @return string Charset name
*/
public static function detect($string, $failover = null, $language = null)
{
if (substr($string, 0, 4) == "\0\0\xFE\xFF") return 'UTF-32BE'; // Big Endian
if (substr($string, 0, 4) == "\xFF\xFE\0\0") return 'UTF-32LE'; // Little Endian
if (substr($string, 0, 2) == "\xFE\xFF") return 'UTF-16BE'; // Big Endian
if (substr($string, 0, 2) == "\xFF\xFE") return 'UTF-16LE'; // Little Endian
if (substr($string, 0, 3) == "\xEF\xBB\xBF") return 'UTF-8';
// heuristics
if (strlen($string) >= 4) {
if ($string[0] == "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-32BE';
if ($string[0] != "\0" && $string[1] == "\0" && $string[2] == "\0" && $string[3] == "\0") return 'UTF-32LE';
if ($string[0] == "\0" && $string[1] != "\0" && $string[2] == "\0" && $string[3] != "\0") return 'UTF-16BE';
if ($string[0] != "\0" && $string[1] == "\0" && $string[2] != "\0" && $string[3] == "\0") return 'UTF-16LE';
}
if (empty($language)) {
$rcube = rcube::get_instance();
$language = $rcube->get_user_language();
}
// Prioritize charsets according to current language (#1485669)
$prio = null;
switch ($language) {
case 'ja_JP':
- $prio = array('ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win');
+ $prio = ['ISO-2022-JP', 'JIS', 'UTF-8', 'EUC-JP', 'eucJP-win', 'SJIS', 'SJIS-win'];
break;
case 'zh_CN':
case 'zh_TW':
- $prio = array('UTF-8', 'BIG-5', 'GB2312', 'EUC-TW');
+ $prio = ['UTF-8', 'BIG-5', 'GB2312', 'EUC-TW'];
break;
case 'ko_KR':
- $prio = array('UTF-8', 'EUC-KR', 'ISO-2022-KR');
+ $prio = ['UTF-8', 'EUC-KR', 'ISO-2022-KR'];
break;
case 'ru_RU':
- $prio = array('UTF-8', 'WINDOWS-1251', 'KOI8-R');
+ $prio = ['UTF-8', 'WINDOWS-1251', 'KOI8-R'];
break;
case 'tr_TR':
- $prio = array('UTF-8', 'ISO-8859-9', 'WINDOWS-1254');
+ $prio = ['UTF-8', 'ISO-8859-9', 'WINDOWS-1254'];
break;
}
// mb_detect_encoding() is not reliable for some charsets (#1490135)
// use mb_check_encoding() to make charset priority lists really working
if (!empty($prio) && function_exists('mb_check_encoding')) {
foreach ($prio as $encoding) {
if (mb_check_encoding($string, $encoding)) {
return $encoding;
}
}
}
if (function_exists('mb_detect_encoding')) {
if (empty($prio)) {
- $prio = array('UTF-8', 'SJIS', 'GB2312',
+ $prio = ['UTF-8', 'SJIS', 'GB2312',
'ISO-8859-1', 'ISO-8859-2', 'ISO-8859-3', 'ISO-8859-4',
'ISO-8859-5', 'ISO-8859-6', 'ISO-8859-7', 'ISO-8859-8', 'ISO-8859-9',
'ISO-8859-10', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16',
'WINDOWS-1252', 'WINDOWS-1251', 'EUC-JP', 'EUC-TW', 'KOI8-R', 'BIG-5',
'ISO-2022-KR', 'ISO-2022-JP',
- );
+ ];
}
$encodings = array_unique(array_merge($prio, mb_list_encodings()));
if ($encoding = mb_detect_encoding($string, $encodings)) {
return $encoding;
}
}
// No match, check for UTF-8
// from http://w3.org/International/questions/qa-forms-utf-8.html
if (preg_match('/\A(
[\x09\x0A\x0D\x20-\x7E]
| [\xC2-\xDF][\x80-\xBF]
| \xE0[\xA0-\xBF][\x80-\xBF]
| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}
| \xED[\x80-\x9F][\x80-\xBF]
| \xF0[\x90-\xBF][\x80-\xBF]{2}
| [\xF1-\xF3][\x80-\xBF]{3}
| \xF4[\x80-\x8F][\x80-\xBF]{2}
)*\z/xs', substr($string, 0, 2048))
) {
return 'UTF-8';
}
return $failover;
}
/**
* Removes non-unicode characters from input.
*
* @param mixed $input String or array.
*
* @return mixed String or array
*/
public static function clean($input)
{
// handle input of type array
if (is_array($input)) {
foreach ($input as $idx => $val) {
$input[$idx] = self::clean($val);
}
return $input;
}
if (!is_string($input) || $input == '') {
return $input;
}
// mbstring is much faster (especially with long strings)
if (function_exists('mb_convert_encoding')) {
$msch = mb_substitute_character();
mb_substitute_character('none');
$res = mb_convert_encoding($input, 'UTF-8', 'UTF-8');
mb_substitute_character($msch);
if ($res !== false) {
return $res;
}
}
$seq = '';
$out = '';
$regexp = '/^('.
// '[\x00-\x7F]'. // UTF8-1
'|[\xC2-\xDF][\x80-\xBF]'. // UTF8-2
'|\xE0[\xA0-\xBF][\x80-\xBF]'. // UTF8-3
'|[\xE1-\xEC][\x80-\xBF][\x80-\xBF]'. // UTF8-3
'|\xED[\x80-\x9F][\x80-\xBF]'. // UTF8-3
'|[\xEE-\xEF][\x80-\xBF][\x80-\xBF]'. // UTF8-3
'|\xF0[\x90-\xBF][\x80-\xBF][\x80-\xBF]'. // UTF8-4
'|[\xF1-\xF3][\x80-\xBF][\x80-\xBF][\x80-\xBF]'.// UTF8-4
'|\xF4[\x80-\x8F][\x80-\xBF][\x80-\xBF]'. // UTF8-4
')$/';
for ($i = 0, $len = strlen($input); $i < $len; $i++) {
$chr = $input[$i];
$ord = ord($chr);
// 1-byte character
if ($ord <= 0x7F) {
if ($seq !== '') {
$out .= preg_match($regexp, $seq) ? $seq : '';
$seq = '';
}
$out .= $chr;
}
// first byte of multibyte sequence
else if ($ord >= 0xC0) {
if ($seq !== '') {
$out .= preg_match($regexp, $seq) ? $seq : '';
$seq = '';
}
$seq = $chr;
}
// next byte of multibyte sequence
else if ($seq !== '') {
$seq .= $chr;
}
}
if ($seq !== '') {
$out .= preg_match($regexp, $seq) ? $seq : '';
}
return $out;
}
}
diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php
index 109bcca7e..8530160fe 100644
--- a/program/lib/Roundcube/rcube_config.php
+++ b/program/lib/Roundcube/rcube_config.php
@@ -1,941 +1,943 @@
<?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: |
| Class to read configuration settings |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Configuration class for Roundcube
*
* @package Framework
* @subpackage Core
*/
class rcube_config
{
const DEFAULT_SKIN = 'elastic';
public $system_skin = 'elastic';
private $env = '';
- private $paths = array();
- private $prop = array();
- private $errors = array();
- private $userprefs = array();
- private $immutable = array();
+ private $paths = [];
+ private $prop = [];
+ private $errors = [];
+ private $userprefs = [];
+ private $immutable = [];
private $client_tz;
/**
* Renamed options
*
* @var array
*/
- private $legacy_props = array(
+ private $legacy_props = [
// new name => old name
'mail_pagesize' => 'pagesize',
'addressbook_pagesize' => 'pagesize',
'reply_mode' => 'top_posting',
'refresh_interval' => 'keep_alive',
'min_refresh_interval' => 'min_keep_alive',
'messages_cache_ttl' => 'message_cache_lifetime',
'mail_read_time' => 'preview_pane_mark_read',
'session_debug' => 'log_session',
'redundant_attachments_cache_ttl' => 'redundant_attachments_memcache_ttl',
- );
+ ];
/**
* Object constructor
*
* @param string $env Environment suffix for config files to load
*/
public function __construct($env = '')
{
$this->env = $env;
if ($paths = getenv('RCUBE_CONFIG_PATH')) {
$this->paths = explode(PATH_SEPARATOR, $paths);
// make all paths absolute
foreach ($this->paths as $i => $path) {
if (!rcube_utils::is_absolute_path($path)) {
if ($realpath = realpath(RCUBE_INSTALL_PATH . $path)) {
$this->paths[$i] = unslashify($realpath) . '/';
}
else {
unset($this->paths[$i]);
}
}
else {
$this->paths[$i] = unslashify($path) . '/';
}
}
}
if (defined('RCUBE_CONFIG_DIR') && !in_array(RCUBE_CONFIG_DIR, $this->paths)) {
$this->paths[] = RCUBE_CONFIG_DIR;
}
if (empty($this->paths)) {
$this->paths[] = RCUBE_INSTALL_PATH . 'config/';
}
$this->load();
// Defaults, that we do not require you to configure,
// but contain information that is used in various locations in the code:
if (empty($this->prop['contactlist_fields'])) {
- $this->set('contactlist_fields', array('name', 'firstname', 'surname', 'email'));
+ $this->set('contactlist_fields', ['name', 'firstname', 'surname', 'email']);
}
}
/**
* @brief Guess the type the string may fit into.
*
* Look inside the string to determine what type might be best as a container.
*
- * @param mixed $value The value to inspect
+ * @param string $value The value to inspect
*
- * @return The guess at the type.
+ * @return string The guessed type.
*/
private function guess_type($value)
{
$type = 'string';
// array requires hint to be passed.
if (preg_match('/^[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?$/', $value)) {
- $type = 'double';
+ $type = 'float';
}
else if (preg_match('/^\d+$/', $value)) {
$type = 'integer';
}
else if (preg_match('/^(t(rue)?)|(f(alse)?)$/i', $value)) {
$type = 'boolean';
}
return $type;
}
/**
* @brief Parse environment variable into PHP type.
*
* Perform an appropriate parsing of the string to create the desired PHP type.
*
* @param string $string String to parse into PHP type
* @param string $type Type of value to return
*
- * @return Appropriately typed interpretation of $string.
+ * @return mixed Appropriately typed interpretation of $string.
*/
private function parse_env($string, $type)
{
- $_ = $string;
-
switch ($type) {
case 'boolean':
- $_ = (boolean) $_;
- break;
+ return (bool) $string;
+
case 'integer':
- $_ = (integer) $_;
- break;
+ return (int) $string;
+
case 'double':
- $_ = (double) $_;
- break;
+ return (float) $string;
+
case 'string':
- break;
+ return $string;
+
case 'array':
- $_ = json_decode($_, true);
- break;
+ return json_decode($string, true);
+
case 'object':
- $_ = json_decode($_, false);
- break;
- case 'resource':
- case 'NULL':
- default:
- $_ = $this->parse_env($_, $this->guess_type($_));
+ return json_decode($string, false);
}
- return $_;
+ return $this->parse_env($string, $this->guess_type($string));
}
/**
* @brief Get environment variable value.
*
* Retrieve an environment variable's value or if it's not found, return the
* provided default value.
*
* @param string $varname Environment variable name
* @param mixed $default_value Default value to return if necessary
* @param string $type Type of value to return
*
* @return mixed Value of the environment variable or default if not found.
*/
private function getenv_default($varname, $default_value, $type = null)
{
$value = getenv($varname);
if ($value === false) {
$value = $default_value;
}
else {
$value = $this->parse_env($value, $type ?: gettype($default_value));
}
return $value;
}
/**
* Load config from local config file
*/
private function load()
{
// Load default settings
if (!$this->load_from_file('defaults.inc.php')) {
$this->errors[] = 'defaults.inc.php was not found.';
}
// load main config file
if (!$this->load_from_file('config.inc.php')) {
// Old configuration files
if (!$this->load_from_file('main.inc.php') || !$this->load_from_file('db.inc.php')) {
$this->errors[] = 'config.inc.php was not found.';
}
else if (rand(1,100) == 10) { // log warning on every 100th request (average)
trigger_error("config.inc.php was not found. Please migrate your config by running bin/update.sh", E_USER_WARNING);
}
}
// load host-specific configuration
$this->load_host_config();
// set skin (with fallback to old 'skin_path' property)
if (empty($this->prop['skin'])) {
if (!empty($this->prop['skin_path'])) {
$this->prop['skin'] = str_replace('skins/', '', unslashify($this->prop['skin_path']));
}
else {
$this->prop['skin'] = self::DEFAULT_SKIN;
}
}
if ($this->prop['skin'] == 'default') {
$this->prop['skin'] = self::DEFAULT_SKIN;
}
$this->system_skin = $this->prop['skin'];
// fix paths
- foreach (array('log_dir' => 'logs', 'temp_dir' => 'temp') as $key => $dir) {
- foreach (array($this->prop[$key], '../' . $this->prop[$key], RCUBE_INSTALL_PATH . $dir) as $path) {
+ foreach (['log_dir' => 'logs', 'temp_dir' => 'temp'] as $key => $dir) {
+ foreach ([$this->prop[$key], '../' . $this->prop[$key], RCUBE_INSTALL_PATH . $dir] as $path) {
if ($path && ($realpath = realpath(unslashify($path)))) {
$this->prop[$key] = $realpath;
break;
}
}
}
// fix default imap folders encoding
- foreach (array('drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox') as $folder) {
+ foreach (['drafts_mbox', 'junk_mbox', 'sent_mbox', 'trash_mbox'] as $folder) {
$this->prop[$folder] = rcube_charset::convert($this->prop[$folder], RCUBE_CHARSET, 'UTF7-IMAP');
}
// set PHP error logging according to config
$error_log = $this->prop['log_driver'] ?: 'file';
if ($error_log == 'file') {
$error_log = $this->prop['log_dir'] . '/errors';
$error_log .= isset($this->prop['log_file_ext']) ? $this->prop['log_file_ext'] : '.log';
}
if ($error_log && $error_log != 'stdout') {
ini_set('error_log', $error_log);
}
// set default screen layouts
- $this->prop['supported_layouts'] = array('widescreen', 'desktop', 'list');
+ $this->prop['supported_layouts'] = ['widescreen', 'desktop', 'list'];
// remove deprecated properties
unset($this->prop['dst_active']);
}
/**
* Load a host-specific config file if configured
* This will merge the host specific configuration with the given one
*/
private function load_host_config()
{
if (empty($this->prop['include_host_config'])) {
return;
}
- foreach (array('HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR') as $key) {
- $fname = null;
- $name = $_SERVER[$key];
-
- if (!$name) {
+ foreach (['HTTP_HOST', 'SERVER_NAME', 'SERVER_ADDR'] as $key) {
+ if (empty($_SERVER[$key])) {
continue;
}
- if (is_array($this->prop['include_host_config'])) {
- $fname = $this->prop['include_host_config'][$name];
+ $fname = null;
+ $name = $_SERVER[$key];
+
+ if (!empty($this->prop['include_host_config']) && is_array($this->prop['include_host_config'])) {
+ if (isset($this->prop['include_host_config'][$name])) {
+ $fname = $this->prop['include_host_config'][$name];
+ }
}
else {
$fname = preg_replace('/[^a-z0-9\.\-_]/i', '', $name) . '.inc.php';
}
if ($fname && $this->load_from_file($fname)) {
return;
}
}
}
/**
* Read configuration from a file
* and merge with the already stored config values
*
* @param string $file Name of the config file to be loaded
*
- * @return booelan True on success, false on failure
+ * @return bool True on success, false on failure
*/
public function load_from_file($file)
{
$success = false;
foreach ($this->resolve_paths($file) as $fpath) {
if ($fpath && is_file($fpath) && is_readable($fpath)) {
// use output buffering, we don't need any output here
ob_start();
include($fpath);
ob_end_clean();
if (isset($config) && is_array($config)) {
$this->merge($config);
$success = true;
}
// deprecated name of config variable
if (isset($rcmail_config) && is_array($rcmail_config)) {
$this->merge($rcmail_config);
$success = true;
}
}
}
return $success;
}
/**
* Helper method to resolve absolute paths to the given config file.
* This also takes the 'env' property into account.
*
- * @param string $file Filename or absolute file path
- * @param boolean $use_env Return -$env file path if exists
+ * @param string $file Filename or absolute file path
+ * @param bool $use_env Return -$env file path if exists
*
* @return array List of candidates in config dir path(s)
*/
public function resolve_paths($file, $use_env = true)
{
- $files = array();
+ $files = [];
$abs_path = rcube_utils::is_absolute_path($file);
foreach ($this->paths as $basepath) {
$realpath = $abs_path ? $file : realpath($basepath . '/' . $file);
// check if <file>-<env>.inc.php exists
if ($use_env && !empty($this->env)) {
$envfile = preg_replace('/\.(inc.php)$/', '-' . $this->env . '.\\1', $file);
$envfile = $abs_path ? $envfile : realpath($basepath . '/' . $envfile);
if (is_file($envfile)) {
$realpath = $envfile;
}
}
if ($realpath) {
$files[] = $realpath;
// no need to continue the loop if an absolute file path is given
if ($abs_path) {
break;
}
}
}
return $files;
}
/**
* Getter for a specific config parameter
*
- * @param string $name Parameter name
- * @param mixed $def Default value if not set
+ * @param string $name Parameter name
+ * @param mixed $def Default value if not set
*
- * @return mixed The requested config value
+ * @return mixed The requested config value
*/
public function get($name, $def = null)
{
if (isset($this->prop[$name])) {
$result = $this->prop[$name];
}
else {
$result = $def;
}
$result = $this->getenv_default('ROUNDCUBE_' . strtoupper($name), $result);
$rcube = rcube::get_instance();
if ($name == 'timezone') {
if (empty($result) || $result == 'auto') {
$result = $this->client_timezone();
}
}
else if ($name == 'client_mimetypes') {
if (!$result && !$def) {
$result = 'text/plain,text/html'
. ',image/jpeg,image/gif,image/png,image/bmp,image/tiff,image/webp'
. ',application/x-javascript,application/pdf,application/x-shockwave-flash';
}
if ($result && is_string($result)) {
$result = explode(',', $result);
}
}
else if ($name == 'layout') {
if (!in_array($result, $this->prop['supported_layouts'])) {
$result = $this->prop['supported_layouts'][0];
}
}
else if ($name == 'collected_senders') {
if (is_bool($result)) {
$result = $result ? rcube_addressbook::TYPE_TRUSTED_SENDER : '';
}
$result = (string) $result;
}
else if ($name == 'collected_recipients') {
if (is_bool($result)) {
$result = $result ? rcube_addressbook::TYPE_RECIPIENT : '';
}
$result = (string) $result;
}
- $plugin = $rcube->plugins->exec_hook('config_get', array(
- 'name' => $name, 'default' => $def, 'result' => $result));
+ $plugin = $rcube->plugins->exec_hook('config_get', [
+ 'name' => $name,
+ 'default' => $def,
+ 'result' => $result
+ ]);
return $plugin['result'];
}
/**
* Setter for a config parameter
*
* @param string $name Parameter name
* @param mixed $value Parameter value
* @param bool $immutable Make the value immutable
*/
public function set($name, $value, $immutable = false)
{
$this->prop[$name] = $value;
if ($immutable) {
$this->immutable[$name] = $value;
}
}
/**
* Override config options with the given values (eg. user prefs)
*
* @param array $prefs Hash array with config props to merge over
*/
public function merge($prefs)
{
$prefs = $this->fix_legacy_props($prefs);
$this->prop = array_merge($this->prop, $prefs, $this->userprefs, $this->immutable);
}
/**
* Merge the given prefs over the current config
* and make sure that they survive further merging.
*
* @param array $prefs Hash array with user prefs
*/
public function set_user_prefs($prefs)
{
$prefs = $this->fix_legacy_props($prefs);
// Honor the dont_override setting for any existing user preferences
$dont_override = $this->get('dont_override');
if (is_array($dont_override) && !empty($dont_override)) {
foreach ($dont_override as $key) {
unset($prefs[$key]);
}
}
if (isset($prefs['skin']) && $prefs['skin'] == 'default') {
$prefs['skin'] = $this->system_skin;
}
$skins_allowed = $this->get('skins_allowed');
if (!empty($prefs['skin']) && !empty($skins_allowed) && !in_array($prefs['skin'], (array) $skins_allowed)) {
unset($prefs['skin']);
}
$this->userprefs = $prefs;
$this->prop = array_merge($this->prop, $prefs);
}
/**
* Getter for all config options.
*
* Unlike get() this method does not resolve any special
* values like e.g. 'timezone'.
*
* It is discouraged to use this method outside of Roundcube core.
*
* @return array Hash array containing all config properties
*/
public function all()
{
$props = $this->prop;
foreach ($props as $prop_name => $prop_value) {
$props[$prop_name] = $this->getenv_default('ROUNDCUBE_' . strtoupper($prop_name), $prop_value);
}
$rcube = rcube::get_instance();
- $plugin = $rcube->plugins->exec_hook('config_get', array(
- 'name' => '*', 'result' => $props));
+ $plugin = $rcube->plugins->exec_hook('config_get', ['name' => '*', 'result' => $props]);
return $plugin['result'];
}
/**
* Some options set as immutable that are also listed
* in dont_override should not be stored permanently
* in user preferences. Here's the list of these
*
* @return array List of transient options
*/
public function transient_options()
{
return array_intersect(array_keys($this->immutable), (array) $this->get('dont_override'));
}
/**
* Special getter for user's timezone offset including DST
*
* @return float Timezone offset (in hours)
* @deprecated
*/
public function get_timezone()
{
if ($tz = $this->get('timezone')) {
try {
$tz = new DateTimeZone($tz);
return $tz->getOffset(new DateTime('now')) / 3600;
}
catch (Exception $e) {
}
}
return 0;
}
/**
* Return requested DES crypto key.
*
* @param string $key Crypto key name
*
* @return string Crypto key
*/
public function get_crypto_key($key)
{
// Bomb out if the requested key does not exist
if (!array_key_exists($key, $this->prop) || empty($this->prop[$key])) {
- rcube::raise_error(array(
- 'code' => 500, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Request for unconfigured crypto key \"$key\""
- ), true, true);
+ rcube::raise_error([
+ 'code' => 500, 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Request for unconfigured crypto key \"$key\""
+ ], true, true);
}
return $this->prop[$key];
}
/**
* Return configured crypto method.
*
* @return string Crypto method
*/
public function get_crypto_method()
{
return $this->get('cipher_method') ?: 'DES-EDE3-CBC';
}
/**
* Try to autodetect operating system and find the correct line endings
*
* @return string The appropriate mail header delimiter
* @deprecated Since 1.3 we don't use mail()
*/
public function header_delimiter()
{
// use the configured delimiter for headers
if (!empty($this->prop['mail_header_delimiter'])) {
$delim = $this->prop['mail_header_delimiter'];
if ($delim == "\n" || $delim == "\r\n") {
return $delim;
}
else {
- rcube::raise_error(array(
- 'code' => 500, 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Invalid mail_header_delimiter setting"
- ), true, false);
+ rcube::raise_error([
+ 'code' => 500, 'file' => __FILE__, 'line' => __LINE__,
+ 'message' => "Invalid mail_header_delimiter setting"
+ ], true, false);
}
}
$php_os = strtolower(substr(PHP_OS, 0, 3));
- if ($php_os == 'win')
+ if ($php_os == 'win') {
return "\r\n";
+ }
- if ($php_os == 'mac')
+ if ($php_os == 'mac') {
return "\r\n";
+ }
return "\n";
}
/**
* Returns list of configured PGP key servers
*
* @return array|null List of keyservers' URLs
*/
public function keyservers()
{
$list = (array) $this->prop['keyservers'];
foreach ($list as $idx => $host) {
if (!preg_match('|^[a-z]+://|', $host)) {
$host = "https://$host";
}
$list[$idx] = slashify($host);
}
return !empty($list) ? $list : null;
}
/**
* Return the mail domain configured for the given host
*
- * @param string $host IMAP host
- * @param boolean $encode If true, domain name will be converted to IDN ASCII
+ * @param string $host IMAP host
+ * @param bool $encode If true, domain name will be converted to IDN ASCII
*
* @return string Resolved SMTP host
*/
- public function mail_domain($host, $encode=true)
+ public function mail_domain($host, $encode = true)
{
$domain = $host;
- if (is_array($this->prop['mail_domain'])) {
- if (isset($this->prop['mail_domain'][$host])) {
- $domain = $this->prop['mail_domain'][$host];
+ if (!empty($this->prop['mail_domain'])) {
+ if (is_array($this->prop['mail_domain'])) {
+ if (isset($this->prop['mail_domain'][$host])) {
+ $domain = $this->prop['mail_domain'][$host];
+ }
+ }
+ else {
+ $domain = rcube_utils::parse_host($this->prop['mail_domain']);
}
- }
- else if (!empty($this->prop['mail_domain'])) {
- $domain = rcube_utils::parse_host($this->prop['mail_domain']);
}
if ($encode) {
$domain = rcube_utils::idn_to_ascii($domain);
}
return $domain;
}
/**
* Getter for error state
*
* @return mixed Error message on error, False if no errors
*/
public function get_error()
{
return empty($this->errors) ? false : implode("\n", $this->errors);
}
/**
* Internal getter for client's (browser) timezone identifier
*/
private function client_timezone()
{
if ($this->client_tz) {
return $this->client_tz;
}
// @TODO: remove this legacy timezone handling in the future
- $props = $this->fix_legacy_props(array('timezone' => $_SESSION['timezone']));
+ $props = $this->fix_legacy_props(['timezone' => $_SESSION['timezone']]);
if (!empty($props['timezone'])) {
// Prevent from using deprecated timezone names
$props['timezone'] = $this->resolve_timezone_alias($props['timezone']);
try {
$tz = new DateTimeZone($props['timezone']);
return $this->client_tz = $tz->getName();
}
catch (Exception $e) { /* gracefully ignore */ }
}
// fallback to server's timezone
return date_default_timezone_get();
}
/**
* Convert legacy options into new ones
*
* @param array $props Hash array with config props
*
* @return array Converted config props
*/
private function fix_legacy_props($props)
{
foreach ($this->legacy_props as $new => $old) {
if (isset($props[$old])) {
if (!isset($props[$new])) {
$props[$new] = $props[$old];
}
unset($props[$old]);
}
}
// convert deprecated numeric timezone value
if (isset($props['timezone']) && is_numeric($props['timezone'])) {
if ($tz = self::timezone_name_from_abbr($props['timezone'])) {
$props['timezone'] = $tz;
}
else {
unset($props['timezone']);
}
}
// translate old `preview_pane` settings to `layout`
if (isset($props['preview_pane']) && !isset($props['layout'])) {
$props['layout'] = $props['preview_pane'] ? 'desktop' : 'list';
unset($props['preview_pane']);
}
// translate old `display_version` settings to `display_product_info`
if (isset($props['display_version']) && !isset($props['display_product_info'])) {
$props['display_product_info'] = $props['display_version'] ? 2 : 1;
unset($props['display_version']);
}
return $props;
}
/**
* timezone_name_from_abbr() replacement. Converts timezone offset
* into timezone name abbreviation.
*
* @param float $offset Timezone offset (in hours)
*
- * @return string Timezone abbreviation
+ * @return string|null Timezone abbreviation
*/
static public function timezone_name_from_abbr($offset)
{
// List of timezones here is not complete - https://bugs.php.net/bug.php?id=44780
if ($tz = timezone_name_from_abbr('', $offset * 3600, 0)) {
return $tz;
}
// try with more complete list (#1489261)
- $timezones = array(
+ $timezones = [
'-660' => "Pacific/Apia",
'-600' => "Pacific/Honolulu",
'-570' => "Pacific/Marquesas",
'-540' => "America/Anchorage",
'-480' => "America/Los_Angeles",
'-420' => "America/Denver",
'-360' => "America/Chicago",
'-300' => "America/New_York",
'-270' => "America/Caracas",
'-240' => "America/Halifax",
'-210' => "Canada/Newfoundland",
'-180' => "America/Sao_Paulo",
'-60' => "Atlantic/Azores",
'0' => "Europe/London",
'60' => "Europe/Paris",
'120' => "Europe/Helsinki",
'180' => "Europe/Moscow",
'210' => "Asia/Tehran",
'240' => "Asia/Dubai",
'270' => "Asia/Kabul",
'300' => "Asia/Karachi",
'330' => "Asia/Kolkata",
'345' => "Asia/Katmandu",
'360' => "Asia/Yekaterinburg",
'390' => "Asia/Rangoon",
'420' => "Asia/Krasnoyarsk",
'480' => "Asia/Shanghai",
'525' => "Australia/Eucla",
'540' => "Asia/Tokyo",
'570' => "Australia/Adelaide",
'600' => "Australia/Melbourne",
'630' => "Australia/Lord_Howe",
'660' => "Asia/Vladivostok",
'690' => "Pacific/Norfolk",
'720' => "Pacific/Auckland",
'765' => "Pacific/Chatham",
'780' => "Pacific/Enderbury",
'840' => "Pacific/Kiritimati",
- );
+ ];
+
+ $key = (string) intval($offset * 60);
- return $timezones[(string) intval($offset * 60)];
+ return !empty($timezones[$key]) ? $timezones[$key] : null;
}
/**
* Replace deprecated timezone name with a valid one.
*
* @param string $tzname Timezone name
*
* @return string Timezone name
*/
static public function resolve_timezone_alias($tzname)
{
// http://www.php.net/manual/en/timezones.others.php
// https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
- $deprecated_timezones = array(
+ $deprecated_timezones = [
'Australia/ACT' => 'Australia/Sydney',
'Australia/LHI' => 'Australia/Lord_Howe',
'Australia/North' => 'Australia/Darwin',
'Australia/NSW' => 'Australia/Sydney',
'Australia/Queensland' => 'Australia/Brisbane',
'Australia/South' => 'Australia/Adelaide',
'Australia/Adelaide' => 'Australia/Hobart',
'Australia/Tasmania' => 'Australia/Hobart',
'Australia/Victoria' => 'Australia/Melbourne',
'Australia/West' => 'Australia/Perth',
'Brazil/Acre' => 'America/Rio_Branco',
'Brazil/DeNoronha' => 'America/Noronha',
'Brazil/East' => 'America/Sao_Paulo',
'Brazil/West' => 'America/Manaus',
'Canada/Atlantic' => 'America/Halifax',
'Canada/Central' => 'America/Winnipeg',
'Canada/Eastern' => 'America/Toronto',
'Canada/Mountain' => 'America/Edmonton',
'Canada/Newfoundland' => 'America/St_Johns',
'Canada/Pacific' => 'America/Vancouver',
'Canada/Saskatchewan' => 'America/Regina',
'Canada/Yukon' => 'America/Whitehorse',
'CET' => 'Europe/Berlin',
'Chile/Continental' => 'America/Santiago',
'Chile/EasterIsland' => 'Pacific/Easter',
'CST6CDT' => 'America/Chicago',
'Cuba' => ' America/Havana',
'EET' => 'Europe/Berlin',
'Egypt' => 'Africa/Cairo',
'Eire' => 'Europe/Dublin',
'EST' => 'America/New_York',
'EST5EDT' => 'America/New_York',
'Factory' => 'UTC', // ?
'GB' => 'Europe/London',
'GB-Eire' => 'Europe/London',
'GMT' => 'UTC',
'GMT+0' => 'UTC',
'GMT-0' => 'UTC',
'GMT0' => 'UTC',
'Greenwich' => 'UTC',
'Hongkong' => 'Asia/Hong_Kong',
'HST' => 'Pacific/Honolulu',
'Iceland' => 'Atlantic/Reykjavik',
'Iran' => 'Asia/Tehran',
'Israel' => 'Asia/Jerusalem',
'Jamaica' => 'America/Jamaica',
'Japan' => 'Asia/Tokyo',
'Kwajalein' => 'Pacific/Kwajalein',
'Libya' => 'Africa/Tripoli',
'MET' => 'Europe/Berlin',
'Mexico/BajaNorte' => 'America/Tijuana',
'Mexico/BajaSur' => 'America/Mazatlan',
'Mexico/General' => 'America/Mexico_City',
'MST' => 'America/Denver',
'MST7MDT' => 'America/Denver',
'Navajo' => 'America/Denver',
'NZ' => 'Pacific/Auckland',
'NZ-CHAT' => 'Pacific/Chatham',
'Poland' => 'Europe/Warsaw',
'Portugal' => 'Europe/Lisbon',
'PRC' => 'Asia/Shanghai',
'PST8PDT' => 'America/Los_Angeles',
'ROC' => 'Asia/Taipei',
'ROK' => 'Asia/Seoul',
'Singapore' => 'Asia/Singapore',
'Turkey' => 'Europe/Istanbul',
'UCT' => 'UTC',
'Universal' => 'UTC',
'US/Alaska' => 'America/Anchorage',
'US/Aleutian' => 'America/Adak',
'US/Arizona' => 'America/Phoenix',
'US/Central' => 'America/Chicago',
'US/East-Indiana' => 'America/Indiana/Indianapolis',
'US/Eastern' => 'America/New_York',
'US/Hawaii' => 'Pacific/Honolulu',
'US/Indiana-Starke' => 'America/Indiana/Knox',
'US/Michigan' => 'America/Detroit',
'US/Mountain' => 'America/Denver',
'US/Pacific' => 'America/Los_Angeles',
'US/Pacific-New' => 'America/Los_Angeles',
'US/Samoa' => 'Pacific/Pago_Pago',
'W-SU' => 'Europe/Moscow',
'WET' => 'Europe/Berlin',
'Z' => 'UTC',
'Zulu' => 'UTC',
// Some of these Etc/X zones are not deprecated, but still problematic
'Etc/GMT' => 'UTC',
'Etc/GMT+0' => 'UTC',
'Etc/GMT+1' => 'Atlantic/Azores',
'Etc/GMT+10' => 'Pacific/Honolulu',
'Etc/GMT+11' => 'Pacific/Midway',
'Etc/GMT+12' => 'Pacific/Auckland',
'Etc/GMT+2' => 'America/Noronha',
'Etc/GMT+3' => 'America/Argentina/Buenos_Aires',
'Etc/GMT+4' => 'America/Manaus',
'Etc/GMT+5' => 'America/New_York',
'Etc/GMT+6' => 'America/Chicago',
'Etc/GMT+7' => 'America/Denver',
'Etc/GMT+8' => 'America/Los_Angeles',
'Etc/GMT+9' => 'America/Anchorage',
'Etc/GMT-0' => 'UTC',
'Etc/GMT-1' => 'Europe/Berlin',
'Etc/GMT-10' => 'Australia/Sydney',
'Etc/GMT-11' => 'Pacific/Norfolk',
'Etc/GMT-12' => 'Pacific/Auckland',
'Etc/GMT-13' => 'Pacific/Apia',
'Etc/GMT-14' => 'Pacific/Kiritimati',
'Etc/GMT-2' => 'Africa/Cairo',
'Etc/GMT-3' => 'Europe/Moscow',
'Etc/GMT-4' => 'Europe/Samara',
'Etc/GMT-5' => 'Asia/Yekaterinburg',
'Etc/GMT-6' => 'Asia/Almaty',
'Etc/GMT-7' => 'Asia/Bangkok',
'Etc/GMT-8' => 'Asia/Hong_Kong',
'Etc/GMT-9' => 'Asia/Tokyo',
'Etc/GMT0' => 'UTC',
'Etc/Greenwich' => 'UTC',
'Etc/UCT' => 'UTC',
'Etc/Universal' => 'UTC',
'Etc/UTC' => 'UTC',
'Etc/Zulu' => 'UTC',
- );
+ ];
- return $deprecated_timezones[$tzname] ?: $tzname;
+ return isset($deprecated_timezones[$tzname]) ? $deprecated_timezones[$tzname] : $tzname;
}
}
diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php
index 51c9fa3b6..cb7475774 100644
--- a/program/lib/Roundcube/rcube_contacts.php
+++ b/program/lib/Roundcube/rcube_contacts.php
@@ -1,1078 +1,1091 @@
<?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: |
| Interface to the local address book database |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
+-----------------------------------------------------------------------+
*/
/**
* Model class for the local address book database
*
* @package Framework
* @subpackage Addressbook
*/
class rcube_contacts extends rcube_addressbook
{
// protected for backward compat. with some plugins
protected $db_name = 'contacts';
protected $db_groups = 'contactgroups';
protected $db_groupmembers = 'contactgroupmembers';
- protected $vcard_fieldmap = array();
+ protected $vcard_fieldmap = [];
/**
* Store database connection.
*
* @var rcube_db
*/
protected $db = null;
protected $user_id = 0;
protected $filter = null;
protected $result = null;
protected $cache;
- protected $table_cols = array('name', 'email', 'firstname', 'surname');
- protected $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'nickname',
+ protected $table_cols = ['name', 'email', 'firstname', 'surname'];
+ protected $fulltext_cols = ['name', 'firstname', 'surname', 'middlename', 'nickname',
'jobtitle', 'organization', 'department', 'maidenname', 'email', 'phone',
- 'address', 'street', 'locality', 'zipcode', 'region', 'country', 'website', 'im', 'notes');
+ 'address', 'street', 'locality', 'zipcode', 'region', 'country', 'website', 'im', 'notes'];
// public properties
public $primary_key = 'contact_id';
public $name;
public $readonly = false;
public $groups = true;
public $undelete = true;
public $list_page = 1;
public $page_size = 10;
public $group_id = 0;
public $ready = false;
- public $coltypes = array('name', 'firstname', 'surname', 'middlename', 'prefix', 'suffix', 'nickname',
+ public $coltypes = ['name', 'firstname', 'surname', 'middlename', 'prefix', 'suffix', 'nickname',
'jobtitle', 'organization', 'department', 'assistant', 'manager',
'gender', 'maidenname', 'spouse', 'email', 'phone', 'address',
- 'birthday', 'anniversary', 'website', 'im', 'notes', 'photo');
- public $date_cols = array('birthday', 'anniversary');
+ 'birthday', 'anniversary', 'website', 'im', 'notes', 'photo'];
+ public $date_cols = ['birthday', 'anniversary'];
const SEPARATOR = ',';
/**
* Object constructor
*
- * @param object $dbconn Instance of the rcube_db class
- * @param integer $user User-ID
+ * @param object $dbconn Instance of the rcube_db class
+ * @param int $user User-ID
*/
function __construct($dbconn, $user)
{
$this->db = $dbconn;
$this->user_id = $user;
$this->ready = $this->db && !$this->db->is_error();
}
/**
* Returns addressbook name
+ *
+ * @return string
*/
function get_name()
{
return $this->name;
}
/**
* Save a search string for future listings
*
* @param string $filter SQL params to use in listing method
*/
function set_search_set($filter)
{
$this->filter = $filter;
- $this->cache = null;
+ $this->cache = null;
}
/**
* Getter for saved search properties
*
* @return mixed Search properties used by this class
*/
function get_search_set()
{
return $this->filter;
}
/**
* Setter for the current group
* (empty, has to be re-implemented by extending class)
*/
function set_group($gid)
{
$this->group_id = $gid;
$this->cache = null;
}
/**
* Reset all saved results and search parameters
*/
function reset()
{
$this->result = null;
$this->filter = null;
$this->cache = null;
}
/**
* List all active contact groups of this source
*
* @param string $search Search string to match group name
* @param int $mode Matching mode. Sum of rcube_addressbook::SEARCH_*
*
* @return array Indexed list of contact groups, each a hash array
*/
function list_groups($search = null, $mode = 0)
{
- $results = array();
+ $results = [];
if (!$this->groups) {
return $results;
}
$sql_filter = '';
if ($search) {
if ($mode & rcube_addressbook::SEARCH_STRICT) {
$sql_filter = $this->db->ilike('name', $search);
}
else if ($mode & rcube_addressbook::SEARCH_PREFIX) {
$sql_filter = $this->db->ilike('name', $search . '%');
}
else {
$sql_filter = $this->db->ilike('name', '%' . $search . '%');
}
$sql_filter = " AND $sql_filter";
}
$sql_result = $this->db->query(
"SELECT * FROM " . $this->db->table_name($this->db_groups, true)
. " WHERE `del` <> 1 AND `user_id` = ?" . $sql_filter
. " ORDER BY `name`",
- $this->user_id);
+ $this->user_id
+ );
while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$sql_arr['ID'] = $sql_arr['contactgroup_id'];
$results[] = $sql_arr;
}
return $results;
}
/**
* Get group properties such as name and email address(es)
*
* @param string $group_id Group identifier
*
* @return array Group properties as hash array
*/
function get_group($group_id)
{
$sql_result = $this->db->query(
"SELECT * FROM " . $this->db->table_name($this->db_groups, true)
. " WHERE `del` <> 1 AND `contactgroup_id` = ? AND `user_id` = ?",
- $group_id, $this->user_id);
+ $group_id, $this->user_id
+ );
if ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$sql_arr['ID'] = $sql_arr['contactgroup_id'];
return $sql_arr;
}
return null;
}
/**
* List the current set of contact records
*
- * @param array List of cols to show, Null means all
- * @param int Only return this number of records, use negative values for tail
- * @param boolean True to skip the count query (select only)
+ * @param array List of cols to show, Null means all
+ * @param int Only return this number of records, use negative values for tail
+ * @param bool True to skip the count query (select only)
*
* @return array Indexed list of contact records, each a hash array
*/
function list_records($cols = null, $subset = 0, $nocount = false)
{
if ($nocount || $this->list_page <= 1) {
// create dummy result, we don't need a count now
$this->result = new rcube_result_set();
} else {
// count all records
$this->result = $this->count();
}
$start_row = $subset < 0 ? $this->result->first + $this->page_size + $subset : $this->result->first;
$length = $subset != 0 ? abs($subset) : $this->page_size;
$join = '';
if ($this->group_id) {
$join = " LEFT JOIN " . $this->db->table_name($this->db_groupmembers, true) . " AS m".
" ON (m.`contact_id` = c.`".$this->primary_key."`)";
}
$order_col = in_array($this->sort_col, $this->table_cols) ? $this->sort_col : 'name';
- $order_cols = array("c.`$order_col`");
+ $order_cols = ["c.`$order_col`"];
if ($order_col == 'firstname') {
$order_cols[] = 'c.`surname`';
}
else if ($order_col == 'surname') {
$order_cols[] = 'c.`firstname`';
}
if ($order_col != 'name') {
$order_cols[] = 'c.`name`';
}
$order_cols[] = 'c.`email`';
$sql_result = $this->db->limitquery(
"SELECT * FROM " . $this->db->table_name($this->db_name, true) . " AS c" .
$join .
" WHERE c.`del` <> 1" .
" AND c.`user_id` = ?" .
($this->group_id ? " AND m.`contactgroup_id` = ?" : "").
($this->filter ? " AND ".$this->filter : "") .
" ORDER BY ". $this->db->concat($order_cols) . " " . $this->sort_order,
$start_row,
$length,
$this->user_id,
$this->group_id
);
// determine whether we have to parse the vcard or if only db cols are requested
$read_vcard = !$cols || count(array_intersect($cols, $this->table_cols)) < count($cols);
while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$sql_arr['ID'] = $sql_arr[$this->primary_key];
if ($read_vcard) {
$sql_arr = $this->convert_db_data($sql_arr);
}
else {
- $sql_arr['email'] = $sql_arr['email'] ? explode(self::SEPARATOR, $sql_arr['email']) : array();
+ $sql_arr['email'] = $sql_arr['email'] ? explode(self::SEPARATOR, $sql_arr['email']) : [];
$sql_arr['email'] = array_map('trim', $sql_arr['email']);
}
$this->result->add($sql_arr);
}
$cnt = count($this->result->records);
// update counter
if ($nocount) {
$this->result->count = $cnt;
}
else if ($this->list_page <= 1) {
if ($cnt < $this->page_size && $subset == 0) {
$this->result->count = $cnt;
}
else if (isset($this->cache['count'])) {
$this->result->count = $this->cache['count'];
}
else {
$this->result->count = $this->_count();
}
}
return $this->result;
}
/**
* Search contacts
*
- * @param mixed $fields The field name or array of field names to search in
- * @param mixed $value Search value (or array of values when $fields is array)
- * @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*
- * @param boolean $select True if results are requested, False if count only
- * @param boolean $nocount True to skip the count query (select only)
- * @param array $required List of fields that cannot be empty
+ * @param mixed $fields The field name or array of field names to search in
+ * @param mixed $value Search value (or array of values when $fields is array)
+ * @param int $mode Search mode. Sum of rcube_addressbook::SEARCH_*
+ * @param bool $select True if results are requested, False if count only
+ * @param bool $nocount True to skip the count query (select only)
+ * @param array $required List of fields that cannot be empty
*
* @return object rcube_result_set Contact records and 'count' value
*/
- function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = array())
+ function search($fields, $value, $mode = 0, $select = true, $nocount = false, $required = [])
{
if (!is_array($required) && !empty($required)) {
- $required = array($required);
+ $required = [$required];
}
- $where = $post_search = array();
+ $where = $post_search = [];
$mode = intval($mode);
// direct ID search
if ($fields == 'ID' || $fields == $this->primary_key) {
$ids = !is_array($value) ? explode(self::SEPARATOR, $value) : $value;
$ids = $this->db->array2list($ids, 'integer');
$where[] = 'c.' . $this->primary_key.' IN ('.$ids.')';
}
else if (is_array($value)) {
- foreach ((array)$fields as $idx => $col) {
+ foreach ((array) $fields as $idx => $col) {
$val = $value[$idx];
if (!strlen($val)) {
continue;
}
// table column
if (in_array($col, $this->table_cols)) {
$where[] = $this->fulltext_sql_where($val, $mode, $col);
}
// vCard field
else {
if (in_array($col, $this->fulltext_cols)) {
$where[] = $this->fulltext_sql_where($val, $mode, 'words');
}
$post_search[$col] = mb_strtolower($val);
}
}
}
// fulltext search in all fields
else if ($fields == '*') {
$where[] = $this->fulltext_sql_where($value, $mode, 'words');
}
else {
// require each word in to be present in one of the fields
$words = ($mode & rcube_addressbook::SEARCH_STRICT) ? array($value) : rcube_utils::tokenize_string($value, 1);
foreach ($words as $word) {
- $groups = array();
- foreach ((array)$fields as $idx => $col) {
+ $groups = [];
+ foreach ((array) $fields as $idx => $col) {
$groups[] = $this->fulltext_sql_where($word, $mode, $col);
}
$where[] = '(' . implode(' OR ', $groups) . ')';
}
}
foreach (array_intersect($required, $this->table_cols) as $col) {
$where[] = $this->db->quote_identifier($col).' <> '.$this->db->quote('');
}
$required = array_diff($required, $this->table_cols);
if (!empty($where)) {
// use AND operator for advanced searches
$where = implode(' AND ', $where);
}
// Post-searching in vCard data fields
// we will search in all records and then build a where clause for their IDs
if (!empty($post_search) || !empty($required)) {
- $ids = array(0);
+ $ids = [0];
// build key name regexp
$regexp = '/^(' . implode('|', array_keys($post_search)) . ')(?:.*)$/';
// use initial WHERE clause, to limit records number if possible
- if (!empty($where))
+ if (!empty($where)) {
$this->set_search_set($where);
+ }
// count result pages
$cnt = $this->count()->count;
$pages = ceil($cnt / $this->page_size);
$scnt = !empty($post_search) ? count($post_search) : 0;
// get (paged) result
for ($i=0; $i<$pages; $i++) {
$this->list_records(null, $i, true);
while ($row = $this->result->next()) {
$id = $row[$this->primary_key];
- $found = array();
+ $found = [];
if (!empty($post_search)) {
foreach (preg_grep($regexp, array_keys($row)) as $col) {
$pos = strpos($col, ':');
$colname = $pos ? substr($col, 0, $pos) : $col;
$search = $post_search[$colname];
- foreach ((array)$row[$col] as $value) {
+ foreach ((array) $row[$col] as $value) {
if ($this->compare_search_value($colname, $value, $search, $mode)) {
$found[$colname] = true;
break;
}
}
}
}
// check if required fields are present
if (!empty($required)) {
foreach ($required as $req) {
$hit = false;
foreach (array_keys($row) as $c) {
if ($c === $req || strpos($c, $req.':') === 0) {
if ((is_string($row[$c]) && strlen($row[$c])) || !empty($row[$c])) {
$hit = true;
break;
}
}
}
if (!$hit) {
continue 2;
}
}
}
// all fields match
if (count($found) >= $scnt) {
$ids[] = $id;
}
}
}
// build WHERE clause
$ids = $this->db->array2list($ids, 'integer');
$where = 'c.`' . $this->primary_key.'` IN ('.$ids.')';
// reset counter
unset($this->cache['count']);
// when we know we have an empty result
if ($ids == '0') {
$this->set_search_set($where);
return ($this->result = new rcube_result_set(0, 0));
}
}
if (!empty($where)) {
$this->set_search_set($where);
- if ($select)
+ if ($select) {
$this->list_records(null, 0, $nocount);
- else
+ }
+ else {
$this->result = $this->count();
+ }
}
return $this->result;
}
/**
* Helper method to compose SQL where statements for fulltext searching
*/
protected function fulltext_sql_where($value, $mode, $col = 'words', $bool = 'AND')
{
$AS = $col == 'words' ? ' ' : self::SEPARATOR;
- $words = $col == 'words' ? rcube_utils::normalize_string($value, true, 1) : array($value);
+ $words = $col == 'words' ? rcube_utils::normalize_string($value, true, 1) : [$value];
- $where = array();
+ $where = [];
foreach ($words as $word) {
if ($mode & rcube_addressbook::SEARCH_STRICT) {
$where[] = '(' . $this->db->ilike($col, $word)
. ' OR ' . $this->db->ilike($col, $word . $AS . '%')
. ' OR ' . $this->db->ilike($col, '%' . $AS . $word . $AS . '%')
. ' OR ' . $this->db->ilike($col, '%' . $AS . $word) . ')';
}
else if ($mode & rcube_addressbook::SEARCH_PREFIX) {
$where[] = '(' . $this->db->ilike($col, $word . '%')
. ' OR ' . $this->db->ilike($col, '%' . $AS . $word . '%') . ')';
}
else {
$where[] = $this->db->ilike($col, '%' . $word . '%');
}
}
return count($where) ? '(' . implode(" $bool ", $where) . ')' : '';
}
/**
* Count number of available contacts in database
*
* @return rcube_result_set Result object
*/
function count()
{
$count = isset($this->cache['count']) ? $this->cache['count'] : $this->_count();
return new rcube_result_set($count, ($this->list_page-1) * $this->page_size);
}
/**
* Count number of available contacts in database
*
* @return int Contacts count
*/
protected function _count()
{
$join = null;
if ($this->group_id) {
$join = " LEFT JOIN " . $this->db->table_name($this->db_groupmembers, true) . " AS m".
" ON (m.`contact_id` = c.`".$this->primary_key."`)";
}
// count contacts for this user
$sql_result = $this->db->query(
"SELECT COUNT(c.`contact_id`) AS cnt".
" FROM " . $this->db->table_name($this->db_name, true) . " AS c".
$join.
" WHERE c.`del` <> 1".
" AND c.`user_id` = ?".
($this->group_id ? " AND m.`contactgroup_id` = ?" : "").
($this->filter ? " AND (".$this->filter.")" : ""),
$this->user_id,
$this->group_id
);
$sql_arr = $this->db->fetch_assoc($sql_result);
- $this->cache['count'] = (int) $sql_arr['cnt'];
+ $this->cache['count'] = !empty($sql_arr) ? (int) $sql_arr['cnt'] : 0;
return $this->cache['count'];
}
/**
* Return the last result set
*
* @return mixed Result array or NULL if nothing selected yet
*/
function get_result()
{
return $this->result;
}
/**
* Get a specific contact record
*
* @param mixed $id Record identifier(s)
* @param bool $assoc Enables returning associative array
*
* @return rcube_result_set|array Result object with all record fields
*/
function get_record($id, $assoc = false)
{
// return cached result
if ($this->result && ($first = $this->result->first()) && $first[$this->primary_key] == $id) {
return $assoc ? $first : $this->result;
}
$this->db->query(
"SELECT * FROM " . $this->db->table_name($this->db_name, true).
" WHERE `contact_id` = ?".
" AND `user_id` = ?".
" AND `del` <> 1",
$id,
$this->user_id
);
$this->result = null;
if ($sql_arr = $this->db->fetch_assoc()) {
$record = $this->convert_db_data($sql_arr);
$this->result = new rcube_result_set(1);
$this->result->add($record);
}
return $assoc && !empty($record) ? $record : $this->result;
}
/**
* Get group assignments of a specific contact record
*
* @param mixed $id Record identifier
*
* @return array List of assigned groups as ID=>Name pairs
*/
function get_record_groups($id)
{
- $results = array();
+ $results = [];
if (!$this->groups) {
return $results;
}
$sql_result = $this->db->query(
"SELECT cgm.`contactgroup_id`, cg.`name` "
. " FROM " . $this->db->table_name($this->db_groupmembers, true) . " AS cgm"
. " LEFT JOIN " . $this->db->table_name($this->db_groups, true) . " AS cg"
. " ON (cgm.`contactgroup_id` = cg.`contactgroup_id` AND cg.`del` <> 1)"
. " WHERE cgm.`contact_id` = ?",
$id
);
while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$results[$sql_arr['contactgroup_id']] = $sql_arr['name'];
}
return $results;
}
/**
* Check the given data before saving.
* If input not valid, the message to display can be fetched using get_error()
*
- * @param array &$save_data Associative array with data to save
- * @param boolean $autofix Try to fix/complete record automatically
+ * @param array &$save_data Associative array with data to save
+ * @param bool $autofix Try to fix/complete record automatically
*
- * @return boolean True if input is valid, False if not.
+ * @return bool True if input is valid, False if not.
*/
public function validate(&$save_data, $autofix = false)
{
// validate e-mail addresses
$valid = parent::validate($save_data, $autofix);
// require at least some name or email
if ($valid) {
$name = (isset($save_data['firstname']) ? $save_data['firstname'] : '')
. (isset($save_data['surname']) ? $save_data['surname'] : '')
. (isset($save_data['name']) ? $save_data['name'] : '');
if (!strlen($name) && !count(array_filter($this->get_col_values('email', $save_data, true)))) {
$this->set_error(self::ERROR_VALIDATE, 'nonamewarning');
$valid = false;
}
}
return $valid;
}
/**
* Create a new contact record
*
* @param array $save_data Associative array with save data
* @param bool $check Enables validity checks
*
- * @return integer|boolean The created record ID on success, False on error
+ * @return int|bool The created record ID on success, False on error
*/
function insert($save_data, $check = false)
{
if (!is_array($save_data)) {
return false;
}
$insert_id = $existing = false;
if ($check) {
foreach ($save_data as $col => $values) {
if (strpos($col, 'email') === 0) {
foreach ((array)$values as $email) {
if ($existing = $this->search('email', $email, false, false))
break 2;
}
}
}
}
$save_data = $this->convert_save_data($save_data);
- $a_insert_cols = $a_insert_values = array();
+ $a_insert_cols = $a_insert_values = [];
foreach ($save_data as $col => $value) {
$a_insert_cols[] = $this->db->quote_identifier($col);
$a_insert_values[] = $this->db->quote($value);
}
if ((empty($existing) || empty($existing->count)) && !empty($a_insert_cols)) {
$this->db->query(
"INSERT INTO " . $this->db->table_name($this->db_name, true)
. " (`user_id`, `changed`, `del`, " . implode(', ', $a_insert_cols) . ")"
. " VALUES (" . intval($this->user_id) . ", " . $this->db->now() . ", 0, " . implode(', ', $a_insert_values) . ")"
);
$insert_id = $this->db->insert_id($this->db_name);
}
$this->cache = null;
return $insert_id;
}
/**
* Update a specific contact record
*
* @param mixed $id Record identifier
* @param array $save_cols Associative array with save data
*
- * @return boolean True on success, False on error
+ * @return bool True on success, False on error
*/
function update($id, $save_cols)
{
$updated = false;
- $write_sql = array();
+ $write_sql = [];
$record = $this->get_record($id, true);
$save_cols = $this->convert_save_data($save_cols, $record);
foreach ($save_cols as $col => $value) {
$write_sql[] = sprintf("%s=%s", $this->db->quote_identifier($col), $this->db->quote($value));
}
if (!empty($write_sql)) {
$this->db->query(
"UPDATE " . $this->db->table_name($this->db_name, true)
. " SET `changed` = " . $this->db->now() . ", " . implode(', ', $write_sql)
. " WHERE `contact_id` = ?"
. " AND `user_id` = ?"
. " AND `del` <> 1",
$id,
$this->user_id
);
$updated = $this->db->affected_rows();
$this->result = null; // clear current result (from get_record())
}
return !empty($updated);
}
/**
* Convert data stored in the database into output format
*/
private function convert_db_data($sql_arr)
{
- $record = array();
- $record['ID'] = $sql_arr[$this->primary_key];
+ $record = [
+ 'ID' => $sql_arr[$this->primary_key]
+ ];
if ($sql_arr['vcard']) {
unset($sql_arr['email']);
$vcard = new rcube_vcard($sql_arr['vcard'], RCUBE_CHARSET, false, $this->vcard_fieldmap);
$record += $vcard->get_assoc() + $sql_arr;
}
else {
$record += $sql_arr;
$record['email'] = explode(self::SEPARATOR, $record['email']);
$record['email'] = array_map('trim', $record['email']);
}
return $record;
}
/**
* Convert input data for storing in the database
*/
- private function convert_save_data($save_data, $record = array())
+ private function convert_save_data($save_data, $record = [])
{
- $out = array();
+ $out = [];
$words = '';
if (!empty($record['vcard'])) {
$vcard = $record['vcard'];
}
else if (!empty($save_data['vcard'])) {
$vcard = $save_data['vcard'];
}
else {
$vcard = '';
}
// copy values into vcard object
$vcard = new rcube_vcard($vcard, RCUBE_CHARSET, false, $this->vcard_fieldmap);
$vcard->reset();
// don't store groups in vCard (#1490277)
$vcard->set('groups', null);
unset($save_data['groups']);
foreach ($save_data as $key => $values) {
list($field, $section) = rcube_utils::explode(':', $key);
$fulltext = in_array($field, $this->fulltext_cols);
// avoid casting DateTime objects to array
if (is_object($values) && is_a($values, 'DateTime')) {
- $values = array(0 => $values);
+ $values = [$values];
}
- foreach ((array)$values as $value) {
- if (isset($value))
+ foreach ((array) $values as $value) {
+ if (isset($value)) {
$vcard->set($field, $value, $section);
- if ($fulltext && is_array($value))
+ }
+ if ($fulltext && is_array($value)) {
$words .= ' ' . rcube_utils::normalize_string(implode(' ', $value));
- else if ($fulltext && strlen($value) >= 3)
+ }
+ else if ($fulltext && strlen($value) >= 3) {
$words .= ' ' . rcube_utils::normalize_string($value);
+ }
}
}
+
$out['vcard'] = $vcard->export(false);
foreach ($this->table_cols as $col) {
$key = $col;
if (!isset($save_data[$key])) {
$key .= ':home';
}
if (isset($save_data[$key])) {
if (is_array($save_data[$key])) {
$out[$col] = implode(self::SEPARATOR, $save_data[$key]);
}
else {
$out[$col] = $save_data[$key];
}
}
}
// save all e-mails in database column
$out['email'] = implode(self::SEPARATOR, $vcard->email);
// join words for fulltext search
$out['words'] = implode(' ', array_unique(explode(' ', $words)));
return $out;
}
/**
* Mark one or more contact records as deleted
*
- * @param array $ids Record identifiers
- * @param boolean $force Remove record(s) irreversible (unsupported)
+ * @param array $ids Record identifiers
+ * @param bool $force Remove record(s) irreversible (unsupported)
*
* @return int Number of removed records
*/
function delete($ids, $force = true)
{
if (!is_array($ids)) {
$ids = explode(self::SEPARATOR, $ids);
}
$ids = $this->db->array2list($ids, 'integer');
// flag record as deleted (always)
$this->db->query(
"UPDATE " . $this->db->table_name($this->db_name, true).
" SET `del` = 1, `changed` = ".$this->db->now().
" WHERE `user_id` = ?".
" AND `contact_id` IN ($ids)",
$this->user_id
);
$this->cache = null;
return $this->db->affected_rows();
}
/**
* Undelete one or more contact records
*
* @param array $ids Record identifiers
*
* @return int Number of undeleted contact records
*/
function undelete($ids)
{
if (!is_array($ids)) {
$ids = explode(self::SEPARATOR, $ids);
}
$ids = $this->db->array2list($ids, 'integer');
// clear deleted flag
$this->db->query(
"UPDATE " . $this->db->table_name($this->db_name, true).
" SET `del` = 0, `changed` = ".$this->db->now().
" WHERE `user_id` = ?".
" AND `contact_id` IN ($ids)",
$this->user_id
);
$this->cache = null;
return $this->db->affected_rows();
}
/**
* Remove all records from the database
*
* @param bool $with_groups Remove also groups
*
* @return int Number of removed records
*/
function delete_all($with_groups = false)
{
$this->cache = null;
$now = $this->db->now();
$this->db->query("UPDATE " . $this->db->table_name($this->db_name, true)
. " SET `del` = 1, `changed` = $now"
. " WHERE `user_id` = ?", $this->user_id);
$count = $this->db->affected_rows();
if ($with_groups) {
$this->db->query("UPDATE " . $this->db->table_name($this->db_groups, true)
. " SET `del` = 1, `changed` = $now"
. " WHERE `user_id` = ?", $this->user_id);
$count += $this->db->affected_rows();
}
return $count;
}
/**
* Create a contact group with the given name
*
* @param string $name The group name
*
- * @return mixed False on error, array with record props in success
+ * @return array|false False on error, array with record props in success
*/
function create_group($name)
{
$result = false;
// make sure we have a unique name
$name = $this->unique_groupname($name);
$this->db->query(
"INSERT INTO " . $this->db->table_name($this->db_groups, true).
" (`user_id`, `changed`, `name`)".
" VALUES (".intval($this->user_id).", ".$this->db->now().", ".$this->db->quote($name).")"
);
if ($insert_id = $this->db->insert_id($this->db_groups)) {
- $result = array('id' => $insert_id, 'name' => $name);
+ $result = ['id' => $insert_id, 'name' => $name];
}
return $result;
}
/**
* Delete the given group (and all linked group members)
*
* @param string $gid Group identifier
*
- * @return boolean True on success, false if no data was changed
+ * @return bool True on success, false if no data was changed
*/
function delete_group($gid)
{
// flag group record as deleted
- $this->db->query(
+ $sql_result = $this->db->query(
"UPDATE " . $this->db->table_name($this->db_groups, true)
. " SET `del` = 1, `changed` = " . $this->db->now()
. " WHERE `contactgroup_id` = ?"
. " AND `user_id` = ?",
$gid, $this->user_id
);
$this->cache = null;
- return $this->db->affected_rows();
+ return $this->db->affected_rows($sql_result) > 0;
}
/**
* Rename a specific contact group
*
* @param string $gid Group identifier
* @param string $name New name to set for this group
* @param string $new_gid (not used)
*
- * @return boolean New name on success, false if no data was changed
+ * @return string|false New name on success, false if no data was changed
*/
function rename_group($gid, $name, &$new_gid)
{
// make sure we have a unique name
$name = $this->unique_groupname($name);
$sql_result = $this->db->query(
"UPDATE " . $this->db->table_name($this->db_groups, true).
" SET `name` = ?, `changed` = ".$this->db->now().
" WHERE `contactgroup_id` = ?".
" AND `user_id` = ?",
$name, $gid, $this->user_id
);
return $this->db->affected_rows($sql_result) ? $name : false;
}
/**
* Add the given contact records the a certain group
*
* @param string Group identifier
* @param array|string List of contact identifiers to be added
*
* @return int Number of contacts added
*/
function add_to_group($group_id, $ids)
{
if (!is_array($ids)) {
$ids = explode(self::SEPARATOR, $ids);
}
$added = 0;
- $exists = array();
+ $exists = [];
// get existing assignments ...
$sql_result = $this->db->query(
"SELECT `contact_id` FROM " . $this->db->table_name($this->db_groupmembers, true).
" WHERE `contactgroup_id` = ?".
" AND `contact_id` IN (".$this->db->array2list($ids, 'integer').")",
$group_id
);
while ($sql_result && ($sql_arr = $this->db->fetch_assoc($sql_result))) {
$exists[] = $sql_arr['contact_id'];
}
// ... and remove them from the list
$ids = array_diff($ids, $exists);
foreach ($ids as $contact_id) {
$this->db->query(
"INSERT INTO " . $this->db->table_name($this->db_groupmembers, true).
" (`contactgroup_id`, `contact_id`, `created`)".
" VALUES (?, ?, ".$this->db->now().")",
$group_id,
$contact_id
);
if ($error = $this->db->is_error()) {
$this->set_error(self::ERROR_SAVING, $error);
}
else {
$added++;
}
}
return $added;
}
/**
* Remove the given contact records from a certain group
*
* @param string $group_id Group identifier
* @param array|string $ids List of contact identifiers to be removed
*
* @return int Number of deleted group members
*/
function remove_from_group($group_id, $ids)
{
- if (!is_array($ids))
+ if (!is_array($ids)) {
$ids = explode(self::SEPARATOR, $ids);
+ }
$ids = $this->db->array2list($ids, 'integer');
$sql_result = $this->db->query(
"DELETE FROM " . $this->db->table_name($this->db_groupmembers, true).
" WHERE `contactgroup_id` = ?".
" AND `contact_id` IN ($ids)",
$group_id
);
return $this->db->affected_rows($sql_result);
}
/**
* Check for existing groups with the same name
*
* @param string $name Name to check
*
* @return string A group name which is unique for the current use
*/
private function unique_groupname($name)
{
$checkname = $name;
$num = 2;
$hit = false;
do {
$sql_result = $this->db->query(
"SELECT 1 FROM " . $this->db->table_name($this->db_groups, true).
" WHERE `del` <> 1".
" AND `user_id` = ?".
" AND `name` = ?",
$this->user_id,
$checkname);
// append number to make name unique
if ($hit = $this->db->fetch_array($sql_result)) {
$checkname = $name . ' ' . $num++;
}
}
while ($hit);
return $checkname;
}
}
diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php
index eab74bea7..9baf0ed64 100644
--- a/program/lib/Roundcube/rcube_csv2vcard.php
+++ b/program/lib/Roundcube/rcube_csv2vcard.php
@@ -1,689 +1,691 @@
<?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: |
| CSV to vCard data conversion |
+-----------------------------------------------------------------------+
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* CSV to vCard data converter
*
* @package Framework
* @subpackage Addressbook
*/
class rcube_csv2vcard
{
/**
* CSV to vCard fields mapping
*
* @var array
*/
- protected $csv2vcard_map = array(
+ protected $csv2vcard_map = [
// MS Outlook 2010
'anniversary' => 'anniversary',
'assistants_name' => 'assistant',
'assistants_phone' => 'phone:assistant',
'birthday' => 'birthday',
'business_city' => 'locality:work',
'business_countryregion' => 'country:work',
'business_fax' => 'phone:work,fax',
'business_phone' => 'phone:work',
'business_phone_2' => 'phone:work2',
'business_postal_code' => 'zipcode:work',
'business_state' => 'region:work',
'business_street' => 'street:work',
//'business_street_2' => '',
//'business_street_3' => '',
'car_phone' => 'phone:car',
'categories' => 'groups',
//'children' => '',
'company' => 'organization',
//'company_main_phone' => '',
'department' => 'department',
'email_2_address' => 'email:other',
//'email_2_type' => '',
'email_3_address' => 'email:other',
//'email_3_type' => '',
'email_address' => 'email:pref',
//'email_type' => '',
'first_name' => 'firstname',
'gender' => 'gender',
'home_city' => 'locality:home',
'home_countryregion' => 'country:home',
'home_fax' => 'phone:home,fax',
'home_phone' => 'phone:home',
'home_phone_2' => 'phone:home2',
'home_postal_code' => 'zipcode:home',
'home_state' => 'region:home',
'home_street' => 'street:home',
//'home_street_2' => '',
//'home_street_3' => '',
//'initials' => '',
//'isdn' => '',
'job_title' => 'jobtitle',
//'keywords' => '',
//'language' => '',
'last_name' => 'surname',
//'location' => '',
'managers_name' => 'manager',
'middle_name' => 'middlename',
//'mileage' => '',
'mobile_phone' => 'phone:cell',
'notes' => 'notes',
//'office_location' => '',
'other_city' => 'locality:other',
'other_countryregion' => 'country:other',
'other_fax' => 'phone:other,fax',
'other_phone' => 'phone:other',
'other_postal_code' => 'zipcode:other',
'other_state' => 'region:other',
'other_street' => 'street:other',
//'other_street_2' => '',
//'other_street_3' => '',
'pager' => 'phone:pager',
'primary_phone' => 'phone:pref',
//'profession' => '',
//'radio_phone' => '',
'spouse' => 'spouse',
'suffix' => 'suffix',
'title' => 'title',
'web_page' => 'website:homepage',
// Thunderbird
'birth_day' => 'birthday-d',
'birth_month' => 'birthday-m',
'birth_year' => 'birthday-y',
'display_name' => 'displayname',
'fax_number' => 'phone:fax',
'home_address' => 'street:home',
//'home_address_2' => '',
'home_country' => 'country:home',
'home_zipcode' => 'zipcode:home',
'mobile_number' => 'phone:cell',
'nickname' => 'nickname',
'organization' => 'organization',
'pager_number' => 'phone:pager',
'primary_email' => 'email:pref',
'secondary_email' => 'email:other',
'web_page_1' => 'website:homepage',
'web_page_2' => 'website:other',
'work_phone' => 'phone:work',
'work_address' => 'street:work',
//'work_address_2' => '',
'work_country' => 'country:work',
'work_zipcode' => 'zipcode:work',
'last' => 'surname',
'first' => 'firstname',
'work_city' => 'locality:work',
'work_state' => 'region:work',
'home_city_short' => 'locality:home',
'home_state_short' => 'region:home',
// Atmail
'date_of_birth' => 'birthday',
// 'email' => 'email:pref',
'home_mobile' => 'phone:cell',
'home_zip' => 'zipcode:home',
'info' => 'notes',
'user_photo' => 'photo',
'url' => 'website:homepage',
'work_company' => 'organization',
'work_dept' => 'department',
'work_fax' => 'phone:work,fax',
'work_mobile' => 'phone:work,cell',
'work_title' => 'jobtitle',
'work_zip' => 'zipcode:work',
'group' => 'groups',
// GMail
'groups' => 'groups',
'group_membership' => 'groups',
'given_name' => 'firstname',
'additional_name' => 'middlename',
'family_name' => 'surname',
'name' => 'displayname',
'name_prefix' => 'prefix',
'name_suffix' => 'suffix',
// Format of Letter Hub test files from
// https://letterhub.com/sample-csv-file-with-contacts/
'company_name' => 'organization',
'address' => 'street:home',
'city' => 'locality:home',
//'county' => '',
'state' => 'region:home',
'zip' => 'zipcode:home',
'phone1' => 'phone:home',
'phone' => 'phone:work',
'email' => 'email:home',
- );
+ ];
/**
* CSV label to text mapping for English
*
* @var array
*/
- protected $label_map = array(
+ protected $label_map = [
// MS Outlook 2010
'anniversary' => "Anniversary",
'assistants_name' => "Assistant's Name",
'assistants_phone' => "Assistant's Phone",
'birthday' => "Birthday",
'business_city' => "Business City",
'business_countryregion' => "Business Country/Region",
'business_fax' => "Business Fax",
'business_phone' => "Business Phone",
'business_phone_2' => "Business Phone 2",
'business_postal_code' => "Business Postal Code",
'business_state' => "Business State",
'business_street' => "Business Street",
//'business_street_2' => "Business Street 2",
//'business_street_3' => "Business Street 3",
'car_phone' => "Car Phone",
'categories' => "Categories",
//'children' => "Children",
'company' => "Company",
//'company_main_phone' => "Company Main Phone",
'department' => "Department",
//'directory_server' => "Directory Server",
'email_2_address' => "E-mail 2 Address",
//'email_2_type' => "E-mail 2 Type",
'email_3_address' => "E-mail 3 Address",
//'email_3_type' => "E-mail 3 Type",
'email_address' => "E-mail Address",
//'email_type' => "E-mail Type",
'first_name' => "First Name",
'gender' => "Gender",
'home_city' => "Home City",
'home_countryregion' => "Home Country/Region",
'home_fax' => "Home Fax",
'home_phone' => "Home Phone",
'home_phone_2' => "Home Phone 2",
'home_postal_code' => "Home Postal Code",
'home_state' => "Home State",
'home_street' => "Home Street",
//'home_street_2' => "Home Street 2",
//'home_street_3' => "Home Street 3",
//'initials' => "Initials",
//'isdn' => "ISDN",
'job_title' => "Job Title",
//'keywords' => "Keywords",
//'language' => "Language",
'last_name' => "Last Name",
//'location' => "Location",
'managers_name' => "Manager's Name",
'middle_name' => "Middle Name",
//'mileage' => "Mileage",
'mobile_phone' => "Mobile Phone",
'notes' => "Notes",
//'office_location' => "Office Location",
'other_city' => "Other City",
'other_countryregion' => "Other Country/Region",
'other_fax' => "Other Fax",
'other_phone' => "Other Phone",
'other_postal_code' => "Other Postal Code",
'other_state' => "Other State",
'other_street' => "Other Street",
//'other_street_2' => "Other Street 2",
//'other_street_3' => "Other Street 3",
'pager' => "Pager",
'primary_phone' => "Primary Phone",
//'profession' => "Profession",
//'radio_phone' => "Radio Phone",
'spouse' => "Spouse",
'suffix' => "Suffix",
'title' => "Title",
'web_page' => "Web Page",
// Thunderbird
'birth_day' => "Birth Day",
'birth_month' => "Birth Month",
'birth_year' => "Birth Year",
'display_name' => "Display Name",
'fax_number' => "Fax Number",
'home_address' => "Home Address",
//'home_address_2' => "Home Address 2",
'home_country' => "Home Country",
'home_zipcode' => "Home ZipCode",
'mobile_number' => "Mobile Number",
'nickname' => "Nickname",
'organization' => "Organization",
'pager_number' => "Pager Namber",
'primary_email' => "Primary Email",
'secondary_email' => "Secondary Email",
'web_page_1' => "Web Page 1",
'web_page_2' => "Web Page 2",
'work_phone' => "Work Phone",
'work_address' => "Work Address",
//'work_address_2' => "Work Address 2",
'work_city' => "Work City",
'work_country' => "Work Country",
'work_state' => "Work State",
'work_zipcode' => "Work ZipCode",
// Atmail
'date_of_birth' => "Date of Birth",
'email' => "Email",
//'email_2' => "Email2",
//'email_3' => "Email3",
//'email_4' => "Email4",
//'email_5' => "Email5",
'home_mobile' => "Home Mobile",
'home_zip' => "Home Zip",
'info' => "Info",
'user_photo' => "User Photo",
'url' => "URL",
'work_company' => "Work Company",
'work_dept' => "Work Dept",
'work_fax' => "Work Fax",
'work_mobile' => "Work Mobile",
'work_title' => "Work Title",
'work_zip' => "Work Zip",
'group' => "Group",
// GMail
'groups' => "Groups",
'group_membership' => "Group Membership",
'given_name' => "Given Name",
'additional_name' => "Additional Name",
'family_name' => "Family Name",
'name' => "Name",
'name_prefix' => "Name Prefix",
'name_suffix' => "Name Suffix",
- );
+ ];
/**
* Special fields map for GMail format
*
* @var array
*/
- protected $gmail_label_map = array(
- 'E-mail' => array(
- 'Value' => array(
+ protected $gmail_label_map = [
+ 'E-mail' => [
+ 'Value' => [
'home' => 'email:home',
'work' => 'email:work',
'other' => 'email:other',
'' => 'email:other',
- ),
- ),
- 'Phone' => array(
- 'Value' => array(
+ ],
+ ],
+ 'Phone' => [
+ 'Value' => [
'home' => 'phone:home',
'homefax' => 'phone:homefax',
'main' => 'phone:pref',
'pager' => 'phone:pager',
'mobile' => 'phone:cell',
'work' => 'phone:work',
'workfax' => 'phone:workfax',
- ),
- ),
- 'Relation' => array(
- 'Value' => array(
+ ],
+ ],
+ 'Relation' => [
+ 'Value' => [
'spouse' => 'spouse',
- ),
- ),
- 'Website' => array(
- 'Value' => array(
+ ],
+ ],
+ 'Website' => [
+ 'Value' => [
'profile' => 'website:profile',
'blog' => 'website:blog',
'homepage' => 'website:homepage',
'work' => 'website:work',
- ),
- ),
- 'Address' => array(
- 'Street' => array(
+ ],
+ ],
+ 'Address' => [
+ 'Street' => [
'home' => 'street:home',
'work' => 'street:work',
- ),
- 'City' => array(
+ ],
+ 'City' => [
'home' => 'locality:home',
'work' => 'locality:work',
- ),
- 'Region' => array(
+ ],
+ 'Region' => [
'home' => 'region:home',
'work' => 'region:work',
- ),
- 'Postal Code' => array(
+ ],
+ 'Postal Code' => [
'home' => 'zipcode:home',
'work' => 'zipcode:work',
- ),
- 'Country' => array(
+ ],
+ 'Country' => [
'home' => 'country:home',
'work' => 'country:work',
- ),
- ),
- 'Organization' => array(
- 'Name' => array(
+ ],
+ ],
+ 'Organization' => [
+ 'Name' => [
'' => 'organization',
- ),
- 'Title' => array(
+ ],
+ 'Title' => [
'' => 'jobtitle',
- ),
- 'Department' => array(
+ ],
+ 'Department' => [
'' => 'department',
- ),
- ),
- );
+ ],
+ ],
+ ];
- protected $local_label_map = array();
- protected $vcards = array();
- protected $map = array();
+ protected $local_label_map = [];
+ protected $vcards = [];
+ protected $map = [];
/**
* Class constructor
*
* @param string $lang File language
*/
public function __construct($lang = 'en_US')
{
// Localize fields map
if ($lang && $lang != 'en_US') {
if (file_exists(RCUBE_LOCALIZATION_DIR . "$lang/csv2vcard.inc")) {
include RCUBE_LOCALIZATION_DIR . "$lang/csv2vcard.inc";
}
if (!empty($map)) {
$this->local_label_map = array_merge($this->label_map, $map);
}
}
$this->label_map = array_flip($this->label_map);
$this->local_label_map = array_flip($this->local_label_map);
}
/**
* Import contacts from CSV file
*
- * @param string $csv Content of the CSV file
- * @param boolean $dry_run Generate automatic field mapping
+ * @param string $csv Content of the CSV file
+ * @param bool $dry_run Generate automatic field mapping
*
- * @return array Field mapping info (dry run only)
+ * @return array Field mapping info (dry run only)
*/
public function import($csv, $dry_run = false, $skip_head = true)
{
// convert to UTF-8
$head = substr($csv, 0, 4096);
$charset = rcube_charset::detect($head, RCUBE_CHARSET);
$csv = rcube_charset::convert($csv, $charset);
- $csv = preg_replace(array('/^[\xFE\xFF]{2}/', '/^\xEF\xBB\xBF/', '/^\x00+/'), '', $csv); // also remove BOM
+ $csv = preg_replace(['/^[\xFE\xFF]{2}/', '/^\xEF\xBB\xBF/', '/^\x00+/'], '', $csv); // also remove BOM
$head = '';
// Split CSV file into lines
$lines = rcube_utils::explode_quoted_string('[\r\n]+', $csv);
// Parse first 2 lines of file to identify fields
// 2 lines because for gmail CSV we need to get the value from the "Type" fields to identify which is which
if (empty($this->map)) {
$this->parse_header(array_slice($lines, 0, 2));
}
// Parse the fields
foreach ($lines as $n => $line) {
$elements = $this->parse_line($line);
if ($dry_run) {
- return array('source' => $elements, 'destination' => $this->map);
+ return ['source' => $elements, 'destination' => $this->map];
}
if (empty($elements)) {
continue;
}
// first line is the headers so do not import unless explicitly set
if (!$skip_head || $n > 0) {
$this->csv_to_vcard($elements);
}
}
}
/**
* Set field mapping info
*
* @param array Field mapping
*/
public function set_map($elements)
{
// sanitize input
$elements = array_filter($elements, function($val) {
return in_array($val, $this->csv2vcard_map);
});
$this->map = $elements;
}
/**
* Set field mapping info
*
* @return array Array of vcard fields and localized names
*/
public function get_fields()
{
// get all vcard fields
- $fields = array_unique($this->csv2vcard_map);
+ $fields = array_unique($this->csv2vcard_map);
$local_field_names = $this->local_label_map ?: $this->label_map;
$local_field_names = array_flip($local_field_names);
// translate with the local map
- $map = array();
+ $map = [];
foreach ($fields as $csv => $vcard) {
if ($vcard == '_auto_') {
continue;
}
$map[$vcard] = $local_field_names[$csv];
}
// small fix to prevent "Groups" displaying as "Categories"
$map['groups'] = $local_field_names['groups'];
asort($map);
return $map;
}
/**
* Export vCards
*
* @return array rcube_vcard List of vcards
*/
public function export()
{
return $this->vcards;
}
/**
* Parse CSV file line
*
* @param string $line Line of text from CSV file
*
* @return array CSV data extracted from the line
*/
protected function parse_line($line)
{
$line = trim($line);
if (empty($line)) {
- return array();
+ return [];
}
$fields = str_getcsv($line);
return $fields;
}
/**
* Parse CSV header line, detect fields mapping
*
* @param array $elements Array of field names from a first line in CSV file
*/
protected function parse_header($lines)
{
$elements = $this->parse_line($lines[0]);
if (count($lines) == 2) {
// first line of contents needed to properly identify fields in gmail CSV
$contents = $this->parse_line($lines[1]);
}
- $map1 = array();
- $map2 = array();
+ $map1 = [];
+ $map2 = [];
$size = count($elements);
// check English labels
for ($i = 0; $i < $size; $i++) {
if (!empty($this->label_map[$elements[$i]])) {
$label = $this->label_map[$elements[$i]];
if ($label && !empty($this->csv2vcard_map[$label])) {
$map1[$i] = $this->csv2vcard_map[$label];
}
}
}
// check localized labels
if (!empty($this->local_label_map)) {
for ($i = 0; $i < $size; $i++) {
$label = $this->local_label_map[$elements[$i]];
// special localization label
if ($label && $label[0] == '_') {
$label = substr($label, 1);
}
if ($label && !empty($this->csv2vcard_map[$label])) {
$map2[$i] = $this->csv2vcard_map[$label];
}
}
}
// If nothing recognized fallback to simple non-localized labels
if (empty($map1) && empty($map2)) {
for ($i = 0; $i < $size; $i++) {
$label = str_replace(' ', '_', strtolower($elements[$i]));
if (!empty($this->csv2vcard_map[$label])) {
$map1[$i] = $this->csv2vcard_map[$label];
}
}
}
$this->map = count($map1) >= count($map2) ? $map1 : $map2;
if (!empty($contents)) {
foreach ($this->gmail_label_map as $key => $items) {
$num = 1;
while (($_key = "$key $num - Type") && ($found = array_search($_key, $elements)) !== false) {
$type = $contents[$found];
$type = preg_replace('/[^a-z]/', '', strtolower($type));
foreach ($items as $item_key => $vcard_fields) {
$_key = "$key $num - $item_key";
if (($found = array_search($_key, $elements)) !== false) {
$this->map[$found] = $vcard_fields[$type];
}
}
$num++;
}
}
}
}
/**
* Convert CSV data row to vCard
*
* @param array $data CSV data array
*/
protected function csv_to_vcard($data)
{
- $contact = array();
+ $contact = [];
+
foreach ($this->map as $idx => $name) {
- if ($name == '_auto_')
+ if ($name == '_auto_') {
continue;
+ }
$value = $data[$idx];
if ($value !== null && $value !== '') {
if (!empty($contact[$name])) {
$contact[$name] = (array) $contact[$name];
$contact[$name][] = $value;
}
else {
$contact[$name] = $value;
}
}
}
if (empty($contact)) {
return;
}
// Handle special values
if (!empty($contact['birthday-d']) && !empty($contact['birthday-m']) && !empty($contact['birthday-y'])) {
$contact['birthday'] = $contact['birthday-y'] .'-' .$contact['birthday-m'] . '-' . $contact['birthday-d'];
}
if (!empty($contact['groups'])) {
// categories/groups separator in vCard is ',' not ';'
$contact['groups'] = str_replace(',', '', $contact['groups']);
$contact['groups'] = str_replace(';', ',', $contact['groups']);
// remove "* " added by GMail
$contact['groups'] = str_replace('* ', '', $contact['groups']);
// replace strange delimiter added by GMail
$contact['groups'] = str_replace(' ::: ', ',', $contact['groups']);
}
// Empty dates, e.g. "0/0/00", "0000-00-00 00:00:00"
foreach (array('birthday', 'anniversary') as $key) {
if (!empty($contact[$key])) {
$date = preg_replace('/[0[:^word:]]/', '', $contact[$key]);
if (empty($date)) {
unset($contact[$key]);
}
}
}
if (!empty($contact['gender']) && ($gender = strtolower($contact['gender']))) {
- if (!in_array($gender, array('male', 'female'))) {
+ if (!in_array($gender, ['male', 'female'])) {
unset($contact['gender']);
}
}
// Convert address(es) to rcube_vcard data
foreach ($contact as $idx => $value) {
$name = explode(':', $idx);
- if (in_array($name[0], array('street', 'locality', 'region', 'zipcode', 'country'))) {
+ if (in_array($name[0], ['street', 'locality', 'region', 'zipcode', 'country'])) {
$contact['address:'.$name[1]][$name[0]] = $value;
unset($contact[$idx]);
}
}
// Create vcard object
$vcard = new rcube_vcard();
foreach ($contact as $name => $value) {
$name = explode(':', $name);
if (is_array($value) && $name[0] != 'address') {
foreach ((array) $value as $val) {
$vcard->set($name[0], $val, isset($name[1]) ? $name[1] : null);
}
}
else {
$vcard->set($name[0], $value, isset($name[1]) ? $name[1] : null);
}
}
// add to the list
$this->vcards[] = $vcard;
}
}
diff --git a/tests/Framework/Addressbook.php b/tests/Framework/Addressbook.php
new file mode 100644
index 000000000..d0a44f989
--- /dev/null
+++ b/tests/Framework/Addressbook.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * Test class to test rcube_addressbook class
+ *
+ * @package Tests
+ */
+class Framework_Addressbook extends PHPUnit\Framework\TestCase
+{
+ /**
+ * Test for get_col_values() method
+ */
+ function test_get_col_values()
+ {
+ $data = ['email' => 'test@test.com', 'other' => 'test'];
+ $result = rcube_addressbook::get_col_values('email', $data, true);
+
+ $this->assertSame(['test@test.com'], $result);
+
+ $data = ['email:home' => 'test@test.com', 'other' => 'test'];
+ $result = rcube_addressbook::get_col_values('email', $data, true);
+
+ $this->assertSame(['test@test.com'], $result);
+
+ $data = ['email:home' => 'test@test.com', 'other' => 'test'];
+ $result = rcube_addressbook::get_col_values('email', $data, false);
+
+ $this->assertSame(['home' => ['test@test.com']], $result);
+ }
+}
diff --git a/tests/Framework/BaseReplacer.php b/tests/Framework/BaseReplacer.php
index fd0c7c30c..04cbce561 100644
--- a/tests/Framework/BaseReplacer.php
+++ b/tests/Framework/BaseReplacer.php
@@ -1,62 +1,62 @@
<?php
/**
* Test class to test rcube_base_replacer class
*
* @package Tests
*/
class Framework_BaseReplacer extends PHPUnit\Framework\TestCase
{
/**
* Class constructor
*/
function test_class()
{
$object = new rcube_base_replacer('test');
$this->assertInstanceOf('rcube_base_replacer', $object, "Class constructor");
}
/**
* Test replace()
*/
function test_replace()
{
$base = 'http://thisshouldntbetheurl.bob.com/';
$html = '<A href=http://shouldbethislink.com>Test URL</A>';
$replacer = new rcube_base_replacer($base);
$response = $replacer->replace($html);
$this->assertSame('<A href="http://shouldbethislink.com">Test URL</A>', $response);
}
/**
* Data for absolute_url() test
*/
function data_absolute_url()
{
- return array(
- array('', 'http://test', 'http://test/'),
- array('http://test', 'http://anything', 'http://test'),
- array('cid:test', 'http://anything', 'cid:test'),
- array('/test', 'http://test', 'http://test/test'),
- array('./test', 'http://test', 'http://test/test'),
- array('../test1', 'http://test/test2', 'http://test1'),
- array('../test1', 'http://test/test2/', 'http://test/test1'),
- );
+ return [
+ ['', 'http://test', 'http://test/'],
+ ['http://test', 'http://anything', 'http://test'],
+ ['cid:test', 'http://anything', 'cid:test'],
+ ['/test', 'http://test', 'http://test/test'],
+ ['./test', 'http://test', 'http://test/test'],
+ ['../test1', 'http://test/test2', 'http://test1'],
+ ['../test1', 'http://test/test2/', 'http://test/test1'],
+ ];
}
/**
* Test absolute_url()
* @dataProvider data_absolute_url
*/
function test_absolute_url($path, $base, $expected)
{
$replacer = new rcube_base_replacer('test');
$result = $replacer->absolute_url($path, $base);
$this->assertSame($expected, $result);
}
}
diff --git a/tests/Framework/Browser.php b/tests/Framework/Browser.php
index 09465478d..6d5836266 100644
--- a/tests/Framework/Browser.php
+++ b/tests/Framework/Browser.php
@@ -1,199 +1,188 @@
<?php
/**
* Test class to test rcube_browser class
*
* @package Tests
*/
class Framework_Browser extends PHPUnit\Framework\TestCase
{
-
- /**
- * Class constructor
- */
- function test_class()
- {
- $object = new rcube_browser();
-
- $this->assertInstanceOf('rcube_browser', $object, "Class constructor");
- }
-
/**
* @dataProvider browsers
*/
function test_browser($useragent, $opera, $chrome, $ie, $edge, $safari, $mz)
{
$object = $this->getBrowser($useragent);
$this->assertEquals($opera, $object->opera, 'Check for Opera failed');
$this->assertEquals($chrome, $object->chrome, 'Check for Chrome failed');
$this->assertEquals($ie, $object->ie, 'Check for IE failed');
$this->assertEquals($edge, $object->edge, 'Check for Edge failed');
$this->assertEquals($safari, $object->safari, 'Check for Safari failed');
$this->assertEquals($mz, $object->mz, 'Check for MZ failed');
}
/**
* @dataProvider os
*/
function test_os($useragent, $windows, $linux, $unix, $mac)
{
$object = $this->getBrowser($useragent);
$this->assertEquals($windows, $object->win, 'Check Result of Windows');
$this->assertEquals($linux, $object->linux, 'Check Result of Linux');
$this->assertEquals($mac, $object->mac, 'Check Result of Mac');
$this->assertEquals($unix, $object->unix, 'Check Result of Unix');
}
/**
* @dataProvider versions
*/
function test_version($useragent, $version)
{
$object = $this->getBrowser($useragent);
$this->assertEquals($version, $object->ver);
}
function versions()
{
- return $this->extractDataSet(array('version'));
+ return $this->extractDataSet(['version']);
}
function browsers()
{
- return $this->extractDataSet(array('isOpera', 'isChrome', 'isIE', 'isEdge', 'isSafari', 'isMZ'));
+ return $this->extractDataSet(['isOpera', 'isChrome', 'isIE', 'isEdge', 'isSafari', 'isMZ']);
}
function useragents()
{
- return array(
- 'WIN: Mozilla Firefox ' => array(
+ return [
+ 'WIN: Mozilla Firefox ' => [
'useragent' => 'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.1) Gecko/20060111 Firefox/1.5.0.1',
'version' => '1.8',
'isWin' => true,
'isLinux' => false,
'isMac' => false,
'isUnix' => false,
'isOpera' => false,
'isChrome' => false,
'isIE' => false,
'isEdge' => false,
'isSafari' => false,
'isMZ' => true,
- ),
+ ],
- 'LINUX: Bon Echo ' => array(
+ 'LINUX: Bon Echo ' => [
'useragent' => 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.1.1) Gecko/20070222 BonEcho/2.0.0.1',
'version' => '1.8',
'isWin' => false,
'isLinux' => true,
'isMac' => false,
'isUnix' => false,
'isOpera' => false,
'isChrome' => false,
'isIE' => false,
'isEdge' => false,
'isSafari' => false,
'isMZ' => true,
- ),
+ ],
- 'Chrome Mac' => array(
+ 'Chrome Mac' => [
'useragent' => 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_4; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.461.0 Safari/534.3',
'version' => '6',
'isWin' => false,
'isLinux' => false,
'isMac' => true,
'isUnix' => false,
'isOpera' => false,
'isChrome' => true,
'isIE' => false,
'isEdge' => false,
'isSafari' => false,
'isMZ' => false,
- ),
+ ],
- 'IE 11' => array(
+ 'IE 11' => [
'useragent' => 'Mozilla/5.0 (Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C; rv:11.0) like Gecko',
'version' => '11.0',
'isWin' => true,
'isLinux' => false,
'isMac' => false,
'isUnix' => false,
'isOpera' => false,
'isChrome' => false,
'isIE' => true,
'isEdge' => false,
'isSafari' => false,
'isMZ' => false,
- ),
+ ],
- 'Opera 15' => array(
+ 'Opera 15' => [
'useragent' => 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.29 Safari/537.36 OPR/15.0.1147.24',
'version' => '15.0',
'isWin' => true,
'isLinux' => false,
'isMac' => false,
'isUnix' => false,
'isOpera' => true,
'isChrome' => false,
'isIE' => false,
'isEdge' => false,
'isSafari' => false,
'isMZ' => false,
- ),
+ ],
- 'Edge 14' => array(
+ 'Edge 14' => [
'useragent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14931',
'version' => '14.14931',
'isWin' => true,
'isLinux' => false,
'isMac' => false,
'isUnix' => false,
'isOpera' => false,
'isChrome' => false,
'isIE' => false,
'isEdge' => true,
'isSafari' => false,
'isMZ' => false,
- ),
- );
+ ],
+ ];
}
function os()
{
- return $this->extractDataSet(array('isWin', 'isLinux', 'isUnix', 'isMac'));
+ return $this->extractDataSet(['isWin', 'isLinux', 'isUnix', 'isMac']);
}
private function extractDataSet($keys)
{
- $keys = array_merge(array('useragent'), $keys);
+ $keys = array_merge(['useragent'], $keys);
$browser = $this->useragents();
- $extracted = array();
+ $extracted = [];
foreach ($browser as $label => $data) {
foreach($keys as $key) {
$extracted[$data['useragent']][] = $data[$key];
}
}
return $extracted;
}
/**
* @param string $useragent
* @return rcube_browser
*/
private function getBrowser($useragent)
{
/** @var $object rcube_browser */
$_SERVER['HTTP_USER_AGENT'] = $useragent;
$object = new rcube_browser();
return $object;
}
}
diff --git a/tests/Framework/Cache.php b/tests/Framework/Cache.php
index 330076ac9..04ea632c9 100644
--- a/tests/Framework/Cache.php
+++ b/tests/Framework/Cache.php
@@ -1,20 +1,31 @@
<?php
/**
* Test class to test rcube_cache class
*
* @package Tests
*/
class Framework_Cache extends PHPUnit\Framework\TestCase
{
-
/**
- * Class constructor
+ * Test factory method
*/
- function test_class()
+ function test_factory()
{
- $object = new rcube_cache('db', 1);
+ $object = rcube_cache::factory('db', 1);
+ $this->assertInstanceOf('rcube_cache_db', $object, "Class constructor");
$this->assertInstanceOf('rcube_cache', $object, "Class constructor");
}
+
+ /**
+ * key_name() method
+ */
+ function test_key_name()
+ {
+ $this->assertSame('test', rcube_cache::key_name('test'));
+
+ $params = ['test1' => 'test2'];
+ $this->assertSame('test.ad0234829205b9033196ba818f7a872b', rcube_cache::key_name('test', $params));
+ }
}
diff --git a/tests/Framework/CacheDB.php b/tests/Framework/CacheDB.php
new file mode 100644
index 000000000..b1283529d
--- /dev/null
+++ b/tests/Framework/CacheDB.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * Test class to test rcube_cache_db class
+ *
+ * @package Tests
+ */
+class Framework_CacheDB extends PHPUnit\Framework\TestCase
+{
+ /**
+ * Test common cache functionality
+ */
+ function test_common_cache_operations()
+ {
+ $rcube = rcube::get_instance();
+ $db = $rcube->get_dbh();
+ $db->query('DELETE FROM cache');
+
+ $cache = new rcube_cache_db(1, 'test', 60);
+rcube::write_log('sql', '>>>>>>>>>>>>>>>>>>>>>>>>');
+ // Set and get cache record
+ $data = ['data'];
+
+ $cache->set('test', $data);
+
+ $this->assertSame($data, $cache->get('test'));
+
+ $cache->close();
+
+ $cache = new rcube_cache_db(1, 'test', 60);
+
+ $this->assertSame($data, $cache->get('test'));
+
+ // Remove cached record
+ $cache->remove('test');
+
+ $this->assertSame(null, $cache->get('test'));
+
+ $cache->close();
+
+ $cache = new rcube_cache_db(1, 'test', 60);
+
+ $this->assertSame(null, $cache->get('test'));
+
+ // Call expunge methods
+ $cache->expunge();
+ }
+}
diff --git a/tests/Framework/Charset.php b/tests/Framework/Charset.php
index da3bff890..2fc2042d1 100644
--- a/tests/Framework/Charset.php
+++ b/tests/Framework/Charset.php
@@ -1,194 +1,193 @@
<?php
/**
* Test class to test rcube_charset class
*
* @package Tests
* @group mbstring
*/
class Framework_Charset extends PHPUnit\Framework\TestCase
{
/**
* Data for test_clean()
*/
function data_clean()
{
- return array(
- array('', ''),
- array("\xC1", ""),
- array("Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν", "Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν"),
- );
+ return [
+ ['', ''],
+ ["\xC1", ""],
+ ["Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν", "Οὐχὶ ταὐτὰ παρίσταταί μοι γιγνώσκειν"],
+ ];
}
/**
* @dataProvider data_clean
*/
function test_clean($input, $output)
{
$this->assertEquals($output, rcube_charset::clean($input));
}
/**
* Just check for faulty byte-sequence, regardless of the actual cleaning results
*/
function test_clean_2()
{
$bogus = "сим\xD0вол";
$this->assertRegExp('/\xD0\xD0/', $bogus);
$this->assertNotRegExp('/\xD0\xD0/', rcube_charset::clean($bogus));
}
/**
* Data for test_parse_charset()
*/
function data_parse_charset()
{
- return array(
- array('UTF8', 'UTF-8'),
- array('WIN1250', 'WINDOWS-1250'),
- );
+ return [
+ ['UTF8', 'UTF-8'],
+ ['WIN1250', 'WINDOWS-1250'],
+ ];
}
/**
* @dataProvider data_parse_charset
*/
function test_parse_charset($input, $output)
{
$this->assertEquals($output, rcube_charset::parse_charset($input));
}
/**
* Data for test_convert()
*/
function data_convert()
{
- return array(
- array('ö', 'ö', 'UTF-8', 'UTF-8'),
- array('ö', '', 'UTF-8', 'ASCII'),
- array('aż', 'a', 'UTF-8', 'US-ASCII'),
- array('&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки', 'UTF7-IMAP', 'UTF-8'),
- array('Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-', 'UTF-8', 'UTF7-IMAP'),
- array(base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), '㈱山﨑工業', 'ISO-2022-JP', 'UTF-8'),
- array('㈱山﨑工業', base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), 'UTF-8', 'ISO-2022-JP'),
- );
+ return [
+ ['ö', 'ö', 'UTF-8', 'UTF-8'],
+ ['ö', '', 'UTF-8', 'ASCII'],
+ ['aż', 'a', 'UTF-8', 'US-ASCII'],
+ ['&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки', 'UTF7-IMAP', 'UTF-8'],
+ ['Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-', 'UTF-8', 'UTF7-IMAP'],
+ [base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), '㈱山﨑工業', 'ISO-2022-JP', 'UTF-8'],
+ ['㈱山﨑工業', base64_decode('GyRCLWo7M3l1OSk2SBsoQg=='), 'UTF-8', 'ISO-2022-JP'],
+ ];
}
/**
* @dataProvider data_convert
*/
function test_convert($input, $output, $from, $to)
{
$this->assertEquals($output, rcube_charset::convert($input, $from, $to));
}
/**
* Data for test_utf7_to_utf8()
*/
function data_utf7_to_utf8()
{
- return array(
- array('+BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'),
- );
+ return [
+ ['+BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'],
+ ];
}
/**
* @dataProvider data_utf7_to_utf8
*/
function test_utf7_to_utf8($input, $output)
{
$this->assertEquals($output, rcube_charset::utf7_to_utf8($input));
}
/**
* Data for test_utf7imap_to_utf8()
*/
function data_utf7imap_to_utf8()
{
- return array(
- array('&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'),
- );
+ return [
+ ['&BCAEMARBBEEESwQ7BDoEOA-', 'Рассылки'],
+ ];
}
/**
* @dataProvider data_utf7imap_to_utf8
*/
function test_utf7imap_to_utf8($input, $output)
{
$this->assertEquals($output, rcube_charset::utf7imap_to_utf8($input));
}
/**
* Data for test_utf8_to_utf7imap()
*/
function data_utf8_to_utf7imap()
{
- return array(
- array('Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-'),
- );
+ return [
+ ['Рассылки', '&BCAEMARBBEEESwQ7BDoEOA-'],
+ ];
}
/**
* @dataProvider data_utf8_to_utf7imap
*/
function test_utf8_to_utf7imap($input, $output)
{
$this->assertEquals($output, rcube_charset::utf8_to_utf7imap($input));
}
/**
* Data for test_utf16_to_utf8()
*/
function data_utf16_to_utf8()
{
- return array(
- array(base64_decode('BCAEMARBBEEESwQ7BDoEOA=='), 'Рассылки'),
- );
+ return [
+ [base64_decode('BCAEMARBBEEESwQ7BDoEOA=='), 'Рассылки'],
+ ];
}
/**
* @dataProvider data_utf16_to_utf8
*/
function test_utf16_to_utf8($input, $output)
{
$this->assertEquals($output, rcube_charset::utf16_to_utf8($input));
}
/**
* Data for test_detect()
*/
function data_detect()
{
- return array(
- array('', '', 'UTF-8'),
- array('a', 'UTF-8', 'UTF-8'),
- );
+ return [
+ ['', '', 'UTF-8'],
+ ['a', 'UTF-8', 'UTF-8'],
+ ];
}
/**
* @dataProvider data_detect
*/
function test_detect($input, $fallback, $output)
{
$this->assertEquals($output, rcube_charset::detect($input, $fallback));
}
/**
* Data for test_detect()
*/
function data_detect_with_lang()
{
- return array(
- array(base64_decode('xeOl3KZXutkspUStbg=='), 'zh_TW', 'BIG-5'),
- );
+ return [
+ [base64_decode('xeOl3KZXutkspUStbg=='), 'zh_TW', 'BIG-5'],
+ ];
}
/**
* @dataProvider data_detect_with_lang
*/
function test_detect_with_lang($input, $lang, $output)
{
$this->assertEquals($output, rcube_charset::detect($input, $output, $lang));
}
-
}
diff --git a/tests/Framework/Contacts.php b/tests/Framework/Contacts.php
index 08e60d1cc..f7dc192ce 100644
--- a/tests/Framework/Contacts.php
+++ b/tests/Framework/Contacts.php
@@ -1,20 +1,41 @@
<?php
/**
* Test class to test rcube_contacts class
*
* @package Tests
*/
class Framework_Contacts extends PHPUnit\Framework\TestCase
{
-
/**
* Class constructor
*/
function test_class()
{
$object = new rcube_contacts(null, null);
$this->assertInstanceOf('rcube_contacts', $object, "Class constructor");
}
+
+ /**
+ * Test validate() method
+ */
+ function test_validate()
+ {
+ $contacts = new rcube_contacts(null, null);
+
+ $data = [];
+ $this->assertSame(false, $contacts->validate($data));
+ $this->assertSame(['type' => 3, 'message' => 'nonamewarning'], $contacts->get_error());
+
+ $data = ['name' => 'test'];
+ $this->assertSame(true, $contacts->validate($data));
+
+ $data = ['email' => '@example.org'];
+ $this->assertSame(false, $contacts->validate($data));
+ $this->assertSame(['type' => 3, 'message' => 'Invalid email address: @example.org'], $contacts->get_error());
+
+ $data = ['email' => 'test@test.com'];
+ $this->assertSame(true, $contacts->validate($data));
+ }
}
diff --git a/tests/Framework/Csv2vcard.php b/tests/Framework/Csv2vcard.php
index 5bd917f1b..585fc2bd5 100644
--- a/tests/Framework/Csv2vcard.php
+++ b/tests/Framework/Csv2vcard.php
@@ -1,94 +1,94 @@
<?php
/**
* Test class to test rcube_csv2vcard class
*
* @package Tests
*/
class Framework_Csv2vcard extends PHPUnit\Framework\TestCase
{
function test_import_generic()
{
$csv = new rcube_csv2vcard;
// empty input
$csv->import('');
- $this->assertSame(array(), $csv->export());
+ $this->assertSame([], $csv->export());
}
function test_import_tb_plain()
{
$csv_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/tb_plain.csv');
$vcf_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/tb_plain.vcf');
$csv = new rcube_csv2vcard;
$csv->import($csv_text);
$result = $csv->export();
$vcard = $result[0]->export(false);
$this->assertCount(1, $result);
$vcf_text = trim(str_replace("\r\n", "\n", $vcf_text));
$vcard = trim(str_replace("\r\n", "\n", $vcard));
$this->assertEquals($vcf_text, $vcard);
}
function test_import_email()
{
$csv_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/email.csv');
$vcf_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/email.vcf');
$csv = new rcube_csv2vcard;
$csv->import($csv_text);
$result = $csv->export();
$this->assertCount(4, $result);
$vcard = '';
foreach ($result as $vcf) {
$vcard .= $vcf->export(false) . "\n";
}
$vcf_text = trim(str_replace("\r\n", "\n", $vcf_text));
$vcard = trim(str_replace("\r\n", "\n", $vcard));
$this->assertEquals($vcf_text, $vcard);
}
function test_import_gmail()
{
$csv_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/gmail.csv');
$vcf_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/gmail.vcf');
$csv = new rcube_csv2vcard;
$csv->import($csv_text);
$result = $csv->export();
$vcard = $result[0]->export(false);
$this->assertCount(1, $result);
$vcf_text = trim(str_replace("\r\n", "\n", $vcf_text));
$vcard = trim(str_replace("\r\n", "\n", $vcard));
$this->assertEquals($vcf_text, $vcard);
}
function test_import_outlook()
{
$csv_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/outlook.csv');
$vcf_text = file_get_contents(TESTS_DIR . '/src/Csv2vcard/outlook.vcf');
$csv = new rcube_csv2vcard;
$csv->import($csv_text);
$result = $csv->export();
$vcard = $result[0]->export(false);
$this->assertCount(1, $result);
$vcf_text = trim(str_replace("\r\n", "\n", $vcf_text));
$vcard = trim(str_replace("\r\n", "\n", $vcard));
$this->assertEquals($vcf_text, $vcard);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Apr 18, 10:10 AM (6 h, 10 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
436187
Default Alt Text
(255 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment