diff options
Diffstat (limited to 'program/lib')
-rw-r--r-- | program/lib/Roundcube/bootstrap.php | 12 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_addressbook.php | 24 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_config.php | 222 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_contacts.php | 4 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_csv2vcard.php | 2 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_db.php | 36 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_db_mysql.php | 25 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_imap.php | 5 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_imap_cache.php | 45 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_ldap.php | 1 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_mime.php | 18 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_utils.php | 38 | ||||
-rw-r--r-- | program/lib/Roundcube/rcube_vcard.php | 9 |
13 files changed, 330 insertions, 111 deletions
diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php index 5d5a22387..6e5143382 100644 --- a/program/lib/Roundcube/bootstrap.php +++ b/program/lib/Roundcube/bootstrap.php @@ -26,25 +26,25 @@ */ $config = array( - 'error_reporting' => E_ALL &~ (E_NOTICE | E_STRICT), + 'error_reporting' => E_ALL & ~E_NOTICE & ~E_STRICT, // Some users are not using Installer, so we'll check some // critical PHP settings here. Only these, which doesn't provide // an error/warning in the logs later. See (#1486307). 'mbstring.func_overload' => 0, - 'magic_quotes_runtime' => 0, - 'magic_quotes_sybase' => 0, // #1488506 + 'magic_quotes_runtime' => false, + 'magic_quotes_sybase' => false, // #1488506 ); // check these additional ini settings if not called via CLI if (php_sapi_name() != 'cli') { $config += array( - 'suhosin.session.encrypt' => 0, - 'file_uploads' => 1, + 'suhosin.session.encrypt' => false, + 'file_uploads' => true, ); } foreach ($config as $optname => $optval) { - $ini_optval = filter_var(ini_get($optname), FILTER_VALIDATE_BOOLEAN); + $ini_optval = filter_var(ini_get($optname), is_bool($optval) ? FILTER_VALIDATE_BOOLEAN : FILTER_VALIDATE_INT); if ($optval != $ini_optval && @ini_set($optname, $optval) === false) { $error = "ERROR: Wrong '$optname' option value and it wasn't possible to set it to required value ($optval).\n" . "Check your PHP configuration (including php_admin_flag)."; diff --git a/program/lib/Roundcube/rcube_addressbook.php b/program/lib/Roundcube/rcube_addressbook.php index 4ed139c45..9301211ff 100644 --- a/program/lib/Roundcube/rcube_addressbook.php +++ b/program/lib/Roundcube/rcube_addressbook.php @@ -3,7 +3,7 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2006-2012, The Roundcube Dev Team | + | Copyright (C) 2006-2013, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -35,6 +35,7 @@ abstract class rcube_addressbook /** public properties (mandatory) */ public $primary_key; public $groups = false; + public $export_groups = true; public $readonly = true; public $searchonly = false; public $undelete = false; @@ -423,7 +424,7 @@ abstract class rcube_addressbook * @param boolean True to return one array with all values, False for hash array with values grouped by type * @return array List of column values */ - function get_col_values($col, $data, $flat = false) + public static function get_col_values($col, $data, $flat = false) { $out = array(); foreach ((array)$data as $c => $values) { @@ -476,7 +477,8 @@ abstract class rcube_addressbook $fn = trim(join(' ', array_filter(array($contact['prefix'], $contact['firstname'], $contact['middlename'], $contact['surname'], $contact['suffix'])))); // use email address part for name - $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email']; + $email = self::get_col_values('email', $contact, true); + $email = $email[0]; if ($email && (empty($fn) || $fn == $email)) { // return full email @@ -523,9 +525,9 @@ abstract class rcube_addressbook $fn = $contact['name']; // fallback to email address - $email = is_array($contact['email']) ? $contact['email'][0] : $contact['email']; - if (empty($fn) && $email) - return $email; + if (empty($fn) && ($email = self::get_col_values('email', $contact, true)) && !empty($email)) { + return $email[0]; + } return $fn; } @@ -538,8 +540,8 @@ abstract class rcube_addressbook $key = $contact[$sort_col] . ':' . $contact['sourceid']; // add email to a key to not skip contacts with the same name (#1488375) - if (!empty($contact['email'])) { - $key .= ':' . implode(':', (array)$contact['email']); + if (($email = self::get_col_values('email', $contact, true)) && !empty($email)) { + $key .= ':' . implode(':', (array)$email); } return $key; @@ -561,9 +563,9 @@ abstract class rcube_addressbook // 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::strtotime($value)) - && ($search = rcube_utils::strtotime($search)) - && date('Ymd', $value) == date('Ymd', $search)); + return (($value = rcube_utils::anytodatetime($value)) + && ($search = rcube_utils::anytodatetime($search)) + && $value->format('Ymd') == $search->format('Ymd')); } // composite field, e.g. address diff --git a/program/lib/Roundcube/rcube_config.php b/program/lib/Roundcube/rcube_config.php index 90e1394cf..a3741758f 100644 --- a/program/lib/Roundcube/rcube_config.php +++ b/program/lib/Roundcube/rcube_config.php @@ -3,7 +3,7 @@ /* +-----------------------------------------------------------------------+ | This file is part of the Roundcube Webmail client | - | Copyright (C) 2008-2012, The Roundcube Dev Team | + | Copyright (C) 2008-2013, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -27,7 +27,7 @@ class rcube_config const DEFAULT_SKIN = 'larry'; private $env = ''; - private $basedir = 'config/'; + private $paths = array(); private $prop = array(); private $errors = array(); private $userprefs = array(); @@ -58,7 +58,32 @@ class rcube_config public function __construct($env = '') { $this->env = $env; - $this->basedir = RCUBE_CONFIG_DIR; + + if ($paths = getenv('RCUBE_CONFIG_PATH')) { + $this->paths = explode(PATH_SEPARATOR, $paths); + // make all paths absolute + foreach ($this->paths as $i => $path) { + if (!$this->_is_absolute($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(); @@ -138,17 +163,6 @@ class rcube_config // enable display_errors in 'show' level, but not for ajax requests ini_set('display_errors', intval(empty($_REQUEST['_remote']) && ($this->prop['debug_level'] & 4))); - // set timezone auto settings values - if ($this->prop['timezone'] == 'auto') { - $this->prop['_timezone_value'] = $this->client_timezone(); - } - else if (is_numeric($this->prop['timezone']) && ($tz = timezone_name_from_abbr("", $this->prop['timezone'] * 3600, 0))) { - $this->prop['timezone'] = $tz; - } - else if (empty($this->prop['timezone'])) { - $this->prop['timezone'] = 'UTC'; - } - // remove deprecated properties unset($this->prop['dst_active']); @@ -186,47 +200,73 @@ class rcube_config */ public function load_from_file($file) { - $fpath = $this->resolve_path($file); - 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 (is_array($config)) { - $this->merge($config); - return true; - } - // deprecated name of config variable - else if (is_array($rcmail_config)) { - $this->merge($rcmail_config); - return true; + $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 (is_array($config)) { + $this->merge($config); + $success = true; + } + // deprecated name of config variable + if (is_array($rcmail_config)) { + $this->merge($rcmail_config); + $success = true; + } } } - return false; + return $success; } /** - * Helper method to resolve the absolute path to the given config file. + * Helper method to resolve absolute paths to the given config file. * This also takes the 'env' property into account. + * + * @param string Filename or absolute file path + * @param boolean Return -$env file path if exists + * @return array List of candidates in config dir path(s) */ - public function resolve_path($file, $use_env = true) + public function resolve_paths($file, $use_env = true) { - if (strpos($file, '/') === false) { - $file = realpath($this->basedir . '/' . $file); - } + $files = array(); + $abs_path = $this->_is_absolute($file); - // check if <file>-env.ini exists - if ($file && $use_env && !empty($this->env)) { - $envfile = preg_replace('/\.(inc.php)$/', '-' . $this->env . '.\\1', $file); - if (is_file($envfile)) - return $envfile; + foreach ($this->paths as $basepath) { + $realpath = $abs_path ? $file : realpath($basepath . '/' . $file); + + // check if <file>-env.ini exists + if ($realpath && $use_env && !empty($this->env)) { + $envfile = preg_replace('/\.(inc.php)$/', '-' . $this->env . '.\\1', $realpath); + 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 $file; + return $files; } + /** + * Determine whether the given file path is absolute or relative + */ + private function _is_absolute($path) + { + return $path[0] == DIRECTORY_SEPARATOR || preg_match('!^[a-z]:[\\\\/]!i', $path); + } /** * Getter for a specific config parameter @@ -246,8 +286,10 @@ class rcube_config $rcube = rcube::get_instance(); - if ($name == 'timezone' && isset($this->prop['_timezone_value'])) { - $result = $this->prop['_timezone_value']; + if ($name == 'timezone') { + if (empty($result) || $result == 'auto') { + $result = $this->client_timezone(); + } } else if ($name == 'client_mimetypes') { if ($result == null && $def == null) @@ -305,11 +347,6 @@ class rcube_config } } - // convert user's timezone into the new format - if (is_numeric($prefs['timezone']) && ($tz = timezone_name_from_abbr('', $prefs['timezone'] * 3600, 0))) { - $prefs['timezone'] = $tz; - } - // larry is the new default skin :-) if ($prefs['skin'] == 'default') { $prefs['skin'] = self::DEFAULT_SKIN; @@ -317,13 +354,6 @@ class rcube_config $this->userprefs = $prefs; $this->prop = array_merge($this->prop, $prefs); - - // override timezone settings with client values - if ($this->prop['timezone'] == 'auto') { - $this->prop['_timezone_value'] = isset($_SESSION['timezone']) ? $this->client_timezone() : $this->prop['_timezone_value']; - } - else if (isset($this->prop['_timezone_value'])) - unset($this->prop['_timezone_value']); } @@ -464,13 +494,12 @@ class rcube_config */ private function client_timezone() { - if (isset($_SESSION['timezone']) && is_numeric($_SESSION['timezone']) - && ($ctz = timezone_name_from_abbr("", $_SESSION['timezone'] * 3600, 0))) { - return $ctz; - } - else if (!empty($_SESSION['timezone'])) { + // @TODO: remove this legacy timezone handling in the future + $props = $this->fix_legacy_props(array('timezone' => $_SESSION['timezone'])); + + if (!empty($props['timezone'])) { try { - $tz = timezone_open($_SESSION['timezone']); + $tz = new DateTimeZone($props['timezone']); return $tz->getName(); } catch (Exception $e) { /* gracefully ignore */ } @@ -498,6 +527,77 @@ class rcube_config } } + // 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']); + } + } + 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 + */ + 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( + '-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", + '300' => "Asia/Karachi", + '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", + ); + + return $timezones[(string) intval($offset * 60)]; + } } diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php index 3919cdc6e..6d01368a1 100644 --- a/program/lib/Roundcube/rcube_contacts.php +++ b/program/lib/Roundcube/rcube_contacts.php @@ -718,6 +718,10 @@ class rcube_contacts extends rcube_addressbook foreach ($save_data as $key => $values) { list($field, $section) = 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); + } foreach ((array)$values as $value) { if (isset($value)) $vcard->set($field, $value, $section); diff --git a/program/lib/Roundcube/rcube_csv2vcard.php b/program/lib/Roundcube/rcube_csv2vcard.php index fb8d8f103..00e6d4e20 100644 --- a/program/lib/Roundcube/rcube_csv2vcard.php +++ b/program/lib/Roundcube/rcube_csv2vcard.php @@ -145,6 +145,7 @@ class rcube_csv2vcard 'work_mobile' => 'phone:work,cell', 'work_title' => 'jobtitle', 'work_zip' => 'zipcode:work', + 'group' => 'groups', ); /** @@ -268,6 +269,7 @@ class rcube_csv2vcard 'work_mobile' => "Work Mobile", 'work_title' => "Work Title", 'work_zip' => "Work Zip", + 'groups' => "Group", ); protected $local_label_map = array(); diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php index 852070073..e66226ff5 100644 --- a/program/lib/Roundcube/rcube_db.php +++ b/program/lib/Roundcube/rcube_db.php @@ -386,17 +386,7 @@ class rcube_db $result = $this->dbh->query($query); if ($result === false) { - $error = $this->dbh->errorInfo(); - - if (empty($this->options['ignore_key_errors']) || $error[0] != '23000') { - $this->db_error = true; - $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); - - rcube::raise_error(array('code' => 500, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => $this->db_error_msg . " (SQL Query: $query)" - ), true, false); - } + $result = $this->handle_error($query); } $this->last_result = $result; @@ -405,6 +395,30 @@ class rcube_db } /** + * Helper method to handle DB errors. + * This by default logs the error but could be overriden by a driver implementation + * + * @param string Query that triggered the error + * @return mixed Result to be stored and returned + */ + protected function handle_error($query) + { + $error = $this->dbh->errorInfo(); + + if (empty($this->options['ignore_key_errors']) || $error[0] != '23000') { + $this->db_error = true; + $this->db_error_msg = sprintf('[%s] %s', $error[1], $error[2]); + + rcube::raise_error(array('code' => 500, 'type' => 'db', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => $this->db_error_msg . " (SQL Query: $query)" + ), true, false); + } + + return false; + } + + /** * Get number of affected rows for the last query * * @param mixed $result Optional query handle diff --git a/program/lib/Roundcube/rcube_db_mysql.php b/program/lib/Roundcube/rcube_db_mysql.php index 6fa5ad768..24f9ce1bd 100644 --- a/program/lib/Roundcube/rcube_db_mysql.php +++ b/program/lib/Roundcube/rcube_db_mysql.php @@ -179,4 +179,29 @@ class rcube_db_mysql extends rcube_db return isset($this->variables[$varname]) ? $this->variables[$varname] : $default; } + /** + * Handle DB errors, re-issue the query on deadlock errors from InnoDB row-level locking + * + * @param string Query that triggered the error + * @return mixed Result to be stored and returned + */ + protected function handle_error($query) + { + $error = $this->dbh->errorInfo(); + + // retry after "Deadlock found when trying to get lock" errors + $retries = 2; + while ($error[1] == 1213 && $retries >= 0) { + usleep(50000); // wait 50 ms + $result = $this->dbh->query($query); + if ($result !== false) { + return $result; + } + $error = $this->dbh->errorInfo(); + $retries--; + } + + return parent::handle_error($query); + } + } diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php index 689a6266d..aa074233f 100644 --- a/program/lib/Roundcube/rcube_imap.php +++ b/program/lib/Roundcube/rcube_imap.php @@ -3785,9 +3785,10 @@ class rcube_imap extends rcube_storage if ($this->messages_caching && !$this->mcache) { $rcube = rcube::get_instance(); if (($dbh = $rcube->get_dbh()) && ($userid = $rcube->get_user_id())) { - $ttl = $rcube->config->get('messages_cache_ttl', '10d'); + $ttl = $rcube->config->get('messages_cache_ttl', '10d'); + $threshold = $rcube->config->get('messages_cache_threshold', 50); $this->mcache = new rcube_imap_cache( - $dbh, $this, $userid, $this->options['skip_deleted'], $ttl); + $dbh, $this, $userid, $this->options['skip_deleted'], $ttl, $threshold); } } diff --git a/program/lib/Roundcube/rcube_imap_cache.php b/program/lib/Roundcube/rcube_imap_cache.php index 061ac546d..d72bfe0ab 100644 --- a/program/lib/Roundcube/rcube_imap_cache.php +++ b/program/lib/Roundcube/rcube_imap_cache.php @@ -56,6 +56,13 @@ class rcube_imap_cache private $ttl; /** + * Maximum cached message size + * + * @var int + */ + private $threshold; + + /** * Internal (in-memory) cache * * @var array @@ -96,9 +103,9 @@ class rcube_imap_cache * @param int $userid User identifier * @param bool $skip_deleted skip_deleted flag * @param string $ttl Expiration time of memcache/apc items - * + * @param int $threshold Maximum cached message size */ - function __construct($db, $imap, $userid, $skip_deleted, $ttl=0) + function __construct($db, $imap, $userid, $skip_deleted, $ttl=0, $threshold=0) { // convert ttl string to seconds $ttl = get_offset_sec($ttl); @@ -109,6 +116,7 @@ class rcube_imap_cache $this->userid = $userid; $this->skip_deleted = $skip_deleted; $this->ttl = $ttl; + $this->threshold = $threshold; } @@ -1155,13 +1163,13 @@ class rcube_imap_cache // Save current message from internal cache if ($message = $this->icache['__message']) { // clean up some object's data - $object = $this->message_object_prepare($message['object']); + $this->message_object_prepare($message['object']); // calculate current md5 sum - $md5sum = md5(serialize($object)); + $md5sum = md5(serialize($message['object'])); if ($message['md5sum'] != $md5sum) { - $this->add_message($message['mailbox'], $object, !$message['exists']); + $this->add_message($message['mailbox'], $message['object'], !$message['exists']); } $this->icache['__message']['md5sum'] = $md5sum; @@ -1171,12 +1179,19 @@ class rcube_imap_cache /** * Prepares message object to be stored in database. + * + * @param rcube_message_header|rcube_message_part */ - private function message_object_prepare($msg) + private function message_object_prepare(&$msg, &$size = 0) { - // Remove body too big (>25kB) - if ($msg->body && strlen($msg->body) > 25 * 1024) { - unset($msg->body); + // Remove body too big + if ($msg->body && ($length = strlen($msg->body))) { + $size += $length; + + if ($size > $this->threshold * 1024) { + $size -= $length; + unset($msg->body); + } } // Fix mimetype which might be broken by some code when message is displayed @@ -1186,13 +1201,19 @@ class rcube_imap_cache list($msg->ctype_primary, $msg->ctype_secondary) = explode('/', $msg->mimetype); } + unset($msg->replaces); + if (is_array($msg->structure->parts)) { - foreach ($msg->structure->parts as $idx => $part) { - $msg->structure->parts[$idx] = $this->message_object_prepare($part); + foreach ($msg->structure->parts as $part) { + $this->message_object_prepare($part, $size); } } - return $msg; + if (is_array($msg->parts)) { + foreach ($msg->parts as $part) { + $this->message_object_prepare($part, $size); + } + } } diff --git a/program/lib/Roundcube/rcube_ldap.php b/program/lib/Roundcube/rcube_ldap.php index cb7fa8466..78573789b 100644 --- a/program/lib/Roundcube/rcube_ldap.php +++ b/program/lib/Roundcube/rcube_ldap.php @@ -34,6 +34,7 @@ class rcube_ldap extends rcube_addressbook public $ready = false; public $group_id = 0; public $coltypes = array(); + public $export_groups = false; // private properties protected $ldap; diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php index 572540f47..96a8eac61 100644 --- a/program/lib/Roundcube/rcube_mime.php +++ b/program/lib/Roundcube/rcube_mime.php @@ -708,12 +708,20 @@ class rcube_mime */ public static function file_content_type($path, $name, $failover = 'application/octet-stream', $is_stream = false, $skip_suffix = false) { + static $mime_ext = array(); + $mime_type = null; - $mime_magic = rcube::get_instance()->config->get('mime_magic'); - $mime_ext = $skip_suffix ? null : @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); + $config = rcube::get_instance()->config; + $mime_magic = $config->get('mime_magic'); + + if (!$skip_suffix && empty($mime_ext)) { + foreach ($config->resolve_paths('mimetypes.php') as $fpath) { + $mime_ext = array_merge($mime_ext, (array) @include($fpath)); + } + } // use file name suffix with hard-coded mime-type map - if (is_array($mime_ext) && $name) { + if (!$skip_suffix && is_array($mime_ext) && $name) { if ($suffix = substr($name, strrpos($name, '.')+1)) { $mime_type = $mime_ext[strtolower($suffix)]; } @@ -818,7 +826,9 @@ class rcube_mime // fallback to some well-known types most important for daily emails if (empty($mime_types)) { - $mime_extensions = (array) @include(RCUBE_CONFIG_DIR . '/mimetypes.php'); + foreach (rcube::get_instance()->config->resolve_paths('mimetypes.php') as $fpath) { + $mime_extensions = array_merge($mime_extensions, (array) @include($fpath)); + } foreach ($mime_extensions as $ext => $mime) { $mime_types[$mime][] = $ext; diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php index 2540f779d..1d76ae508 100644 --- a/program/lib/Roundcube/rcube_utils.php +++ b/program/lib/Roundcube/rcube_utils.php @@ -787,6 +787,44 @@ class rcube_utils return (int) $ts; } + /** + * Date parsing function that turns the given value into a DateTime object + * + * @param string $date Date string + * + * @return object DateTime instance or false on failure + */ + public static function anytodatetime($date) + { + if (is_object($date) && is_a($date, 'DateTime')) { + return $date; + } + + $dt = false; + $date = trim($date); + + // try to parse string with DateTime first + if (!empty($date)) { + try { + $dt = new DateTime($date); + } + catch (Exception $e) { + // ignore + } + } + + // try our advanced strtotime() method + if (!$dt && ($timestamp = self::strtotime($date))) { + try { + $dt = new DateTime("@".$timestamp); + } + catch (Exception $e) { + // ignore + } + } + + return $dt; + } /* * Idn_to_ascii wrapper. diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php index a71305c4b..d54dc56ad 100644 --- a/program/lib/Roundcube/rcube_vcard.php +++ b/program/lib/Roundcube/rcube_vcard.php @@ -47,6 +47,7 @@ class rcube_vcard 'manager' => 'X-MANAGER', 'spouse' => 'X-SPOUSE', 'edit' => 'X-AB-EDIT', + 'groups' => 'CATEGORIES', ); private $typemap = array( 'IPHONE' => 'mobile', @@ -357,8 +358,8 @@ class rcube_vcard case 'birthday': case 'anniversary': - if (($val = rcube_utils::strtotime($value)) && ($fn = self::$fieldmap[$field])) { - $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); + if (($val = rcube_utils::anytodatetime($value)) && ($fn = self::$fieldmap[$field])) { + $this->raw[$fn][] = array(0 => $val->format('Y-m-d'), 'value' => array('date')); } break; @@ -756,7 +757,7 @@ class rcube_vcard * * @return string Joined and quoted string */ - private static function vcard_quote($s, $sep = ';') + public static function vcard_quote($s, $sep = ';') { if (is_array($s)) { foreach($s as $part) { @@ -765,7 +766,7 @@ class rcube_vcard return(implode($sep, (array)$r)); } - return strtr($s, array('\\' => '\\\\', "\r" => '', "\n" => '\n', ',' => '\,', ';' => '\;')); + return strtr($s, array('\\' => '\\\\', "\r" => '', "\n" => '\n', $sep => '\\'.$sep)); } /** |