summaryrefslogtreecommitdiff
path: root/program/lib/Roundcube
diff options
context:
space:
mode:
Diffstat (limited to 'program/lib/Roundcube')
-rw-r--r--program/lib/Roundcube/bootstrap.php19
-rw-r--r--program/lib/Roundcube/html.php14
-rw-r--r--program/lib/Roundcube/rcube.php26
-rw-r--r--program/lib/Roundcube/rcube_contacts.php4
-rw-r--r--program/lib/Roundcube/rcube_db.php45
-rw-r--r--program/lib/Roundcube/rcube_db_pgsql.php15
-rw-r--r--program/lib/Roundcube/rcube_image.php4
-rw-r--r--program/lib/Roundcube/rcube_imap.php3
-rw-r--r--program/lib/Roundcube/rcube_imap_generic.php61
-rw-r--r--program/lib/Roundcube/rcube_message.php37
-rw-r--r--program/lib/Roundcube/rcube_message_header.php7
-rw-r--r--program/lib/Roundcube/rcube_mime.php178
-rw-r--r--program/lib/Roundcube/rcube_plugin.php18
-rw-r--r--program/lib/Roundcube/rcube_plugin_api.php120
-rw-r--r--program/lib/Roundcube/rcube_session.php28
-rw-r--r--program/lib/Roundcube/rcube_string_replacer.php4
-rw-r--r--program/lib/Roundcube/rcube_utils.php16
-rw-r--r--program/lib/Roundcube/rcube_vcard.php4
18 files changed, 463 insertions, 140 deletions
diff --git a/program/lib/Roundcube/bootstrap.php b/program/lib/Roundcube/bootstrap.php
index 0640a9448..b7e69cb2a 100644
--- a/program/lib/Roundcube/bootstrap.php
+++ b/program/lib/Roundcube/bootstrap.php
@@ -31,16 +31,25 @@ $config = array(
// critical PHP settings here. Only these, which doesn't provide
// an error/warning in the logs later. See (#1486307).
'mbstring.func_overload' => 0,
- 'suhosin.session.encrypt' => 0,
- 'session.auto_start' => 0,
- 'file_uploads' => 1,
'magic_quotes_runtime' => 0,
'magic_quotes_sybase' => 0, // #1488506
);
+
+// check these additional ini settings if not called via CLI
+if (php_sapi_name() != 'cli') {
+ $config += array(
+ 'suhosin.session.encrypt' => 0,
+ 'session.auto_start' => 0,
+ 'file_uploads' => 1,
+ );
+}
+
foreach ($config as $optname => $optval) {
if ($optval != ini_get($optname) && @ini_set($optname, $optval) === false) {
- die("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).");
+ $error = "ERROR: Wrong '$optname' option value and it wasn't possible to set it to required value ($optval).\n"
+ . "Check your PHP configuration (including php_admin_flag).";
+ if (defined('STDERR')) fwrite(STDERR, $error); else echo $error;
+ exit(1);
}
}
diff --git a/program/lib/Roundcube/html.php b/program/lib/Roundcube/html.php
index 592720308..dbc9ca51f 100644
--- a/program/lib/Roundcube/html.php
+++ b/program/lib/Roundcube/html.php
@@ -35,6 +35,7 @@ class html
public static $common_attrib = array('id','class','style','title','align');
public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','thead','tbody','tr','th','td','style','script');
+
/**
* Constructor
*
@@ -217,7 +218,7 @@ class html
$attr = array('src' => $attr);
}
return self::tag('iframe', $attr, $cont, array_merge(self::$common_attrib,
- array('src','name','width','height','border','frameborder')));
+ array('src','name','width','height','border','frameborder','onload')));
}
/**
@@ -332,7 +333,16 @@ class html
*/
public static function quote($str)
{
- return @htmlspecialchars($str, ENT_COMPAT, RCUBE_CHARSET);
+ static $flags;
+
+ if (!$flags) {
+ $flags = ENT_COMPAT;
+ if (defined('ENT_SUBSTITUTE')) {
+ $flags |= ENT_SUBSTITUTE;
+ }
+ }
+
+ return @htmlspecialchars($str, $flags, RCUBE_CHARSET);
}
}
diff --git a/program/lib/Roundcube/rcube.php b/program/lib/Roundcube/rcube.php
index 3ae511e1e..b681f0531 100644
--- a/program/lib/Roundcube/rcube.php
+++ b/program/lib/Roundcube/rcube.php
@@ -405,6 +405,7 @@ class rcube
$sess_domain = $this->config->get('session_domain');
$sess_path = $this->config->get('session_path');
$lifetime = $this->config->get('session_lifetime', 0) * 60;
+ $is_secure = $this->config->get('use_https') || rcube_utils::https_check();
// set session domain
if ($sess_domain) {
@@ -419,7 +420,7 @@ class rcube
ini_set('session.gc_maxlifetime', $lifetime * 2);
}
- ini_set('session.cookie_secure', rcube_utils::https_check());
+ ini_set('session.cookie_secure', $is_secure);
ini_set('session.name', $sess_name ? $sess_name : 'roundcube_sessid');
ini_set('session.use_cookies', 1);
ini_set('session.use_only_cookies', 1);
@@ -1081,6 +1082,9 @@ class rcube
'message' => $arg->getMessage(),
);
}
+ else if (is_string($arg)) {
+ $arg = array('message' => $arg, 'type' => 'php');
+ }
if (empty($arg['code'])) {
$arg['code'] = 500;
@@ -1093,14 +1097,24 @@ class rcube
return;
}
- if (($log || $terminate) && $arg['type'] && $arg['message']) {
+ $cli = php_sapi_name() == 'cli';
+
+ if (($log || $terminate) && !$cli && $arg['type'] && $arg['message']) {
$arg['fatal'] = $terminate;
self::log_bug($arg);
}
- // display error page and terminate script
- if ($terminate && is_object(self::$instance->output)) {
- self::$instance->output->raise_error($arg['code'], $arg['message']);
+ // terminate script
+ if ($terminate) {
+ // display error page
+ if (is_object(self::$instance->output)) {
+ self::$instance->output->raise_error($arg['code'], $arg['message']);
+ }
+ else if ($cli) {
+ fwrite(STDERR, 'ERROR: ' . $arg['message']);
+ }
+
+ exit(1);
}
}
@@ -1139,7 +1153,7 @@ class rcube
if (!self::write_log('errors', $log_entry)) {
// send error to PHPs error handler if write_log didn't succeed
- trigger_error($arg_arr['message']);
+ trigger_error($arg_arr['message'], E_USER_WARNING);
}
}
diff --git a/program/lib/Roundcube/rcube_contacts.php b/program/lib/Roundcube/rcube_contacts.php
index c66e98687..e4fd7dc10 100644
--- a/program/lib/Roundcube/rcube_contacts.php
+++ b/program/lib/Roundcube/rcube_contacts.php
@@ -626,10 +626,6 @@ class rcube_contacts extends rcube_addressbook
$insert_id = $this->db->insert_id($this->db_name);
}
- // also add the newly created contact to the active group
- if ($insert_id && $this->group_id)
- $this->add_to_group($this->group_id, $insert_id);
-
$this->cache = null;
return $insert_id;
diff --git a/program/lib/Roundcube/rcube_db.php b/program/lib/Roundcube/rcube_db.php
index 49bbe5c6e..d86e3dd98 100644
--- a/program/lib/Roundcube/rcube_db.php
+++ b/program/lib/Roundcube/rcube_db.php
@@ -444,17 +444,20 @@ class rcube_db
*
* @param mixed $result Optional query handle
* @return mixed Number of rows or false on failure
+ * @deprecated This method shows very poor performance and should be avoided.
*/
public function num_rows($result = null)
{
if ($result || ($result === null && ($result = $this->last_result))) {
// repeat query with SELECT COUNT(*) ...
- if (preg_match('/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/i', $result->queryString, $m)) {
+ if (preg_match('/^SELECT\s+(?:ALL\s+|DISTINCT\s+)?(?:.*?)\s+FROM\s+(.*)$/ims', $result->queryString, $m)) {
$query = $this->dbh->query('SELECT COUNT(*) FROM ' . $m[1], PDO::FETCH_NUM);
return $query ? intval($query->fetchColumn(0)) : false;
}
else {
- return count($result->fetchAll());
+ $num = count($result->fetchAll());
+ $result->execute(); // re-execute query because there's no seek(0)
+ return $num;
}
}
@@ -631,6 +634,22 @@ class rcube_db
}
/**
+ * Escapes a string so it can be safely used in a query
+ *
+ * @param string $str A string to escape
+ *
+ * @return string Escaped string for use in a query
+ */
+ public function escape($str)
+ {
+ if (is_null($str)) {
+ return 'NULL';
+ }
+
+ return substr($this->quote($str), 1, -1);
+ }
+
+ /**
* Quotes a string so it can be safely used as a table or column name
*
* @param string $str Value to quote
@@ -645,6 +664,20 @@ class rcube_db
}
/**
+ * Escapes a string so it can be safely used in a query
+ *
+ * @param string $str A string to escape
+ *
+ * @return string Escaped string for use in a query
+ * @deprecated Replaced by rcube_db::escape
+ * @see rcube_db::escape
+ */
+ public function escapeSimple($str)
+ {
+ return $this->escape($str);
+ }
+
+ /**
* Quotes a string so it can be safely used as a table or column name
*
* @param string $str Value to quote
@@ -813,11 +846,9 @@ class rcube_db
{
$rcube = rcube::get_instance();
- // return table name if configured
- $config_key = 'db_table_'.$table;
-
- if ($name = $rcube->config->get($config_key)) {
- return $name;
+ // add prefix to the table name if configured
+ if ($prefix = $rcube->config->get('db_prefix')) {
+ return $prefix . $table;
}
return $table;
diff --git a/program/lib/Roundcube/rcube_db_pgsql.php b/program/lib/Roundcube/rcube_db_pgsql.php
index cf23c5e48..adfd2207b 100644
--- a/program/lib/Roundcube/rcube_db_pgsql.php
+++ b/program/lib/Roundcube/rcube_db_pgsql.php
@@ -53,19 +53,20 @@ class rcube_db_pgsql extends rcube_db
/**
* Return correct name for a specific database sequence
*
- * @param string $sequence Secuence name
+ * @param string $table Table name
*
* @return string Translated sequence name
*/
- protected function sequence_name($sequence)
+ protected function sequence_name($table)
{
- $rcube = rcube::get_instance();
+ // Note: we support only one sequence per table
+ // Note: The sequence name must be <table_name>_seq
+ $sequence = $table . '_seq';
+ $rcube = rcube::get_instance();
// return sequence name if configured
- $config_key = 'db_sequence_'.$sequence;
-
- if ($name = $rcube->config->get($config_key)) {
- return $name;
+ if ($prefix = $rcube->config->get('db_prefix')) {
+ return $prefix . $sequence;
}
return $sequence;
diff --git a/program/lib/Roundcube/rcube_image.php b/program/lib/Roundcube/rcube_image.php
index a55ba1600..735a0df01 100644
--- a/program/lib/Roundcube/rcube_image.php
+++ b/program/lib/Roundcube/rcube_image.php
@@ -124,6 +124,7 @@ class rcube_image
}
if ($result === '') {
+ @chmod($filename, 0600);
return $type;
}
}
@@ -183,6 +184,7 @@ class rcube_image
}
if ($result) {
+ @chmod($filename, 0600);
return $type;
}
}
@@ -223,6 +225,7 @@ class rcube_image
$result = rcube::exec($convert . ' 2>&1 -colorspace RGB -quality 75 {in} {type}:{out}', $p);
if ($result === '') {
+ @chmod($filename, 0600);
return true;
}
}
@@ -256,6 +259,7 @@ class rcube_image
}
if ($result) {
+ @chmod($filename, 0600);
return true;
}
}
diff --git a/program/lib/Roundcube/rcube_imap.php b/program/lib/Roundcube/rcube_imap.php
index 0aa059c26..c67985186 100644
--- a/program/lib/Roundcube/rcube_imap.php
+++ b/program/lib/Roundcube/rcube_imap.php
@@ -3372,7 +3372,6 @@ class rcube_imap extends rcube_storage
{
if (!empty($this->options['fetch_headers'])) {
$headers = explode(' ', $this->options['fetch_headers']);
- $headers = array_map('strtoupper', $headers);
}
else {
$headers = array();
@@ -3382,7 +3381,7 @@ class rcube_imap extends rcube_storage
$headers = array_merge($headers, $this->all_headers);
}
- return implode(' ', array_unique($headers));
+ return $headers;
}
diff --git a/program/lib/Roundcube/rcube_imap_generic.php b/program/lib/Roundcube/rcube_imap_generic.php
index 2ac1355fd..460e6cc92 100644
--- a/program/lib/Roundcube/rcube_imap_generic.php
+++ b/program/lib/Roundcube/rcube_imap_generic.php
@@ -2265,24 +2265,53 @@ class rcube_imap_generic
return $result;
}
- function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add = '')
+ /**
+ * Returns message(s) data (flags, headers, etc.)
+ *
+ * @param string $mailbox Mailbox name
+ * @param mixed $message_set Message(s) sequence identifier(s) or UID(s)
+ * @param bool $is_uid True if $message_set contains UIDs
+ * @param bool $bodystr Enable to add BODYSTRUCTURE data to the result
+ * @param array $add_headers List of additional headers
+ *
+ * @return bool|array List of rcube_message_header elements, False on error
+ */
+ function fetchHeaders($mailbox, $message_set, $is_uid = false, $bodystr = false, $add_headers = array())
{
$query_items = array('UID', 'RFC822.SIZE', 'FLAGS', 'INTERNALDATE');
- if ($bodystr)
+ $headers = array('DATE', 'FROM', 'TO', 'SUBJECT', 'CONTENT-TYPE', 'CC', 'REPLY-TO',
+ 'LIST-POST', 'DISPOSITION-NOTIFICATION-TO', 'X-PRIORITY');
+
+ if (!empty($add_headers)) {
+ $add_headers = array_map('strtoupper', $add_headers);
+ $headers = array_unique(array_merge($headers, $add_headers));
+ }
+
+ if ($bodystr) {
$query_items[] = 'BODYSTRUCTURE';
- $query_items[] = 'BODY.PEEK[HEADER.FIELDS ('
- . 'DATE FROM TO SUBJECT CONTENT-TYPE CC REPLY-TO LIST-POST DISPOSITION-NOTIFICATION-TO X-PRIORITY'
- . ($add ? ' ' . trim($add) : '')
- . ')]';
+ }
+
+ $query_items[] = 'BODY.PEEK[HEADER.FIELDS (' . implode(' ', $headers) . ')]';
$result = $this->fetch($mailbox, $message_set, $is_uid, $query_items);
return $result;
}
- function fetchHeader($mailbox, $id, $uidfetch=false, $bodystr=false, $add='')
+ /**
+ * Returns message data (flags, headers, etc.)
+ *
+ * @param string $mailbox Mailbox name
+ * @param int $id Message sequence identifier or UID
+ * @param bool $is_uid True if $id is an UID
+ * @param bool $bodystr Enable to add BODYSTRUCTURE data to the result
+ * @param array $add_headers List of additional headers
+ *
+ * @return bool|rcube_message_header Message data, False on error
+ */
+ function fetchHeader($mailbox, $id, $is_uid = false, $bodystr = false, $add_headers = array())
{
- $a = $this->fetchHeaders($mailbox, $id, $uidfetch, $bodystr, $add);
+ $a = $this->fetchHeaders($mailbox, $id, $is_uid, $bodystr, $add_headers);
if (is_array($a)) {
return array_shift($a);
}
@@ -3638,8 +3667,20 @@ class rcube_imap_generic
*/
static function strToTime($date)
{
- // support non-standard "GMTXXXX" literal
- $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
+ // Clean malformed data
+ $date = preg_replace(
+ array(
+ '/GMT\s*([+-][0-9]+)/', // support non-standard "GMTXXXX" literal
+ '/[^a-z0-9\x20\x09:+-]/i', // remove any invalid characters
+ '/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*/i', // remove weekday names
+ ),
+ array(
+ '\\1',
+ '',
+ '',
+ ), $date);
+
+ $date = trim($date);
// if date parsing fails, we have a date in non-rfc format
// remove token from the end and try again
diff --git a/program/lib/Roundcube/rcube_message.php b/program/lib/Roundcube/rcube_message.php
index 42d7b9bbe..9db1fa30a 100644
--- a/program/lib/Roundcube/rcube_message.php
+++ b/program/lib/Roundcube/rcube_message.php
@@ -85,12 +85,13 @@ class rcube_message
$this->headers = $this->storage->get_message($uid);
- if (!$this->headers)
+ if (!$this->headers) {
return;
+ }
$this->mime = new rcube_mime($this->headers->charset);
- $this->subject = $this->mime->decode_mime_string($this->headers->subject);
+ $this->subject = $this->headers->get('subject');
list(, $this->sender) = each($this->mime->decode_address_list($this->headers->from, 1));
$this->set_safe((intval($_GET['_safe']) || $_SESSION['safe_messages'][$this->folder.':'.$uid]));
@@ -125,15 +126,11 @@ class rcube_message
*/
public function get_header($name, $raw = false)
{
- if (empty($this->headers))
+ if (empty($this->headers)) {
return null;
+ }
- if ($this->headers->$name)
- $value = $this->headers->$name;
- else if ($this->headers->others[$name])
- $value = $this->headers->others[$name];
-
- return $raw ? $value : $this->mime->decode_header($value);
+ return $this->headers->get($name, !$raw);
}
@@ -152,12 +149,13 @@ class rcube_message
* Compose a valid URL for getting a message part
*
* @param string $mime_id Part MIME-ID
+ * @param mixed $embed Mimetype class for parts to be embedded
* @return string URL or false if part does not exist
*/
public function get_part_url($mime_id, $embed = false)
{
if ($this->mime_parts[$mime_id])
- return $this->opt['get_url'] . '&_part=' . $mime_id . ($embed ? '&_embed=1' : '');
+ return $this->opt['get_url'] . '&_part=' . $mime_id . ($embed ? '&_embed=1&_mimeclass=' . $embed : '');
else
return false;
}
@@ -364,7 +362,7 @@ class rcube_message
// parse headers from message/rfc822 part
if (!isset($structure->headers['subject']) && !isset($structure->headers['from'])) {
- list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 8192));
+ list($headers, $dump) = explode("\r\n\r\n", $this->get_part_content($structure->mime_id, null, true, 32768));
$structure->headers = rcube_mime::parse_headers($headers);
}
}
@@ -372,7 +370,8 @@ class rcube_message
$mimetype = $structure->mimetype;
// show message headers
- if ($recursive && is_array($structure->headers) && (isset($structure->headers['subject']) || isset($structure->headers['from']))) {
+ if ($recursive && is_array($structure->headers) &&
+ (isset($structure->headers['subject']) || $structure->headers['from'] || $structure->headers['to'])) {
$c = new stdClass;
$c->type = 'headers';
$c->headers = $structure->headers;
@@ -486,14 +485,6 @@ class rcube_message
$this->parts[] = $c;
}
- // add html part as attachment
- if ($html_part !== null && $structure->parts[$html_part] !== $print_part) {
- $html_part = $structure->parts[$html_part];
- $html_part->mimetype = 'text/html';
-
- $this->attachments[] = $html_part;
- }
-
// add unsupported/unrecognized parts to attachments list
if ($attach_part) {
$this->attachments[] = $structure->parts[$attach_part];
@@ -578,10 +569,6 @@ class rcube_message
if (!empty($mail_part->filename)) {
$this->attachments[] = $mail_part;
}
- // list html part as attachment (here the part is most likely inside a multipart/related part)
- else if ($this->parse_alternative && ($secondary_type == 'html' && !$this->opt['prefer_html'])) {
- $this->attachments[] = $mail_part;
- }
}
// part message/*
else if ($primary_type == 'message') {
@@ -657,7 +644,7 @@ class rcube_message
$img_regexp = '/^image\/(gif|jpe?g|png|tiff|bmp|svg)/';
foreach ($this->inline_parts as $inline_object) {
- $part_url = $this->get_part_url($inline_object->mime_id, true);
+ $part_url = $this->get_part_url($inline_object->mime_id, $inline_object->ctype_primary);
if (isset($inline_object->content_id))
$a_replaces['cid:'.$inline_object->content_id] = $part_url;
if ($inline_object->content_location) {
diff --git a/program/lib/Roundcube/rcube_message_header.php b/program/lib/Roundcube/rcube_message_header.php
index 274ae7f9f..2c5e2b6c8 100644
--- a/program/lib/Roundcube/rcube_message_header.php
+++ b/program/lib/Roundcube/rcube_message_header.php
@@ -215,7 +215,12 @@ class rcube_message_header
$value = $this->others[$name];
}
- return $decode ? rcube_mime::decode_header($value, $this->charset) : $value;
+ if ($decode) {
+ $value = rcube_mime::decode_header($value, $this->charset);
+ $value = rcube_charset::clean($value);
+ }
+
+ return $value;
}
/**
diff --git a/program/lib/Roundcube/rcube_mime.php b/program/lib/Roundcube/rcube_mime.php
index b70d681c9..96296a57c 100644
--- a/program/lib/Roundcube/rcube_mime.php
+++ b/program/lib/Roundcube/rcube_mime.php
@@ -564,82 +564,122 @@ class rcube_mime
/**
- * Improved wordwrap function.
+ * Improved wordwrap function with multibyte support.
+ * The code is based on Zend_Text_MultiByte::wordWrap().
*
- * @param string $string Text to wrap
- * @param int $width Line width
- * @param string $break Line separator
- * @param bool $cut Enable to cut word
- * @param string $charset Charset of $string
+ * @param string $string Text to wrap
+ * @param int $width Line width
+ * @param string $break Line separator
+ * @param bool $cut Enable to cut word
+ * @param string $charset Charset of $string
+ * @param bool $wrap_quoted When enabled quoted lines will not be wrapped
*
* @return string Text
*/
- public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null)
+ public static function wordwrap($string, $width=75, $break="\n", $cut=false, $charset=null, $wrap_quoted=true)
{
- if ($charset && function_exists('mb_internal_encoding')) {
- mb_internal_encoding($charset);
+ if (!$charset) {
+ $charset = RCUBE_CHARSET;
}
- $para = preg_split('/\r?\n/', $string);
- $string = '';
-
- while (count($para)) {
- $line = array_shift($para);
- if ($line[0] == '>') {
- $string .= $line . (count($para) ? $break : '');
- continue;
+ // detect available functions
+ $strlen_func = function_exists('iconv_strlen') ? 'iconv_strlen' : 'mb_strlen';
+ $strpos_func = function_exists('iconv_strpos') ? 'iconv_strpos' : 'mb_strpos';
+ $strrpos_func = function_exists('iconv_strrpos') ? 'iconv_strrpos' : 'mb_strrpos';
+ $substr_func = function_exists('iconv_substr') ? 'iconv_substr' : 'mb_substr';
+
+ // Convert \r\n to \n, this is our line-separator
+ $string = str_replace("\r\n", "\n", $string);
+ $separator = "\n"; // must be 1 character length
+ $result = array();
+
+ while (($stringLength = $strlen_func($string, $charset)) > 0) {
+ $breakPos = $strpos_func($string, $separator, 0, $charset);
+
+ // quoted line (do not wrap)
+ if ($wrap_quoted && $string[0] == '>') {
+ if ($breakPos === $stringLength - 1 || $breakPos === false) {
+ $subString = $string;
+ $cutLength = null;
+ }
+ else {
+ $subString = $substr_func($string, 0, $breakPos, $charset);
+ $cutLength = $breakPos + 1;
+ }
+ }
+ // next line found and current line is shorter than the limit
+ else if ($breakPos !== false && $breakPos < $width) {
+ if ($breakPos === $stringLength - 1) {
+ $subString = $string;
+ $cutLength = null;
+ }
+ else {
+ $subString = $substr_func($string, 0, $breakPos, $charset);
+ $cutLength = $breakPos + 1;
+ }
}
+ else {
+ $subString = $substr_func($string, 0, $width, $charset);
- $list = explode(' ', $line);
- $len = 0;
- while (count($list)) {
- $line = array_shift($list);
- $l = mb_strlen($line);
- $space = $len ? 1 : 0;
- $newlen = $len + $l + $space;
-
- if ($newlen <= $width) {
- $string .= ($space ? ' ' : '').$line;
- $len += ($space + $l);
+ // last line
+ if ($breakPos === false && $subString === $string) {
+ $cutLength = null;
}
else {
- if ($l > $width) {
- if ($cut) {
- $start = 0;
- while ($l) {
- $str = mb_substr($line, $start, $width);
- $strlen = mb_strlen($str);
- $string .= ($len ? $break : '').$str;
- $start += $strlen;
- $l -= $strlen;
- $len = $strlen;
- }
+ $nextChar = $substr_func($string, $width, 1, $charset);
+
+ if ($nextChar === ' ' || $nextChar === $separator) {
+ $afterNextChar = $substr_func($string, $width + 1, 1, $charset);
+
+ if ($afterNextChar === false) {
+ $subString .= $nextChar;
+ }
+
+ $cutLength = $strlen_func($subString, $charset) + 1;
+ }
+ else {
+ if ($strrpos_func[0] == 'm') {
+ $spacePos = $strrpos_func($subString, ' ', 0, $charset);
}
else {
- $string .= ($len ? $break : '').$line;
- if (count($list)) {
- $string .= $break;
+ $spacePos = $strrpos_func($subString, ' ', $charset);
+ }
+
+ if ($spacePos !== false) {
+ $subString = $substr_func($subString, 0, $spacePos, $charset);
+ $cutLength = $spacePos + 1;
+ }
+ else if ($cut === false) {
+ $spacePos = $strpos_func($string, ' ', 0, $charset);
+
+ if ($spacePos !== false && $spacePos < $breakPos) {
+ $subString = $substr_func($string, 0, $spacePos, $charset);
+ $cutLength = $spacePos + 1;
+ }
+ else {
+ $subString = $string;
+ $cutLength = null;
}
- $len = 0;
}
- }
- else {
- $string .= $break.$line;
- $len = $l;
+ else {
+ $subString = $substr_func($subString, 0, $width, $charset);
+ $cutLength = $width;
+ }
}
}
}
- if (count($para)) {
- $string .= $break;
- }
- }
+ $result[] = $subString;
- if ($charset && function_exists('mb_internal_encoding')) {
- mb_internal_encoding(RCUBE_CHARSET);
+ if ($cutLength !== null) {
+ $string = $substr_func($string, $cutLength, ($stringLength - $cutLength), $charset);
+ }
+ else {
+ break;
+ }
}
- return $string;
+ return implode($break, $result);
}
@@ -769,11 +809,35 @@ class rcube_mime
// fallback to some well-known types most important for daily emails
if (empty($mime_types)) {
- $mime_extensions = @include(RCUBE_CONFIG_DIR . '/mimetypes.php');
- $mime_extensions += array('gif' => 'image/gif', 'png' => 'image/png', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'tif' => 'image/tiff');
+ $mime_extensions = (array) @include(RCUBE_CONFIG_DIR . '/mimetypes.php');
- foreach ($mime_extensions as $ext => $mime)
+ foreach ($mime_extensions as $ext => $mime) {
$mime_types[$mime][] = $ext;
+ }
+ }
+
+ // Add some known aliases that aren't included by some mime.types (#1488891)
+ // the order is important here so standard extensions have higher prio
+ $aliases = array(
+ 'image/gif' => array('gif'),
+ 'image/png' => array('png'),
+ 'image/x-png' => array('png'),
+ 'image/jpeg' => array('jpg', 'jpeg', 'jpe'),
+ 'image/jpg' => array('jpg', 'jpeg', 'jpe'),
+ 'image/pjpeg' => array('jpg', 'jpeg', 'jpe'),
+ 'image/tiff' => array('tif'),
+ 'message/rfc822' => array('eml'),
+ 'text/x-mail' => array('eml'),
+ );
+
+ foreach ($aliases as $mime => $exts) {
+ $mime_types[$mime] = array_unique(array_merge((array) $mime_types[$mime], $exts));
+
+ foreach ($exts as $ext) {
+ if (!isset($mime_extensions[$ext])) {
+ $mime_extensions[$ext] = $mime;
+ }
+ }
}
return $mimetype ? $mime_types[$mimetype] : $mime_extensions;
diff --git a/program/lib/Roundcube/rcube_plugin.php b/program/lib/Roundcube/rcube_plugin.php
index 9ea0f73d3..d24a2693c 100644
--- a/program/lib/Roundcube/rcube_plugin.php
+++ b/program/lib/Roundcube/rcube_plugin.php
@@ -60,6 +60,14 @@ abstract class rcube_plugin
*/
public $noframe = false;
+ /**
+ * A list of config option names that can be modified
+ * by the user via user interface (with save-prefs command)
+ *
+ * @var array
+ */
+ public $allowed_prefs;
+
protected $home;
protected $urlbase;
private $mytask;
@@ -84,6 +92,16 @@ abstract class rcube_plugin
abstract function init();
/**
+ * Provide information about this
+ *
+ * @return array Meta information about a plugin or false if not implemented
+ */
+ public static function info()
+ {
+ return false;
+ }
+
+ /**
* Attempt to load the given plugin which is required for the current plugin
*
* @param string Plugin name
diff --git a/program/lib/Roundcube/rcube_plugin_api.php b/program/lib/Roundcube/rcube_plugin_api.php
index 111c177d9..4bb6c6677 100644
--- a/program/lib/Roundcube/rcube_plugin_api.php
+++ b/program/lib/Roundcube/rcube_plugin_api.php
@@ -36,6 +36,7 @@ class rcube_plugin_api
public $task = '';
public $output;
public $handlers = array();
+ public $allowed_prefs = array();
protected $plugins = array();
protected $tasks = array();
@@ -202,6 +203,11 @@ class rcube_plugin_api
$plugin->init();
$this->plugins[$plugin_name] = $plugin;
}
+
+ if (!empty($plugin->allowed_prefs)) {
+ $this->allowed_prefs = array_merge($this->allowed_prefs, $plugin->allowed_prefs);
+ }
+
return true;
}
}
@@ -222,6 +228,120 @@ class rcube_plugin_api
}
/**
+ * Get information about a specific plugin.
+ * This is either provided my a plugin's info() method or extracted from a package.xml or a composer.json file
+ *
+ * @param string Plugin name
+ * @return array Meta information about a plugin or False if plugin was not found
+ */
+ public function get_info($plugin_name)
+ {
+ static $composer_lock, $license_uris = array(
+ 'Apache' => 'http://www.apache.org/licenses/LICENSE-2.0.html',
+ 'Apache-2' => 'http://www.apache.org/licenses/LICENSE-2.0.html',
+ 'Apache-1' => 'http://www.apache.org/licenses/LICENSE-1.0',
+ 'Apache-1.1' => 'http://www.apache.org/licenses/LICENSE-1.1',
+ 'GPL' => 'http://www.gnu.org/licenses/gpl.html',
+ 'GPLv2' => 'http://www.gnu.org/licenses/gpl-2.0.html',
+ 'GPL-2.0' => 'http://www.gnu.org/licenses/gpl-2.0.html',
+ 'GPLv3' => 'http://www.gnu.org/licenses/gpl-3.0.html',
+ 'GPL-3.0' => 'http://www.gnu.org/licenses/gpl-3.0.html',
+ 'GPL-3.0+' => 'http://www.gnu.org/licenses/gpl.html',
+ 'GPL-2.0+' => 'http://www.gnu.org/licenses/gpl.html',
+ 'LGPL' => 'http://www.gnu.org/licenses/lgpl.html',
+ 'LGPLv2' => 'http://www.gnu.org/licenses/lgpl-2.0.html',
+ 'LGPLv2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html',
+ 'LGPLv3' => 'http://www.gnu.org/licenses/lgpl.html',
+ 'LGPL-2.0' => 'http://www.gnu.org/licenses/lgpl-2.0.html',
+ 'LGPL-2.1' => 'http://www.gnu.org/licenses/lgpl-2.1.html',
+ 'LGPL-3.0' => 'http://www.gnu.org/licenses/lgpl.html',
+ 'LGPL-3.0+' => 'http://www.gnu.org/licenses/lgpl.html',
+ 'BSD' => 'http://opensource.org/licenses/bsd-license.html',
+ 'BSD-2-Clause' => 'http://opensource.org/licenses/BSD-2-Clause',
+ 'BSD-3-Clause' => 'http://opensource.org/licenses/BSD-3-Clause',
+ 'FreeBSD' => 'http://opensource.org/licenses/BSD-2-Clause',
+ 'MIT' => 'http://www.opensource.org/licenses/mit-license.php',
+ 'PHP' => 'http://opensource.org/licenses/PHP-3.0',
+ 'PHP-3' => 'http://www.php.net/license/3_01.txt',
+ 'PHP-3.0' => 'http://www.php.net/license/3_0.txt',
+ 'PHP-3.01' => 'http://www.php.net/license/3_01.txt',
+ );
+
+ $dir = dir($this->dir);
+ $fn = unslashify($dir->path) . DIRECTORY_SEPARATOR . $plugin_name . DIRECTORY_SEPARATOR . $plugin_name . '.php';
+ $info = false;
+
+ if (!class_exists($plugin_name))
+ include($fn);
+
+ if (class_exists($plugin_name))
+ $info = $plugin_name::info();
+
+ // fall back to composer.json file
+ if (!$info) {
+ $composer = INSTALL_PATH . "/plugins/$plugin_name/composer.json";
+ if (file_exists($composer) && ($json = @json_decode(file_get_contents($composer), true))) {
+ list($info['vendor'], $info['name']) = explode('/', $json['name']);
+ $info['license'] = $json['license'];
+ if ($license_uri = $license_uris[$info['license']])
+ $info['license_uri'] = $license_uri;
+ }
+
+ // read local composer.lock file (once)
+ if (!isset($composer_lock)) {
+ $composer_lock = @json_decode(@file_get_contents(INSTALL_PATH . "/composer.lock"), true);
+ if ($composer_lock['packages']) {
+ foreach ($composer_lock['packages'] as $i => $package) {
+ $composer_lock['installed'][$package['name']] = $package;
+ }
+ }
+ }
+
+ // load additional information from local composer.lock file
+ if ($lock = $composer_lock['installed'][$json['name']]) {
+ $info['version'] = $lock['version'];
+ $info['uri'] = $lock['homepage'] ? $lock['homepage'] : $lock['source']['uri'];
+ $info['src_uri'] = $lock['dist']['uri'] ? $lock['dist']['uri'] : $lock['source']['uri'];
+ }
+ }
+
+ // fall back to package.xml file
+ if (!$info) {
+ $package = INSTALL_PATH . "/plugins/$plugin_name/package.xml";
+ if (file_exists($package) && ($file = file_get_contents($package))) {
+ $doc = new DOMDocument();
+ $doc->loadXML($file);
+ $xpath = new DOMXPath($doc);
+ $xpath->registerNamespace('rc', "http://pear.php.net/dtd/package-2.0");
+ $data = array();
+
+ // XPaths of plugin metadata elements
+ $metadata = array(
+ 'name' => 'string(//rc:package/rc:name)',
+ 'version' => 'string(//rc:package/rc:version/rc:release)',
+ 'license' => 'string(//rc:package/rc:license)',
+ 'license_uri' => 'string(//rc:package/rc:license/@uri)',
+ 'src_uri' => 'string(//rc:package/rc:srcuri)',
+ 'uri' => 'string(//rc:package/rc:uri)',
+ );
+
+ foreach ($metadata as $key => $path) {
+ $info[$key] = $xpath->evaluate($path);
+ }
+
+ // dependent required plugins (can be used, but not included in config)
+ $deps = $xpath->evaluate('//rc:package/rc:dependencies/rc:required/rc:package/rc:name');
+ for ($i = 0; $i < $deps->length; $i++) {
+ $dn = $deps->item($i)->nodeValue;
+ $info['requires'][] = $dn;
+ }
+ }
+ }
+
+ return $info;
+ }
+
+ /**
* Allows a plugin object to register a callback for a certain hook
*
* @param string $hook Hook name
diff --git a/program/lib/Roundcube/rcube_session.php b/program/lib/Roundcube/rcube_session.php
index 82ff8a804..dedde2284 100644
--- a/program/lib/Roundcube/rcube_session.php
+++ b/program/lib/Roundcube/rcube_session.php
@@ -203,10 +203,15 @@ class rcube_session
if (is_array($a_oldvars)) {
// remove unset keys on oldvars
foreach ((array)$this->unsets as $var) {
- $path = explode('.', $var);
- $k = array_pop($path);
- $node = &$this->get_node($path, $a_oldvars);
- unset($node[$k]);
+ if (isset($a_oldvars[$var])) {
+ unset($a_oldvars[$var]);
+ }
+ else {
+ $path = explode('.', $var);
+ $k = array_pop($path);
+ $node = &$this->get_node($path, $a_oldvars);
+ unset($node[$k]);
+ }
}
$newvars = $this->serialize(array_merge(
@@ -402,7 +407,7 @@ class rcube_session
/**
* Unset a session variable
*
- * @param string Varibale name (can be a path denoting a certain node in the session array, e.g. compose.attachments.5)
+ * @param string Variable name (can be a path denoting a certain node in the session array, e.g. compose.attachments.5)
* @return boolean True on success
*/
public function remove($var=null)
@@ -413,10 +418,15 @@ class rcube_session
$this->unsets[] = $var;
- $path = explode('.', $var);
- $key = array_pop($path);
- $node = &$this->get_node($path, $_SESSION);
- unset($node[$key]);
+ if (isset($_SESSION[$var])) {
+ unset($_SESSION[$var]);
+ }
+ else {
+ $path = explode('.', $var);
+ $key = array_pop($path);
+ $node = &$this->get_node($path, $_SESSION);
+ unset($node[$key]);
+ }
return true;
}
diff --git a/program/lib/Roundcube/rcube_string_replacer.php b/program/lib/Roundcube/rcube_string_replacer.php
index b8768bc98..0fc90a55a 100644
--- a/program/lib/Roundcube/rcube_string_replacer.php
+++ b/program/lib/Roundcube/rcube_string_replacer.php
@@ -95,12 +95,12 @@ class rcube_string_replacer
$attrib = (array)$this->options['link_attribs'];
$attrib['href'] = $url_prefix . $url;
- $i = $this->add($prefix . html::a($attrib, rcube::Q($url)) . $suffix);
+ $i = $this->add(html::a($attrib, rcube::Q($url)) . $suffix);
}
// Return valid link for recognized schemes, otherwise
// return the unmodified string for unrecognized schemes.
- return $i >= 0 ? $this->get_replacement($i) : $matches[0];
+ return $i >= 0 ? $prefix . $this->get_replacement($i) : $matches[0];
}
/**
diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
index 1ae782a25..fabe0f060 100644
--- a/program/lib/Roundcube/rcube_utils.php
+++ b/program/lib/Roundcube/rcube_utils.php
@@ -729,8 +729,20 @@ class rcube_utils
return $date;
}
- // support non-standard "GMTXXXX" literal
- $date = preg_replace('/GMT\s*([+-][0-9]+)/', '\\1', $date);
+ // Clean malformed data
+ $date = preg_replace(
+ array(
+ '/GMT\s*([+-][0-9]+)/', // support non-standard "GMTXXXX" literal
+ '/[^a-z0-9\x20\x09:+-]/i', // remove any invalid characters
+ '/\s*(Mon|Tue|Wed|Thu|Fri|Sat|Sun)\s*/i', // remove weekday names
+ ),
+ array(
+ '\\1',
+ '',
+ '',
+ ), $date);
+
+ $date = trim($date);
// if date parsing fails, we have a date in non-rfc format.
// remove token from the end and try again
diff --git a/program/lib/Roundcube/rcube_vcard.php b/program/lib/Roundcube/rcube_vcard.php
index de28767f8..54bb9521d 100644
--- a/program/lib/Roundcube/rcube_vcard.php
+++ b/program/lib/Roundcube/rcube_vcard.php
@@ -491,7 +491,9 @@ class rcube_vcard
if (preg_match('/^END:VCARD$/i', $line)) {
// parse vcard
$obj = new rcube_vcard(self::cleanup($vcard_block), $charset, true, self::$fieldmap);
- if (!empty($obj->displayname) || !empty($obj->email)) {
+ // FN and N is required by vCard format (RFC 2426)
+ // on import we can be less restrictive, let's addressbook decide
+ if (!empty($obj->displayname) || !empty($obj->surname) || !empty($obj->firstname) || !empty($obj->email)) {
$out[] = $obj;
}