Page MenuHomePhorge

No OneTemporary

Size
255 KB
Referenced Files
None
Subscribers
None
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

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)

Event Timeline