From 413df054ad3235c59c24e897b616c569adc4f67b Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 12 Aug 2012 11:47:45 +0200 Subject: CS fixes (mostly tab -> spaces) --- program/include/clisetup.php | 49 +++++++++++++++++++----------------- program/include/html.php | 26 +++++++++++-------- program/include/rcube_cache.php | 4 +-- program/include/rcube_config.php | 4 +-- program/include/rcube_imap.php | 22 ++++++++-------- program/include/rcube_ldap.php | 8 +++--- program/include/rcube_plugin_api.php | 4 +-- program/include/rcube_session.php | 11 ++++---- program/include/rcube_smtp.php | 2 +- program/include/rcube_user.php | 2 +- program/include/rcube_utils.php | 10 ++++---- 11 files changed, 75 insertions(+), 67 deletions(-) (limited to 'program/include') diff --git a/program/include/clisetup.php b/program/include/clisetup.php index 039020bdc..a9af90a6f 100644 --- a/program/include/clisetup.php +++ b/program/include/clisetup.php @@ -33,33 +33,36 @@ require_once INSTALL_PATH . 'program/include/iniset.php'; */ function get_opt($aliases = array()) { - $args = array(); - for ($i=1; $i < count($_SERVER['argv']); $i++) { - $arg = $_SERVER['argv'][$i]; - $value = true; - $key = null; + $args = array(); - if ($arg[0] == '-') { - $key = preg_replace('/^-+/', '', $arg); - $sp = strpos($arg, '='); - if ($sp > 0) { - $key = substr($key, 0, $sp - 2); - $value = substr($arg, $sp+1); - } - else if (strlen($_SERVER['argv'][$i+1]) && $_SERVER['argv'][$i+1][0] != '-') { - $value = $_SERVER['argv'][++$i]; - } + for ($i=1; $i < count($_SERVER['argv']); $i++) { + $arg = $_SERVER['argv'][$i]; + $value = true; + $key = null; - $args[$key] = is_string($value) ? preg_replace(array('/^["\']/', '/["\']$/'), '', $value) : $value; - } - else - $args[] = $arg; + if ($arg[0] == '-') { + $key = preg_replace('/^-+/', '', $arg); + $sp = strpos($arg, '='); + if ($sp > 0) { + $key = substr($key, 0, $sp - 2); + $value = substr($arg, $sp+1); + } + else if (strlen($_SERVER['argv'][$i+1]) && $_SERVER['argv'][$i+1][0] != '-') { + $value = $_SERVER['argv'][++$i]; + } - if ($alias = $aliases[$key]) - $args[$alias] = $args[$key]; - } + $args[$key] = is_string($value) ? preg_replace(array('/^["\']/', '/["\']$/'), '', $value) : $value; + } + else { + $args[] = $arg; + } - return $args; + if ($alias = $aliases[$key]) { + $args[$alias] = $args[$key]; + } + } + + return $args; } diff --git a/program/include/html.php b/program/include/html.php index b42da1d7f..880c90463 100644 --- a/program/include/html.php +++ b/program/include/html.php @@ -171,7 +171,7 @@ class html $attr = array('href' => $attr); } return self::tag('a', $attr, $cont, array_merge(self::$common_attrib, - array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); + array('href','target','name','rel','onclick','onmouseover','onmouseout','onmousedown','onmouseup'))); } /** @@ -532,7 +532,7 @@ class html_textarea extends html { protected $tagname = 'textarea'; protected $allowed = array('name','rows','cols','wrap','tabindex', - 'onchange','disabled','readonly','spellcheck'); + 'onchange','disabled','readonly','spellcheck'); /** * Get HTML code for this object @@ -563,7 +563,7 @@ class html_textarea extends html } return self::tag($this->tagname, $this->attrib, $value, - array_merge(self::$common_attrib, $this->allowed)); + array_merge(self::$common_attrib, $this->allowed)); } } @@ -591,7 +591,7 @@ class html_select extends html protected $tagname = 'select'; protected $options = array(); protected $allowed = array('name','size','tabindex','autocomplete', - 'multiple','onchange','disabled','rel'); + 'multiple','onchange','disabled','rel'); /** * Add a new option to this drop-down @@ -655,7 +655,7 @@ class html_table extends html { protected $tagname = 'table'; protected $allowed = array('id','class','style','width','summary', - 'cellpadding','cellspacing','border'); + 'cellpadding','cellspacing','border'); private $header = array(); private $rows = array(); @@ -705,8 +705,9 @@ class html_table extends html */ public function add_header($attr, $cont) { - if (is_string($attr)) - $attr = array('class' => $attr); + if (is_string($attr)) { + $attr = array('class' => $attr); + } $cell = new stdClass; $cell->attrib = $attr; @@ -763,11 +764,13 @@ class html_table extends html */ public function set_row_attribs($attr = array(), $index = null) { - if (is_string($attr)) - $attr = array('class' => $attr); + if (is_string($attr)) { + $attr = array('class' => $attr); + } - if ($index === null) + if ($index === null) { $index = $this->rowindex; + } $this->rows[$index]->attrib = $attr; } @@ -781,8 +784,9 @@ class html_table extends html */ public function get_row_attribs($index = null) { - if ($index === null) + if ($index === null) { $index = $this->rowindex; + } return $this->rows[$index] ? $this->rows[$index]->attrib : null; } diff --git a/program/include/rcube_cache.php b/program/include/rcube_cache.php index cdb1dd52f..4e60deaff 100644 --- a/program/include/rcube_cache.php +++ b/program/include/rcube_cache.php @@ -254,7 +254,7 @@ class rcube_cache } else if ($this->type == 'apc') { $data = apc_fetch($this->ckey($key)); - } + } if ($data) { $md5sum = md5($data); @@ -294,7 +294,7 @@ class rcube_cache } $this->cache[$key] = $data; - $this->cache_sums[$key] = $md5sum; + $this->cache_sums[$key] = $md5sum; } else { $this->cache[$key] = null; diff --git a/program/include/rcube_config.php b/program/include/rcube_config.php index 2fe0d9745..e2997906c 100644 --- a/program/include/rcube_config.php +++ b/program/include/rcube_config.php @@ -324,7 +324,7 @@ class rcube_config if (strlen($key) != 24) { rcube::raise_error(array( 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, + 'file' => __FILE__, 'line' => __LINE__, 'message' => "Configured crypto key '$key' is not exactly 24 bytes long" ), true, true); } @@ -348,7 +348,7 @@ class rcube_config else rcube::raise_error(array( 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, + 'file' => __FILE__, 'line' => __LINE__, 'message' => "Invalid mail_header_delimiter setting" ), true, false); } diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 4ab06cf60..461ecb0d2 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -359,11 +359,11 @@ class rcube_imap extends rcube_storage return array( $this->search_string, - $this->search_set, - $this->search_charset, - $this->search_sort_field, - $this->search_sorted, - ); + $this->search_set, + $this->search_charset, + $this->search_sort_field, + $this->search_sorted, + ); } @@ -3847,12 +3847,12 @@ class rcube_imap extends rcube_storage protected function rsort($folder, $delimiter, &$list, &$out) { while (list($key, $name) = each($list)) { - if (strpos($name, $folder.$delimiter) === 0) { - // set the type of folder name variable (#1485527) - $out[] = (string) $name; - unset($list[$key]); - $this->rsort($name, $delimiter, $list, $out); - } + if (strpos($name, $folder.$delimiter) === 0) { + // set the type of folder name variable (#1485527) + $out[] = (string) $name; + unset($list[$key]); + $this->rsort($name, $delimiter, $list, $out); + } } reset($list); } diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index 3a7fc1805..7f51b8fa7 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -2027,12 +2027,12 @@ class rcube_ldap extends rcube_addressbook # a0 = type context-specific/constructed with a length of 06 (6) bytes following # 02 = type integer with 2 bytes following (offset): 01 01 (ie 1) # 02 = type integer with 2 bytes following (contentCount): 01 00 - + # whith a search string present: # 81 = type context-specific/constructed with a length of 04 (4) bytes following (the length will change here) # 81 indicates a user string is present where as a a0 indicates just a offset search # 81 = type context-specific/constructed with a length of 06 (6) bytes following - + # the following info was taken from the ISO/IEC 8825-1:2003 x.690 standard re: the # encoding of integer values (note: these values are in # two-complement form so since offset will never be negative bit 8 of the @@ -2042,7 +2042,7 @@ class rcube_ldap extends rcube_addressbook # of the second (to the left of first octet) octet: # a) shall not all be ones; and # b) shall not all be zero - + if ($search) { $search = preg_replace('/[^-[:alpha:] ,.()0-9]+/', '', $search); @@ -2062,7 +2062,7 @@ class rcube_ldap extends rcube_addressbook // now compute length over $str $str = self::_ber_addseq($str, 'a0'); } - + // now tack on records per page $str = "020100" . self::_ber_addseq(self::_ber_encode_int($rpp-1), '02') . $str; diff --git a/program/include/rcube_plugin_api.php b/program/include/rcube_plugin_api.php index 8c1e125ce..9ef68cab9 100644 --- a/program/include/rcube_plugin_api.php +++ b/program/include/rcube_plugin_api.php @@ -32,12 +32,12 @@ if (!defined('RCMAIL_PLUGINS_DIR')) class rcube_plugin_api { static private $instance; - + public $dir; public $url = 'plugins/'; public $task = ''; public $output; - + public $handlers = array(); private $plugins = array(); private $tasks = array(); diff --git a/program/include/rcube_session.php b/program/include/rcube_session.php index b6a0ccf62..6192466cd 100644 --- a/program/include/rcube_session.php +++ b/program/include/rcube_session.php @@ -531,7 +531,7 @@ class rcube_session public function set_keep_alive($keep_alive) { $this->keep_alive = $keep_alive; - + if ($this->lifetime < $keep_alive) $this->set_lifetime($keep_alive + 30); } @@ -551,7 +551,7 @@ class rcube_session { return $this->ip; } - + /** * Setter for cookie encryption secret */ @@ -568,7 +568,8 @@ class rcube_session { $this->ip_check = $check; } - + + /** * Setter for the cookie name used for session cookie */ @@ -605,7 +606,7 @@ class rcube_session $result = true; } } - } + } if (!$result) $this->log("Session authentication failed for " . $this->key . "; invalid auth cookie sent; timeslot = " . date('Y-m-d H:i:s', $prev)); @@ -637,7 +638,7 @@ class rcube_session } /** - * + * Writes debug information to the log */ function log($line) { diff --git a/program/include/rcube_smtp.php b/program/include/rcube_smtp.php index e5748248c..b28be5206 100644 --- a/program/include/rcube_smtp.php +++ b/program/include/rcube_smtp.php @@ -423,7 +423,7 @@ class rcube_smtp $lines[] = $key . ': ' . $value; } } - + return array($from, join(SMTP_MIME_CRLF, $lines) . SMTP_MIME_CRLF); } diff --git a/program/include/rcube_user.php b/program/include/rcube_user.php index 644d24bd9..29eb0f26e 100644 --- a/program/include/rcube_user.php +++ b/program/include/rcube_user.php @@ -443,7 +443,7 @@ class rcube_user } $data = $rcube->plugins->exec_hook('user_create', - array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email, 'host'=>$host)); + array('user'=>$user, 'user_name'=>$user_name, 'user_email'=>$user_email, 'host'=>$host)); // plugin aborted this operation if ($data['abort']) diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index 9344a929b..d1a8315ec 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -84,9 +84,9 @@ class rcube_utils // from PEAR::Validate $regexp = '&^(?: - ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name - ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) - $&xi'; + ("\s*(?:[^"\f\n\r\t\v\b\s]+\s*)+")| #1 quoted name + ([-\w!\#\$%\&\'*+~/^`|{}=]+(?:\.[-\w!\#\$%\&\'*+~/^`|{}=]+)*)) #2 OR dot-atom (RFC5322) + $&xi'; if (!preg_match($regexp, $local_part)) { return false; @@ -617,8 +617,8 @@ class rcube_utils // %n - host $n = preg_replace('/:\d+$/', '', $_SERVER['SERVER_NAME']); // %t - host name without first part, e.g. %n=mail.domain.tld, %t=domain.tld - $t = preg_replace('/^[^\.]+\./', '', $n); - // %d - domain name without first part + $t = preg_replace('/^[^\.]+\./', '', $n); + // %d - domain name without first part $d = preg_replace('/^[^\.]+\./', '', $_SERVER['HTTP_HOST']); // %h - IMAP host $h = $_SESSION['storage_host'] ? $_SESSION['storage_host'] : $host; -- cgit v1.2.3 From e1cfb0bc01395bc71162cdd986234f3fe009edb4 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 14 Aug 2012 14:09:39 +0200 Subject: s/rcmail/rcube/ --- program/include/rcube_ldap.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index 3a7fc1805..5561613db 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -767,9 +767,9 @@ class rcube_ldap extends rcube_addressbook } // use VLV pseudo-search for autocompletion - $rcmail = rcmail::get_instance(); + $rcube = rcube::get_instance(); - if ($this->prop['vlv_search'] && $this->conn && join(',', (array)$fields) == join(',', $rcmail->config->get('contactlist_fields'))) + if ($this->prop['vlv_search'] && $this->conn && join(',', (array)$fields) == join(',', $rcube->config->get('contactlist_fields'))) { // add general filter to query if (!empty($this->prop['filter']) && empty($this->filter)) -- cgit v1.2.3 From b8dc3e0e61311fe03f21761fd7de1ca80d10c990 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 14 Aug 2012 15:10:05 +0200 Subject: Disable autocapitalization in login form on iPad/iPhone (#1488609) --- CHANGELOG | 1 + program/include/html.php | 2 +- program/include/rcube_output_html.php | 3 +++ 3 files changed, 5 insertions(+), 1 deletion(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 3441f1e03..827bb5c0c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Disable autocapitalization in login form on iPad/iPhone (#1488609) - Fix focus on the list when list row is clicked (#1488600) - Added separate From and To columns apart from smart From/To column (#1486891) - Fix fallback to Larry skin when configured skin isn't available (#1488591) diff --git a/program/include/html.php b/program/include/html.php index b42da1d7f..6d177e16d 100644 --- a/program/include/html.php +++ b/program/include/html.php @@ -358,7 +358,7 @@ class html_inputfield extends html protected $tagname = 'input'; protected $type = 'text'; protected $allowed = array( - 'type','name','value','size','tabindex', + 'type','name','value','size','tabindex','autocapitalize', 'autocomplete','checked','onchange','onclick','disabled','readonly', 'spellcheck','results','maxlength','src','multiple','placeholder', ); diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 30512d227..0a8f0e364 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -1378,6 +1378,9 @@ class rcube_output_html extends rcube_output if (empty($url) && !preg_match('/_(task|action)=logout/', $_SERVER['QUERY_STRING'])) $url = $_SERVER['QUERY_STRING']; + // Disable autocapitalization on iPad/iPhone (#1488609) + $attrib['autocapitalize'] = 'off'; + // set atocomplete attribute $user_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off'); $host_attrib = $autocomplete > 0 ? array() : array('autocomplete' => 'off'); -- cgit v1.2.3 From 0db8d00d298f8f67d6843f016a5a6259edff9f96 Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Wed, 15 Aug 2012 15:15:54 +0200 Subject: PDO: quote null values as NULL instad of an empty string --- program/include/rcube_db.php | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'program/include') diff --git a/program/include/rcube_db.php b/program/include/rcube_db.php index 042ca15e4..f97d70ab3 100644 --- a/program/include/rcube_db.php +++ b/program/include/rcube_db.php @@ -576,6 +576,10 @@ class rcube_db return intval($input); } + if (is_null($input)) { + return 'NULL'; + } + // create DB handle if not available if (!$this->dbh) { $this->db_connect('r'); -- cgit v1.2.3 From fb2f825cd95677a2ede38e0a2ea99b6823841d74 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 15 Aug 2012 15:32:02 +0200 Subject: Fix line separator in exported messages (#1488603) --- CHANGELOG | 1 + program/include/rcube_imap.php | 7 +++++-- program/include/rcube_imap_generic.php | 4 ++-- program/include/rcube_storage.php | 5 ++++- program/steps/mail/viewsource.inc | 2 +- 5 files changed, 13 insertions(+), 6 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 37170fc65..4cbaa6ece 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix line separator in exported messages (#1488603) - Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613) - Fix XSS issue where href="javascript:" wasn't secured (#1488613) - Fix impossible to create message with empty plain text part (#1488610) diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 461ecb0d2..5dd9c1250 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -2138,14 +2138,17 @@ class rcube_imap extends rcube_storage /** * Sends the whole message source to stdout + * + * @param int $uid Message UID + * @param bool $formatted Enables line-ending formatting */ - public function print_raw_body($uid) + public function print_raw_body($uid, $formatted = true) { if (!$this->check_connection()) { return; } - $this->conn->handlePartBody($this->folder, $uid, true, NULL, NULL, true); + $this->conn->handlePartBody($this->folder, $uid, true, null, null, true, null, $formatted); } diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index 8d956f2b9..915a11aad 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -2377,7 +2377,7 @@ class rcube_imap_generic return $this->handlePartBody($mailbox, $id, $is_uid, $part); } - function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL) + function handlePartBody($mailbox, $id, $is_uid=false, $part='', $encoding=NULL, $print=NULL, $file=NULL, $formatted=true) { if (!$this->select($mailbox)) { return false; @@ -2494,7 +2494,7 @@ class rcube_imap_generic continue; $line = convert_uudecode($line); // default - } else { + } else if ($formatted) { $line = rtrim($line, "\t\r\n\0\x0B") . "\n"; } diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php index 1075b0f43..768a26d73 100644 --- a/program/include/rcube_storage.php +++ b/program/include/rcube_storage.php @@ -502,8 +502,11 @@ abstract class rcube_storage /** * Sends the whole message source to stdout + * + * @param int $uid Message UID + * @param bool $formatted Enables line-ending formatting */ - abstract function print_raw_body($uid); + abstract function print_raw_body($uid, $formatted = true); /** diff --git a/program/steps/mail/viewsource.inc b/program/steps/mail/viewsource.inc index 59ccb386e..c560d7d41 100644 --- a/program/steps/mail/viewsource.inc +++ b/program/steps/mail/viewsource.inc @@ -44,7 +44,7 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_GET)) header("Content-Disposition: attachment; filename=\"$filename\""); } - $RCMAIL->storage->print_raw_body($uid); + $RCMAIL->storage->print_raw_body($uid, empty($_GET['_save'])); } else { -- cgit v1.2.3 From 6ab9369eb194e4dde0cc830a84466dd240e95b23 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 15 Aug 2012 16:21:34 +0200 Subject: Fix lower-casing email address on replies (#1488598) --- CHANGELOG | 1 + program/include/rcube_shared.inc | 23 +++++++++++++++++++++++ program/steps/mail/compose.inc | 9 +++++---- 3 files changed, 29 insertions(+), 4 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 4cbaa6ece..192ecce91 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix lower-casing email address on replies (#1488598) - Fix line separator in exported messages (#1488603) - Fix XSS issue where plain signatures wasn't secured in HTML mode (#1488613) - Fix XSS issue where href="javascript:" wasn't secured (#1488613) diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 85f278432..5b839d8d2 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -305,6 +305,29 @@ function format_email_recipient($email, $name = '') } +/** + * Format e-mail address + * + * @param string $email E-mail address + * + * @return string Formatted e-mail address + */ +function format_email($email) +{ + $email = trim($email); + $parts = explode('@', $email); + $count = count($parts); + + if ($count > 1) { + $parts[$count-1] = mb_strtolower($parts[$count-1]); + + $email = implode('@', $parts); + } + + return $email; +} + + /** * mbstring replacement functions */ diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 1a1d244e1..56f4a052b 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -252,7 +252,8 @@ $MESSAGE->identities = $RCMAIL->user->list_identities(); if (count($MESSAGE->identities)) { foreach ($MESSAGE->identities as $idx => $ident) { - $email = mb_strtolower(rcube_idn_to_utf8($ident['email'])); + $ident['email'] = format_email($ident['email']); + $email = format_email(rcube_idn_to_utf8($ident['email'])); $MESSAGE->identities[$idx]['email_ascii'] = $ident['email']; $MESSAGE->identities[$idx]['ident'] = format_email_recipient($ident['email'], $ident['name']); @@ -277,7 +278,7 @@ else if (count($MESSAGE->identities)) { $a_to = rcube_mime::decode_address_list($MESSAGE->headers->to, null, true, $MESSAGE->headers->charset); foreach ($a_to as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = strtolower($addr['mailto']); + $a_recipients[] = format_email($addr['mailto']); $a_names[] = $addr['name']; } } @@ -286,7 +287,7 @@ else if (count($MESSAGE->identities)) { $a_cc = rcube_mime::decode_address_list($MESSAGE->headers->cc, null, true, $MESSAGE->headers->charset); foreach ($a_cc as $addr) { if (!empty($addr['mailto'])) { - $a_recipients[] = strtolower($addr['mailto']); + $a_recipients[] = format_email($addr['mailto']); $a_names[] = $addr['name']; } } @@ -433,7 +434,7 @@ foreach ($parts as $header) { if (empty($addr_part['mailto'])) continue; - $mailto = mb_strtolower(rcube_idn_to_utf8($addr_part['mailto'])); + $mailto = format_email(rcube_idn_to_utf8($addr_part['mailto'])); if (!in_array($mailto, $a_recipients) && ($header == 'to' || empty($MESSAGE->compose['from_email']) || $mailto != $MESSAGE->compose['from_email']) -- cgit v1.2.3 From 287eff030a7c6437495dc15badb125640cc4c3d3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 16 Aug 2012 09:36:49 +0200 Subject: Make $inline_parts property publicly available --- program/include/rcube_message.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/include') diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php index 9d36acf38..f550b574e 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -50,13 +50,13 @@ class rcube_message */ private $mime; private $opt = array(); - private $inline_parts = array(); private $parse_alternative = false; public $uid = null; public $headers; public $parts = array(); public $mime_parts = array(); + public $inline_parts = array(); public $attachments = array(); public $subject = ''; public $sender = null; -- cgit v1.2.3 From fb001f851f60e99b4ba9d2f837a76a46dfd3fd5f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 16 Aug 2012 12:00:35 +0200 Subject: Force at least one subtype of address to be specified. Fixes issue where contact address wasn't displayed at all. --- program/include/rcube_ldap.php | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'program/include') diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index dbab0fd06..ad2ccddeb 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -139,6 +139,11 @@ class rcube_ldap extends rcube_addressbook unset($this->coltypes[$childcol]); // remove address child col from global coltypes list } } + + // at least one address type must be specified + if (empty($this->coltypes['address']['subtypes'])) { + $this->coltypes['address']['subtypes'] = array('home'); + } } else if ($this->coltypes['address']) { $this->coltypes['address'] += array('type' => 'textarea', 'childs' => null, 'size' => 40); -- cgit v1.2.3 From c72325faed3d244170650a1bf62ddca6eb1b5fa9 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 17 Aug 2012 15:00:12 +0200 Subject: Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593) --- CHANGELOG | 1 + config/main.inc.php.dist | 7 ++++--- program/include/rcmail.php | 21 ++++++++++++--------- 3 files changed, 17 insertions(+), 12 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 3edbdd494..b1f9ce1cf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix bug where domain name was converted to lower-case even with login_lc=false (#1488593) - Rewritten test scripts for PHPUnit - Fix lower-casing email address on replies (#1488598) - Fix line separator in exported messages (#1488603) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 58f9ca865..1e9c1fdcd 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -224,11 +224,12 @@ $rcmail_config['use_https'] = false; // 0 - disabled, 1 - username and host only, 2 - username, host, password $rcmail_config['login_autocomplete'] = 0; -// If users authentication is not case sensitive this must be enabled. -// You can also use it to force conversion of logins to lower case. +// Forces conversion of logins to lower case. +// 0 - disabled, 1 - only domain part, 2 - domain and local part. +// If users authentication is not case-sensitive this must be enabled. // After enabling it all user records need to be updated, e.g. with query: // UPDATE users SET username = LOWER(username); -$rcmail_config['login_lc'] = false; +$rcmail_config['login_lc'] = 0; // Includes should be interpreted as PHP files $rcmail_config['skin_include_php'] = false; diff --git a/program/include/rcmail.php b/program/include/rcmail.php index a6b0bcd57..d866919d6 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -453,7 +453,14 @@ class rcmail extends rcube // Convert username to lowercase. If storage backend // is case-insensitive we need to store always the same username (#1487113) if ($config['login_lc']) { - $username = mb_strtolower($username); + if ($config['login_lc'] == 2 || $config['login_lc'] === true) { + $username = mb_strtolower($username); + } + else if (strpos($username, '@')) { + // lowercase domain name + list($local, $domain) = explode('@', $username); + $username = $local . '@' . mb_strtolower($domain); + } } // try to resolve email address from virtuser table @@ -463,17 +470,13 @@ class rcmail extends rcube // Here we need IDNA ASCII // Only rcube_contacts class is using domain names in Unicode - $host = rcube_utils::idn_to_ascii($host); - if (strpos($username, '@')) { - // lowercase domain name - list($local, $domain) = explode('@', $username); - $username = $local . '@' . mb_strtolower($domain); - $username = rcube_utils::idn_to_ascii($username); - } + $host = rcube_utils::idn_to_ascii($host); + $username = rcube_utils::idn_to_ascii($username); // user already registered -> overwrite username - if ($user = rcube_user::query($username, $host)) + if ($user = rcube_user::query($username, $host)) { $username = $user->data['username']; + } $storage = $this->get_storage(); -- cgit v1.2.3 From c83b83eeae9806cb60ea3f41f2cff055b0c6ed7e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 17 Aug 2012 15:37:04 +0200 Subject: Fix domain part check in email address validation function. Added test cases. --- program/include/rcube_utils.php | 5 +++ tests/Utils.php | 74 +++++++++++++++++++++++++++++++++++++++++ tests/phpunit.xml | 1 + 3 files changed, 80 insertions(+) create mode 100644 tests/Utils.php (limited to 'program/include') diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index d1a8315ec..9f18b79c4 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -110,6 +110,11 @@ class rcube_utils } } + // last domain part + if (preg_match('/[^a-zA-Z]/', array_pop($domain_array))) { + return false; + } + $rcube = rcube::get_instance(); if (!$dns_check || !$rcube->config->get('email_dns_check')) { diff --git a/tests/Utils.php b/tests/Utils.php new file mode 100644 index 000000000..648b39989 --- /dev/null +++ b/tests/Utils.php @@ -0,0 +1,74 @@ +', 'Encoded html within email is invalid'), + array('email.domain.com', 'Missing @'), + array('email@domain@domain.com', 'Two @ sign'), + array('.email@domain.com', 'Leading dot in address is not allowed'), + array('email.@domain.com', 'Trailing dot in address is not allowed'), + array('email..email@domain.com', 'Multiple dots'), + array('あいうえお@domain.com', 'Unicode char as address'), + array('email@domain.com (Joe Smith)', 'Text followed email is not allowed'), + array('email@domain', 'Missing top level domain (.com/.net/.org/etc)'), + array('email@-domain.com', 'Leading dash in front of domain is invalid'), +// array('email@domain.web', '.web is not a valid top level domain'), + array('email@111.222.333.44444', 'Invalid IP format'), + array('email@domain..com', 'Multiple dot in the domain portion is invalid'), + ); + } + + /** + * @dataProvider data_valid_email + */ + function test_valid_email($email, $title) + { + $this->assertTrue(rcube_utils::check_email($email, false), $title); + } + + /** + * @dataProvider data_invalid_email + */ + function test_invalid_email($email, $title) + { + $this->assertFalse(rcube_utils::check_email($email, false), $title); + } + +} diff --git a/tests/phpunit.xml b/tests/phpunit.xml index 4a3b883cf..cfd066e29 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -7,6 +7,7 @@ MailDecode.php MailFunc.php ModCss.php + Utils.php VCards.php -- cgit v1.2.3 From 31541824ab156f225ede89028d3988688ce42a6e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 17 Aug 2012 20:25:40 +0200 Subject: CS fixes, mostly indentation --- program/include/html.php | 2 +- program/include/rcube.php | 1434 +++++++++++++++++++------------------ program/include/rcube_browser.php | 2 - 3 files changed, 740 insertions(+), 698 deletions(-) (limited to 'program/include') diff --git a/program/include/html.php b/program/include/html.php index d15d50875..c6507f813 100644 --- a/program/include/html.php +++ b/program/include/html.php @@ -154,7 +154,7 @@ class html $attr = array('src' => $attr); } return self::tag('img', $attr + array('alt' => ''), null, array_merge(self::$common_attrib, - array('src','alt','width','height','border','usemap','onclick'))); + array('src','alt','width','height','border','usemap','onclick'))); } /** diff --git a/program/include/rcube.php b/program/include/rcube.php index 494b5c3dd..84014ef5c 100644 --- a/program/include/rcube.php +++ b/program/include/rcube.php @@ -25,364 +25,372 @@ * Base class of the Roundcube Framework * implemented as singleton * - * @package Core + * @package Framework + * @subpackage Core */ class rcube { - const INIT_WITH_DB = 1; - const INIT_WITH_PLUGINS = 2; - - /** - * Singleton instace of rcube - * - * @var rcmail - */ - static protected $instance; - - /** - * Stores instance of rcube_config. - * - * @var rcube_config - */ - public $config; - - /** - * Instace of database class. - * - * @var rcube_pdo - */ - public $db; - - /** - * Instace of Memcache class. - * - * @var Memcache - */ - public $memcache; - - /** - * Instace of rcube_session class. - * - * @var rcube_session - */ - public $session; - - /** - * Instance of rcube_smtp class. - * - * @var rcube_smtp - */ - public $smtp; - - /** - * Instance of rcube_storage class. - * - * @var rcube_storage - */ - public $storage; - - /** - * Instance of rcube_output class. - * - * @var rcube_output - */ - public $output; - - /** - * Instance of rcube_plugin_api. - * - * @var rcube_plugin_api - */ - public $plugins; - - - /* private/protected vars */ - protected $texts; - protected $caches = array(); - protected $shutdown_functions = array(); - protected $expunge_cache = false; - - - /** - * This implements the 'singleton' design pattern - * - * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants - * @return rcube The one and only instance - */ - static function get_instance($mode = 0) - { - if (!self::$instance) { - self::$instance = new rcube(); - self::$instance->init($mode); - } + const INIT_WITH_DB = 1; + const INIT_WITH_PLUGINS = 2; - return self::$instance; - } - - - /** - * Private constructor - */ - protected function __construct() - { - // load configuration - $this->config = new rcube_config(); - $this->plugins = new rcube_dummy_plugin_api; - - register_shutdown_function(array($this, 'shutdown')); - } - - - /** - * Initial startup function - */ - protected function init($mode = 0) - { - // initialize syslog - if ($this->config->get('log_driver') == 'syslog') { - $syslog_id = $this->config->get('syslog_id', 'roundcube'); - $syslog_facility = $this->config->get('syslog_facility', LOG_USER); - openlog($syslog_id, LOG_ODELAY, $syslog_facility); - } + /** + * Singleton instace of rcube + * + * @var rcmail + */ + static protected $instance; - // connect to database - if ($mode & self::INIT_WITH_DB) { - $this->get_dbh(); - } + /** + * Stores instance of rcube_config. + * + * @var rcube_config + */ + public $config; + + /** + * Instace of database class. + * + * @var rcube_db + */ + public $db; + + /** + * Instace of Memcache class. + * + * @var Memcache + */ + public $memcache; + + /** + * Instace of rcube_session class. + * + * @var rcube_session + */ + public $session; + + /** + * Instance of rcube_smtp class. + * + * @var rcube_smtp + */ + public $smtp; + + /** + * Instance of rcube_storage class. + * + * @var rcube_storage + */ + public $storage; + + /** + * Instance of rcube_output class. + * + * @var rcube_output + */ + public $output; + + /** + * Instance of rcube_plugin_api. + * + * @var rcube_plugin_api + */ + public $plugins; - // create plugin API and load plugins - if ($mode & self::INIT_WITH_PLUGINS) { - $this->plugins = rcube_plugin_api::get_instance(); + + /* private/protected vars */ + protected $texts; + protected $caches = array(); + protected $shutdown_functions = array(); + protected $expunge_cache = false; + + + /** + * This implements the 'singleton' design pattern + * + * @param integer Options to initialize with this instance. See rcube::INIT_WITH_* constants + * + * @return rcube The one and only instance + */ + static function get_instance($mode = 0) + { + if (!self::$instance) { + self::$instance = new rcube(); + self::$instance->init($mode); + } + + return self::$instance; } - } - - - /** - * Get the current database connection - * - * @return rcube_pdo Database connection object - */ - public function get_dbh() - { - if (!$this->db) { - $config_all = $this->config->all(); - $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); - $this->db->set_debug((bool)$config_all['sql_debug']); + + + /** + * Private constructor + */ + protected function __construct() + { + // load configuration + $this->config = new rcube_config; + $this->plugins = new rcube_dummy_plugin_api; + + register_shutdown_function(array($this, 'shutdown')); } - return $this->db; - } - - - /** - * Get global handle for memcache access - * - * @return object Memcache - */ - public function get_memcache() - { - if (!isset($this->memcache)) { - // no memcache support in PHP - if (!class_exists('Memcache')) { - $this->memcache = false; - return false; - } - $this->memcache = new Memcache; - $this->mc_available = 0; + /** + * Initial startup function + */ + protected function init($mode = 0) + { + // initialize syslog + if ($this->config->get('log_driver') == 'syslog') { + $syslog_id = $this->config->get('syslog_id', 'roundcube'); + $syslog_facility = $this->config->get('syslog_facility', LOG_USER); + openlog($syslog_id, LOG_ODELAY, $syslog_facility); + } - // add all configured hosts to pool - $pconnect = $this->config->get('memcache_pconnect', true); - foreach ($this->config->get('memcache_hosts', array()) as $host) { - if (substr($host, 0, 7) != 'unix://') { - list($host, $port) = explode(':', $host); - if (!$port) $port = 11211; + // connect to database + if ($mode & self::INIT_WITH_DB) { + $this->get_dbh(); } - else { - $port = 0; + + // create plugin API and load plugins + if ($mode & self::INIT_WITH_PLUGINS) { + $this->plugins = rcube_plugin_api::get_instance(); } + } - $this->mc_available += intval($this->memcache->addServer($host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); - } - // test connection and failover (will result in $this->mc_available == 0 on complete failure) - $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist + /** + * Get the current database connection + * + * @return rcube_db Database object + */ + public function get_dbh() + { + if (!$this->db) { + $config_all = $this->config->all(); + $this->db = rcube_db::factory($config_all['db_dsnw'], $config_all['db_dsnr'], $config_all['db_persistent']); + $this->db->set_debug((bool)$config_all['sql_debug']); + } - if (!$this->mc_available) - $this->memcache = false; + return $this->db; } - return $this->memcache; - } + /** + * Get global handle for memcache access + * + * @return object Memcache + */ + public function get_memcache() + { + if (!isset($this->memcache)) { + // no memcache support in PHP + if (!class_exists('Memcache')) { + $this->memcache = false; + return false; + } - /** - * Callback for memcache failure - */ - public function memcache_failure($host, $port) - { - static $seen = array(); + $this->memcache = new Memcache; + $this->mc_available = 0; - // only report once - if (!$seen["$host:$port"]++) { - $this->mc_available--; - self::raise_error(array('code' => 604, 'type' => 'db', - 'line' => __LINE__, 'file' => __FILE__, - 'message' => "Memcache failure on host $host:$port"), - true, false); - } - } - - - /** - * Initialize and get cache object - * - * @param string $name Cache identifier - * @param string $type Cache type ('db', 'apc' or 'memcache') - * @param string $ttl Expiration time for cache items - * @param bool $packed Enables/disables data serialization - * - * @return rcube_cache Cache object - */ - public function get_cache($name, $type='db', $ttl=0, $packed=true) - { - if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) { - $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); - } + // add all configured hosts to pool + $pconnect = $this->config->get('memcache_pconnect', true); + foreach ($this->config->get('memcache_hosts', array()) as $host) { + if (substr($host, 0, 7) != 'unix://') { + list($host, $port) = explode(':', $host); + if (!$port) $port = 11211; + } + else { + $port = 0; + } - return $this->caches[$name]; - } - - - /** - * Create SMTP object and connect to server - * - * @param boolean True if connection should be established - */ - public function smtp_init($connect = false) - { - $this->smtp = new rcube_smtp(); - - if ($connect) - $this->smtp->connect(); - } - - - /** - * Initialize and get storage object - * - * @return rcube_storage Storage object - */ - public function get_storage() - { - // already initialized - if (!is_object($this->storage)) { - $this->storage_init(); - } + $this->mc_available += intval($this->memcache->addServer( + $host, $port, $pconnect, 1, 1, 15, false, array($this, 'memcache_failure'))); + } - return $this->storage; - } + // test connection and failover (will result in $this->mc_available == 0 on complete failure) + $this->memcache->increment('__CONNECTIONTEST__', 1); // NOP if key doesn't exist + if (!$this->mc_available) { + $this->memcache = false; + } + } - /** - * Initialize storage object - */ - public function storage_init() - { - // already initialized - if (is_object($this->storage)) { - return; + return $this->memcache; } - $driver = $this->config->get('storage_driver', 'imap'); - $driver_class = "rcube_{$driver}"; - if (!class_exists($driver_class)) { - self::raise_error(array( - 'code' => 700, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Storage driver class ($driver) not found!"), - true, true); + /** + * Callback for memcache failure + */ + public function memcache_failure($host, $port) + { + static $seen = array(); + + // only report once + if (!$seen["$host:$port"]++) { + $this->mc_available--; + self::raise_error(array( + 'code' => 604, 'type' => 'db', + 'line' => __LINE__, 'file' => __FILE__, + 'message' => "Memcache failure on host $host:$port"), + true, false); + } } - // Initialize storage object - $this->storage = new $driver_class; - // for backward compat. (deprecated, will be removed) - $this->imap = $this->storage; + /** + * Initialize and get cache object + * + * @param string $name Cache identifier + * @param string $type Cache type ('db', 'apc' or 'memcache') + * @param string $ttl Expiration time for cache items + * @param bool $packed Enables/disables data serialization + * + * @return rcube_cache Cache object + */ + public function get_cache($name, $type='db', $ttl=0, $packed=true) + { + if (!isset($this->caches[$name]) && ($userid = $this->get_user_id())) { + $this->caches[$name] = new rcube_cache($type, $userid, $name, $ttl, $packed); + } - // enable caching of mail data - $storage_cache = $this->config->get("{$driver}_cache"); - $messages_cache = $this->config->get('messages_cache'); - // for backward compatybility - if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { - $storage_cache = 'db'; - $messages_cache = true; + return $this->caches[$name]; } - if ($storage_cache) - $this->storage->set_caching($storage_cache); - if ($messages_cache) - $this->storage->set_messages_caching(true); - // set pagesize from config - $pagesize = $this->config->get('mail_pagesize'); - if (!$pagesize) { - $pagesize = $this->config->get('pagesize', 50); + /** + * Create SMTP object and connect to server + * + * @param boolean True if connection should be established + */ + public function smtp_init($connect = false) + { + $this->smtp = new rcube_smtp(); + + if ($connect) { + $this->smtp->connect(); + } } - $this->storage->set_pagesize($pagesize); - - // set class options - $options = array( - 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), - 'auth_cid' => $this->config->get("{$driver}_auth_cid"), - 'auth_pw' => $this->config->get("{$driver}_auth_pw"), - 'debug' => (bool) $this->config->get("{$driver}_debug"), - 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), - 'timeout' => (int) $this->config->get("{$driver}_timeout"), - 'skip_deleted' => (bool) $this->config->get('skip_deleted'), - 'driver' => $driver, - ); - - if (!empty($_SESSION['storage_host'])) { - $options['host'] = $_SESSION['storage_host']; - $options['user'] = $_SESSION['username']; - $options['port'] = $_SESSION['storage_port']; - $options['ssl'] = $_SESSION['storage_ssl']; - $options['password'] = $this->decrypt($_SESSION['password']); - $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; + + + /** + * Initialize and get storage object + * + * @return rcube_storage Storage object + */ + public function get_storage() + { + // already initialized + if (!is_object($this->storage)) { + $this->storage_init(); + } + + return $this->storage; } - $options = $this->plugins->exec_hook("storage_init", $options); - // for backward compat. (deprecated, to be removed) - $options = $this->plugins->exec_hook("imap_init", $options); + /** + * Initialize storage object + */ + public function storage_init() + { + // already initialized + if (is_object($this->storage)) { + return; + } - $this->storage->set_options($options); - $this->set_storage_prop(); - } + $driver = $this->config->get('storage_driver', 'imap'); + $driver_class = "rcube_{$driver}"; + if (!class_exists($driver_class)) { + self::raise_error(array( + 'code' => 700, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Storage driver class ($driver) not found!"), + true, true); + } - /** - * Set storage parameters. - * This must be done AFTER connecting to the server! - */ - protected function set_storage_prop() - { - $storage = $this->get_storage(); + // Initialize storage object + $this->storage = new $driver_class; - $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); + // for backward compat. (deprecated, will be removed) + $this->imap = $this->storage; - if ($default_folders = $this->config->get('default_folders')) { - $storage->set_default_folders($default_folders); - } - if (isset($_SESSION['mbox'])) { - $storage->set_folder($_SESSION['mbox']); + // enable caching of mail data + $storage_cache = $this->config->get("{$driver}_cache"); + $messages_cache = $this->config->get('messages_cache'); + // for backward compatybility + if ($storage_cache === null && $messages_cache === null && $this->config->get('enable_caching')) { + $storage_cache = 'db'; + $messages_cache = true; + } + + if ($storage_cache) { + $this->storage->set_caching($storage_cache); + } + if ($messages_cache) { + $this->storage->set_messages_caching(true); + } + + // set pagesize from config + $pagesize = $this->config->get('mail_pagesize'); + if (!$pagesize) { + $pagesize = $this->config->get('pagesize', 50); + } + $this->storage->set_pagesize($pagesize); + + // set class options + $options = array( + 'auth_type' => $this->config->get("{$driver}_auth_type", 'check'), + 'auth_cid' => $this->config->get("{$driver}_auth_cid"), + 'auth_pw' => $this->config->get("{$driver}_auth_pw"), + 'debug' => (bool) $this->config->get("{$driver}_debug"), + 'force_caps' => (bool) $this->config->get("{$driver}_force_caps"), + 'timeout' => (int) $this->config->get("{$driver}_timeout"), + 'skip_deleted' => (bool) $this->config->get('skip_deleted'), + 'driver' => $driver, + ); + + if (!empty($_SESSION['storage_host'])) { + $options['host'] = $_SESSION['storage_host']; + $options['user'] = $_SESSION['username']; + $options['port'] = $_SESSION['storage_port']; + $options['ssl'] = $_SESSION['storage_ssl']; + $options['password'] = $this->decrypt($_SESSION['password']); + $_SESSION[$driver.'_host'] = $_SESSION['storage_host']; + } + + $options = $this->plugins->exec_hook("storage_init", $options); + + // for backward compat. (deprecated, to be removed) + $options = $this->plugins->exec_hook("imap_init", $options); + + $this->storage->set_options($options); + $this->set_storage_prop(); } - if (isset($_SESSION['page'])) { - $storage->set_page($_SESSION['page']); + + + /** + * Set storage parameters. + * This must be done AFTER connecting to the server! + */ + protected function set_storage_prop() + { + $storage = $this->get_storage(); + + $storage->set_charset($this->config->get('default_charset', RCMAIL_CHARSET)); + + if ($default_folders = $this->config->get('default_folders')) { + $storage->set_default_folders($default_folders); + } + if (isset($_SESSION['mbox'])) { + $storage->set_folder($_SESSION['mbox']); + } + if (isset($_SESSION['page'])) { + $storage->set_page($_SESSION['page']); + } } - } /** @@ -492,433 +500,469 @@ class rcube } - /** - * Get localized text in the desired language - * - * @param mixed $attrib Named parameters array or label name - * @param string $domain Label domain (plugin) name - * - * @return string Localized text - */ - public function gettext($attrib, $domain=null) - { - // load localization files if not done yet - if (empty($this->texts)) - $this->load_language(); - - // extract attributes - if (is_string($attrib)) - $attrib = array('name' => $attrib); - - $name = $attrib['name'] ? $attrib['name'] : ''; - - // attrib contain text values: use them from now - if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) - $this->texts[$name] = $setval; - - // check for text with domain - if ($domain && ($text = $this->texts[$domain.'.'.$name])) - ; - // text does not exist - else if (!($text = $this->texts[$name])) { - return "[$name]"; - } + /** + * Get localized text in the desired language + * + * @param mixed $attrib Named parameters array or label name + * @param string $domain Label domain (plugin) name + * + * @return string Localized text + */ + public function gettext($attrib, $domain=null) + { + // load localization files if not done yet + if (empty($this->texts)) { + $this->load_language(); + } - // replace vars in text - if (is_array($attrib['vars'])) { - foreach ($attrib['vars'] as $var_key => $var_value) - $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); - } + // extract attributes + if (is_string($attrib)) { + $attrib = array('name' => $attrib); + } - // format output - if (($attrib['uppercase'] && strtolower($attrib['uppercase']=='first')) || $attrib['ucfirst']) - return ucfirst($text); - else if ($attrib['uppercase']) - return mb_strtoupper($text); - else if ($attrib['lowercase']) - return mb_strtolower($text); - - return strtr($text, array('\n' => "\n")); - } - - - /** - * Check if the given text label exists - * - * @param string $name Label name - * @param string $domain Label domain (plugin) name or '*' for all domains - * @param string $ref_domain Sets domain name if label is found - * - * @return boolean True if text exists (either in the current language or in en_US) - */ - public function text_exists($name, $domain = null, &$ref_domain = null) - { - // load localization files if not done yet - if (empty($this->texts)) - $this->load_language(); - - if (isset($this->texts[$name])) { - $ref_domain = ''; - return true; - } + $name = $attrib['name'] ? $attrib['name'] : ''; + + // attrib contain text values: use them from now + if (($setval = $attrib[strtolower($_SESSION['language'])]) || ($setval = $attrib['en_us'])) { + $this->texts[$name] = $setval; + } + + // check for text with domain + if ($domain && ($text = $this->texts[$domain.'.'.$name])) { + } + // text does not exist + else if (!($text = $this->texts[$name])) { + return "[$name]"; + } + + // replace vars in text + if (is_array($attrib['vars'])) { + foreach ($attrib['vars'] as $var_key => $var_value) { + $text = str_replace($var_key[0]!='$' ? '$'.$var_key : $var_key, $var_value, $text); + } + } - // any of loaded domains (plugins) - if ($domain == '*') { - foreach ($this->plugins->loaded_plugins() as $domain) - if (isset($this->texts[$domain.'.'.$name])) { - $ref_domain = $domain; - return true; + // format output + if (($attrib['uppercase'] && strtolower($attrib['uppercase'] == 'first')) || $attrib['ucfirst']) { + return ucfirst($text); + } + else if ($attrib['uppercase']) { + return mb_strtoupper($text); } + else if ($attrib['lowercase']) { + return mb_strtolower($text); + } + + return strtr($text, array('\n' => "\n")); } - // specified domain - else if ($domain) { - $ref_domain = $domain; - return isset($this->texts[$domain.'.'.$name]); + + + /** + * Check if the given text label exists + * + * @param string $name Label name + * @param string $domain Label domain (plugin) name or '*' for all domains + * @param string $ref_domain Sets domain name if label is found + * + * @return boolean True if text exists (either in the current language or in en_US) + */ + public function text_exists($name, $domain = null, &$ref_domain = null) + { + // load localization files if not done yet + if (empty($this->texts)) { + $this->load_language(); + } + + if (isset($this->texts[$name])) { + $ref_domain = ''; + return true; + } + + // any of loaded domains (plugins) + if ($domain == '*') { + foreach ($this->plugins->loaded_plugins() as $domain) { + if (isset($this->texts[$domain.'.'.$name])) { + $ref_domain = $domain; + return true; + } + } + } + // specified domain + else if ($domain) { + $ref_domain = $domain; + return isset($this->texts[$domain.'.'.$name]); + } + + return false; } - return false; - } - /** - * Load a localization package - * - * @param string Language ID - */ - public function load_language($lang = null, $add = array()) - { - $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); + /** + * Load a localization package + * + * @param string Language ID + * @param array Additional text labels/messages + */ + public function load_language($lang = null, $add = array()) + { + $lang = $this->language_prop(($lang ? $lang : $_SESSION['language'])); + + // load localized texts + if (empty($this->texts) || $lang != $_SESSION['language']) { + $this->texts = array(); - // load localized texts - if (empty($this->texts) || $lang != $_SESSION['language']) { - $this->texts = array(); + // handle empty lines after closing PHP tag in localization files + ob_start(); - // handle empty lines after closing PHP tag in localization files - ob_start(); + // get english labels (these should be complete) + @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); + @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); - // get english labels (these should be complete) - @include(INSTALL_PATH . 'program/localization/en_US/labels.inc'); - @include(INSTALL_PATH . 'program/localization/en_US/messages.inc'); + if (is_array($labels)) + $this->texts = $labels; + if (is_array($messages)) + $this->texts = array_merge($this->texts, $messages); - if (is_array($labels)) - $this->texts = $labels; - if (is_array($messages)) - $this->texts = array_merge($this->texts, $messages); + // include user language files + if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { + include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); + include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); - // include user language files - if ($lang != 'en' && $lang != 'en_US' && is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { - include_once(INSTALL_PATH . 'program/localization/' . $lang . '/labels.inc'); - include_once(INSTALL_PATH . 'program/localization/' . $lang . '/messages.inc'); + if (is_array($labels)) + $this->texts = array_merge($this->texts, $labels); + if (is_array($messages)) + $this->texts = array_merge($this->texts, $messages); + } - if (is_array($labels)) - $this->texts = array_merge($this->texts, $labels); - if (is_array($messages)) - $this->texts = array_merge($this->texts, $messages); - } + ob_end_clean(); - ob_end_clean(); + $_SESSION['language'] = $lang; + } - $_SESSION['language'] = $lang; + // append additional texts (from plugin) + if (is_array($add) && !empty($add)) { + $this->texts += $add; + } } - // append additional texts (from plugin) - if (is_array($add) && !empty($add)) - $this->texts += $add; - } - - - /** - * Check the given string and return a valid language code - * - * @param string Language code - * @return string Valid language code - */ - protected function language_prop($lang) - { - static $rcube_languages, $rcube_language_aliases; - - // user HTTP_ACCEPT_LANGUAGE if no language is specified - if (empty($lang) || $lang == 'auto') { - $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); - $lang = str_replace('-', '_', $accept_langs[0]); - } - - if (empty($rcube_languages)) { - @include(INSTALL_PATH . 'program/localization/index.inc'); - } - // check if we have an alias for that language - if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { - $lang = $rcube_language_aliases[$lang]; + /** + * Check the given string and return a valid language code + * + * @param string Language code + * + * @return string Valid language code + */ + protected function language_prop($lang) + { + static $rcube_languages, $rcube_language_aliases; + + // user HTTP_ACCEPT_LANGUAGE if no language is specified + if (empty($lang) || $lang == 'auto') { + $accept_langs = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); + $lang = str_replace('-', '_', $accept_langs[0]); + } + + if (empty($rcube_languages)) { + @include(INSTALL_PATH . 'program/localization/index.inc'); + } + + // check if we have an alias for that language + if (!isset($rcube_languages[$lang]) && isset($rcube_language_aliases[$lang])) { + $lang = $rcube_language_aliases[$lang]; + } + // try the first two chars + else if (!isset($rcube_languages[$lang])) { + $short = substr($lang, 0, 2); + + // check if we have an alias for the short language code + if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { + $lang = $rcube_language_aliases[$short]; + } + // expand 'nn' to 'nn_NN' + else if (!isset($rcube_languages[$short])) { + $lang = $short.'_'.strtoupper($short); + } + } + + if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { + $lang = 'en_US'; + } + + return $lang; } - // try the first two chars - else if (!isset($rcube_languages[$lang])) { - $short = substr($lang, 0, 2); - - // check if we have an alias for the short language code - if (!isset($rcube_languages[$short]) && isset($rcube_language_aliases[$short])) { - $lang = $rcube_language_aliases[$short]; - } - // expand 'nn' to 'nn_NN' - else if (!isset($rcube_languages[$short])) { - $lang = $short.'_'.strtoupper($short); - } + + + /** + * Read directory program/localization and return a list of available languages + * + * @return array List of available localizations + */ + public function list_languages() + { + static $sa_languages = array(); + + if (!sizeof($sa_languages)) { + @include(INSTALL_PATH . 'program/localization/index.inc'); + + if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { + while (($name = readdir($dh)) !== false) { + if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) { + continue; + } + + if ($label = $rcube_languages[$name]) { + $sa_languages[$name] = $label; + } + } + closedir($dh); + } + } + + return $sa_languages; } - if (!isset($rcube_languages[$lang]) || !is_dir(INSTALL_PATH . 'program/localization/' . $lang)) { - $lang = 'en_US'; + + /** + * Encrypt using 3DES + * + * @param string $clear clear text input + * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' + * @param boolean $base64 whether or not to base64_encode() the result before returning + * + * @return string encrypted text + */ + public function encrypt($clear, $key = 'des_key', $base64 = true) + { + if (!$clear) { + return ''; + } + + /*- + * Add a single canary byte to the end of the clear text, which + * will help find out how much of padding will need to be removed + * upon decryption; see http://php.net/mcrypt_generic#68082 + */ + $clear = pack("a*H2", $clear, "80"); + + if (function_exists('mcrypt_module_open') && + ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) + ) { + $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); + mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); + $cipher = $iv . mcrypt_generic($td, $clear); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + } + else { + @include_once 'des.inc'; + + if (function_exists('des')) { + $des_iv_size = 8; + $iv = $this->create_iv($des_iv_size); + $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); + } + else { + self::raise_error(array( + 'code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" + ), true, true); + } + } + + return $base64 ? base64_encode($cipher) : $cipher; } - return $lang; - } + /** + * Decrypt 3DES-encrypted string + * + * @param string $cipher encrypted text + * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' + * @param boolean $base64 whether or not input is base64-encoded + * + * @return string decrypted text + */ + public function decrypt($cipher, $key = 'des_key', $base64 = true) + { + if (!$cipher) { + return ''; + } - /** - * Read directory program/localization and return a list of available languages - * - * @return array List of available localizations - */ - public function list_languages() - { - static $sa_languages = array(); + $cipher = $base64 ? base64_decode($cipher) : $cipher; - if (!sizeof($sa_languages)) { - @include(INSTALL_PATH . 'program/localization/index.inc'); + if (function_exists('mcrypt_module_open') && + ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, "")) + ) { + $iv_size = mcrypt_enc_get_iv_size($td); + $iv = substr($cipher, 0, $iv_size); - if ($dh = @opendir(INSTALL_PATH . 'program/localization')) { - while (($name = readdir($dh)) !== false) { - if ($name[0] == '.' || !is_dir(INSTALL_PATH . 'program/localization/' . $name)) - continue; + // session corruption? (#1485970) + if (strlen($iv) < $iv_size) { + return ''; + } - if ($label = $rcube_languages[$name]) - $sa_languages[$name] = $label; + $cipher = substr($cipher, $iv_size); + mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); + $clear = mdecrypt_generic($td, $cipher); + mcrypt_generic_deinit($td); + mcrypt_module_close($td); + } + else { + @include_once 'des.inc'; + + if (function_exists('des')) { + $des_iv_size = 8; + $iv = substr($cipher, 0, $des_iv_size); + $cipher = substr($cipher, $des_iv_size); + $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); + } + else { + self::raise_error(array( + 'code' => 500, 'type' => 'php', + 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" + ), true, true); + } } - closedir($dh); - } + + /*- + * Trim PHP's padding and the canary byte; see note in + * rcube::encrypt() and http://php.net/mcrypt_generic#68082 + */ + $clear = substr(rtrim($clear, "\0"), 0, -1); + + return $clear; } - return $sa_languages; - } - - - /** - * Encrypt using 3DES - * - * @param string $clear clear text input - * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' - * @param boolean $base64 whether or not to base64_encode() the result before returning - * - * @return string encrypted text - */ - public function encrypt($clear, $key = 'des_key', $base64 = true) - { - if (!$clear) - return ''; - - /*- - * Add a single canary byte to the end of the clear text, which - * will help find out how much of padding will need to be removed - * upon decryption; see http://php.net/mcrypt_generic#68082 + + /** + * Generates encryption initialization vector (IV) + * + * @param int Vector size + * + * @return string Vector string */ - $clear = pack("a*H2", $clear, "80"); - - if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { - $iv = $this->create_iv(mcrypt_enc_get_iv_size($td)); - mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); - $cipher = $iv . mcrypt_generic($td, $clear); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = $this->create_iv($des_iv_size); - $cipher = $iv . des($this->config->get_crypto_key($key), $clear, 1, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform encryption; make sure Mcrypt is installed or lib/des.inc is available" - ), true, true); - } + private function create_iv($size) + { + // mcrypt_create_iv() can be slow when system lacks entrophy + // we'll generate IV vector manually + $iv = ''; + for ($i = 0; $i < $size; $i++) { + $iv .= chr(mt_rand(0, 255)); + } + + return $iv; } - return $base64 ? base64_encode($cipher) : $cipher; - } - - /** - * Decrypt 3DES-encrypted string - * - * @param string $cipher encrypted text - * @param string $key encryption key to retrieve from the configuration, defaults to 'des_key' - * @param boolean $base64 whether or not input is base64-encoded - * - * @return string decrypted text - */ - public function decrypt($cipher, $key = 'des_key', $base64 = true) - { - if (!$cipher) - return ''; - - $cipher = $base64 ? base64_decode($cipher) : $cipher; - - if (function_exists('mcrypt_module_open') && - ($td = mcrypt_module_open(MCRYPT_TripleDES, "", MCRYPT_MODE_CBC, ""))) { - $iv_size = mcrypt_enc_get_iv_size($td); - $iv = substr($cipher, 0, $iv_size); - - // session corruption? (#1485970) - if (strlen($iv) < $iv_size) - return ''; - $cipher = substr($cipher, $iv_size); - mcrypt_generic_init($td, $this->config->get_crypto_key($key), $iv); - $clear = mdecrypt_generic($td, $cipher); - mcrypt_generic_deinit($td); - mcrypt_module_close($td); - } - else { - @include_once 'des.inc'; - - if (function_exists('des')) { - $des_iv_size = 8; - $iv = substr($cipher, 0, $des_iv_size); - $cipher = substr($cipher, $des_iv_size); - $clear = des($this->config->get_crypto_key($key), $cipher, 0, 1, $iv); - } - else { - self::raise_error(array( - 'code' => 500, 'type' => 'php', - 'file' => __FILE__, 'line' => __LINE__, - 'message' => "Could not perform decryption; make sure Mcrypt is installed or lib/des.inc is available" - ), true, true); - } + /** + * Build a valid URL to this instance of Roundcube + * + * @param mixed Either a string with the action or url parameters as key-value pairs + * @return string Valid application URL + */ + public function url($p) + { + // STUB: should be overloaded by the application + return ''; } - /*- - * Trim PHP's padding and the canary byte; see note in - * rcube::encrypt() and http://php.net/mcrypt_generic#68082 + + /** + * Function to be executed in script shutdown + * Registered with register_shutdown_function() */ - $clear = substr(rtrim($clear, "\0"), 0, -1); - - return $clear; - } - - /** - * Generates encryption initialization vector (IV) - * - * @param int Vector size - * @return string Vector string - */ - private function create_iv($size) - { - // mcrypt_create_iv() can be slow when system lacks entrophy - // we'll generate IV vector manually - $iv = ''; - for ($i = 0; $i < $size; $i++) - $iv .= chr(mt_rand(0, 255)); - return $iv; - } - - - /** - * Build a valid URL to this instance of Roundcube - * - * @param mixed Either a string with the action or url parameters as key-value pairs - * @return string Valid application URL - */ - public function url($p) - { - // STUB: should be overloaded by the application - return ''; - } - - - /** - * Function to be executed in script shutdown - * Registered with register_shutdown_function() - */ - public function shutdown() - { - foreach ($this->shutdown_functions as $function) - call_user_func($function); - - if (is_object($this->smtp)) - $this->smtp->disconnect(); - - foreach ($this->caches as $cache) { - if (is_object($cache)) - $cache->close(); - } + public function shutdown() + { + foreach ($this->shutdown_functions as $function) { + call_user_func($function); + } + + if (is_object($this->smtp)) { + $this->smtp->disconnect(); + } + + foreach ($this->caches as $cache) { + if (is_object($cache)) { + $cache->close(); + } + } - if (is_object($this->storage)) { - if ($this->expunge_cache) - $this->storage->expunge_cache(); - $this->storage->close(); + if (is_object($this->storage)) { + if ($this->expunge_cache) { + $this->storage->expunge_cache(); + } + $this->storage->close(); + } } - } - - - /** - * Registers shutdown function to be executed on shutdown. - * The functions will be executed before destroying any - * objects like smtp, imap, session, etc. - * - * @param callback Function callback - */ - public function add_shutdown_function($function) - { - $this->shutdown_functions[] = $function; - } - - - /** - * Construct shell command, execute it and return output as string. - * Keywords {keyword} are replaced with arguments - * - * @param $cmd Format string with {keywords} to be replaced - * @param $values (zero, one or more arrays can be passed) - * @return output of command. shell errors not detectable - */ - public static function exec(/* $cmd, $values1 = array(), ... */) - { - $args = func_get_args(); - $cmd = array_shift($args); - $values = $replacements = array(); - - // merge values into one array - foreach ($args as $arg) - $values += (array)$arg; - - preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); - foreach ($matches as $tags) { - list(, $tag, $option, $key) = $tags; - $parts = array(); - - if ($option) { - foreach ((array)$values["-$key"] as $key => $value) { - if ($value === true || $value === false || $value === null) - $parts[] = $value ? $key : ""; - else foreach ((array)$value as $val) - $parts[] = "$key " . escapeshellarg($val); - } - } - else { - foreach ((array)$values[$key] as $value) - $parts[] = escapeshellarg($value); - } - - $replacements[$tag] = join(" ", $parts); + + + /** + * Registers shutdown function to be executed on shutdown. + * The functions will be executed before destroying any + * objects like smtp, imap, session, etc. + * + * @param callback Function callback + */ + public function add_shutdown_function($function) + { + $this->shutdown_functions[] = $function; } - // use strtr behaviour of going through source string once - $cmd = strtr($cmd, $replacements); - return (string)shell_exec($cmd); - } + /** + * Construct shell command, execute it and return output as string. + * Keywords {keyword} are replaced with arguments + * + * @param $cmd Format string with {keywords} to be replaced + * @param $values (zero, one or more arrays can be passed) + * + * @return output of command. shell errors not detectable + */ + public static function exec(/* $cmd, $values1 = array(), ... */) + { + $args = func_get_args(); + $cmd = array_shift($args); + $values = $replacements = array(); + + // merge values into one array + foreach ($args as $arg) { + $values += (array)$arg; + } + + preg_match_all('/({(-?)([a-z]\w*)})/', $cmd, $matches, PREG_SET_ORDER); + foreach ($matches as $tags) { + list(, $tag, $option, $key) = $tags; + $parts = array(); + + if ($option) { + foreach ((array)$values["-$key"] as $key => $value) { + if ($value === true || $value === false || $value === null) { + $parts[] = $value ? $key : ""; + } + else { + foreach ((array)$value as $val) { + $parts[] = "$key " . escapeshellarg($val); + } + } + } + } + else { + foreach ((array)$values[$key] as $value) { + $parts[] = escapeshellarg($value); + } + } + + $replacements[$tag] = join(" ", $parts); + } + + // use strtr behaviour of going through source string once + $cmd = strtr($cmd, $replacements); + + return (string)shell_exec($cmd); + } /** diff --git a/program/include/rcube_browser.php b/program/include/rcube_browser.php index 06033e036..7cfae709d 100644 --- a/program/include/rcube_browser.php +++ b/program/include/rcube_browser.php @@ -20,8 +20,6 @@ */ /** - * rcube_browser - * * Provide details about the client's browser based on the User-Agent header * * @package Core -- cgit v1.2.3 From b2631bffd98a51fc8b61aa8452fbaf3e95bcde24 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sat, 18 Aug 2012 17:57:08 +0200 Subject: More default->larry fixes --- plugins/hide_blockquote/hide_blockquote.php | 6 +---- plugins/hide_blockquote/skins/default/style.css | 31 ------------------------- plugins/hide_blockquote/skins/larry/style.css | 31 +++++++++++++++++++++++++ program/include/rcube_plugin.php | 2 +- 4 files changed, 33 insertions(+), 37 deletions(-) delete mode 100644 plugins/hide_blockquote/skins/default/style.css create mode 100644 plugins/hide_blockquote/skins/larry/style.css (limited to 'program/include') diff --git a/plugins/hide_blockquote/hide_blockquote.php b/plugins/hide_blockquote/hide_blockquote.php index ca0273a5d..7af163dcd 100644 --- a/plugins/hide_blockquote/hide_blockquote.php +++ b/plugins/hide_blockquote/hide_blockquote.php @@ -27,11 +27,7 @@ class hide_blockquote extends rcube_plugin && ($limit = $rcmail->config->get('hide_blockquote_limit')) ) { // include styles - $skin = $rcmail->config->get('skin'); - if (!file_exists($this->home."/skins/$skin/style.css")) { - $skin = 'default'; - } - $this->include_stylesheet("skins/$skin/style.css"); + $this->include_stylesheet($this->local_skin_path() . "/style.css"); // Script and localization $this->include_script('hide_blockquote.js'); diff --git a/plugins/hide_blockquote/skins/default/style.css b/plugins/hide_blockquote/skins/default/style.css deleted file mode 100644 index 198172f92..000000000 --- a/plugins/hide_blockquote/skins/default/style.css +++ /dev/null @@ -1,31 +0,0 @@ -span.blockquote-link { - font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; - top: 0; - cursor: pointer; - right: 5px; - height: 14px; - min-width: 40px; - padding: 0 8px; - font-size: 10px; - font-weight: bold; - color: #a8a8a8; - line-height: 14px; - text-decoration: none; - text-shadow: 0px 1px 1px #fff; - text-align: center; - border: 1px solid #e8e8e8; - border-top: none; - border-bottom-right-radius: 6px; - border-bottom-left-radius: 6px; - background: #f8f8f8; - background: -moz-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); - background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#e8e8e8)); - background: -o-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); - background: -ms-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); - background: linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); -} - -blockquote.blockquote-header { - text-overflow: ellipsis !important; - padding-right: 60px !important; -} \ No newline at end of file diff --git a/plugins/hide_blockquote/skins/larry/style.css b/plugins/hide_blockquote/skins/larry/style.css new file mode 100644 index 000000000..198172f92 --- /dev/null +++ b/plugins/hide_blockquote/skins/larry/style.css @@ -0,0 +1,31 @@ +span.blockquote-link { + font-family: "Lucida Grande", Verdana, Arial, Helvetica, sans-serif; + top: 0; + cursor: pointer; + right: 5px; + height: 14px; + min-width: 40px; + padding: 0 8px; + font-size: 10px; + font-weight: bold; + color: #a8a8a8; + line-height: 14px; + text-decoration: none; + text-shadow: 0px 1px 1px #fff; + text-align: center; + border: 1px solid #e8e8e8; + border-top: none; + border-bottom-right-radius: 6px; + border-bottom-left-radius: 6px; + background: #f8f8f8; + background: -moz-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f8f8f8), color-stop(100%,#e8e8e8)); + background: -o-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); + background: -ms-linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); + background: linear-gradient(top, #f8f8f8 0%, #e8e8e8 100%); +} + +blockquote.blockquote-header { + text-overflow: ellipsis !important; + padding-right: 60px !important; +} \ No newline at end of file diff --git a/program/include/rcube_plugin.php b/program/include/rcube_plugin.php index b1ec32a8a..c1035733b 100644 --- a/program/include/rcube_plugin.php +++ b/program/include/rcube_plugin.php @@ -336,7 +336,7 @@ abstract class rcube_plugin public function local_skin_path() { $rcmail = rcube::get_instance(); - foreach (array($rcmail->config->get('skin'),'default') as $skin) { + foreach (array($rcmail->config->get('skin'), 'larry') as $skin) { $skin_path = 'skins/' . $skin; if (is_dir(realpath(slashify($this->home) . $skin_path))) break; -- cgit v1.2.3 From ae7027de029e28fdd3894efe919b6171b2b11eab Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 20 Aug 2012 09:32:25 +0200 Subject: Added session_path config option and unified cookies settings in javascript --- CHANGELOG | 1 + config/main.inc.php.dist | 7 +++++-- program/include/rcube.php | 5 +++++ program/include/rcube_output_html.php | 5 +++++ program/js/app.js | 8 ++++++++ program/js/common.js | 1 + program/js/googiespell.js | 4 ++-- skins/classic/splitter.js | 4 ++-- skins/larry/ui.js | 6 +++--- 9 files changed, 32 insertions(+), 9 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 6ea101e8f..4d18f64b7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Added session_path config option and unified cookies settings in javascript - Added "Undeleted" option to messages list filter - Rewritten test scripts for PHPUnit - Add new DB abstraction layer based on PHP PDO, supporting SQLite3 (#1488332) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 1e9c1fdcd..8d615f3b0 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -241,12 +241,15 @@ $rcmail_config['display_version'] = false; // must be greater than 'keep_alive'/60 $rcmail_config['session_lifetime'] = 10; -// session domain: .example.org +// Session domain: .example.org $rcmail_config['session_domain'] = ''; -// session name. Default: 'roundcube_sessid' +// Session name. Default: 'roundcube_sessid' $rcmail_config['session_name'] = null; +// Session path. Defaults to PHP session.cookie_path setting. +$rcmail_config['session_path'] = null; + // Backend to use for session storage. Can either be 'db' (default) or 'memcache' // If set to memcache, a list of servers need to be specified in 'memcache_hosts' // Make sure the Memcache extension (http://pecl.php.net/package/memcache) version >= 2.0.0 is installed diff --git a/program/include/rcube.php b/program/include/rcube.php index 84014ef5c..0e40b3c6b 100644 --- a/program/include/rcube.php +++ b/program/include/rcube.php @@ -405,12 +405,17 @@ class rcube $sess_name = $this->config->get('session_name'); $sess_domain = $this->config->get('session_domain'); + $sess_path = $this->config->get('session_path'); $lifetime = $this->config->get('session_lifetime', 0) * 60; // set session domain if ($sess_domain) { ini_set('session.cookie_domain', $sess_domain); } + // set session path + if ($sess_path) { + ini_set('session.cookie_path', $sess_path); + } // set session garbage collecting time according to session_lifetime if ($lifetime) { ini_set('session.gc_maxlifetime', $lifetime * 2); diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 0a8f0e364..a071ee354 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -67,6 +67,11 @@ class rcube_output_html extends rcube_output $this->set_env('task', $task); $this->set_env('x_frame_options', $this->config->get('x_frame_options', 'sameorigin')); + // add cookie info + $this->set_env('cookie_domain', ini_get('session.cookie_domain')); + $this->set_env('cookie_path', ini_get('session.cookie_path')); + $this->set_env('cookie_secure', ini_get('session.cookie_secure')); + // load the correct skin (in case user-defined) $skin = $this->config->get('skin'); $this->set_skin($skin); diff --git a/program/js/app.js b/program/js/app.js index e8bb6c1a7..9ca16b39c 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -6585,6 +6585,12 @@ function rcube_webmail() return 0; }; + // Cookie setter + this.set_cookie = function(name, value, expires) + { + setCookie(name, value, expires, this.env.cookie_path, this.env.cookie_domain, this.env.cookie_secure); + } + } // end object rcube_webmail @@ -6615,6 +6621,8 @@ rcube_webmail.long_subject_title_ie = function(elem, indent) } }; +rcube_webmail.prototype.get_cookie = getCookie; + // copy event engine prototype rcube_webmail.prototype.addEventListener = rcube_event_engine.prototype.addEventListener; rcube_webmail.prototype.removeEventListener = rcube_event_engine.prototype.removeEventListener; diff --git a/program/js/common.js b/program/js/common.js index fdef3453e..a08387ecb 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -635,6 +635,7 @@ function getCookie(name) return unescape(dc.substring(begin + prefix.length, end)); }; +// deprecated aliases, to be removed, use rcmail.set_cookie/rcmail.get_cookie roundcube_browser.prototype.set_cookie = setCookie; roundcube_browser.prototype.get_cookie = getCookie; diff --git a/program/js/googiespell.js b/program/js/googiespell.js index 9f1b41bb2..478858bac 100644 --- a/program/js/googiespell.js +++ b/program/js/googiespell.js @@ -25,7 +25,7 @@ var GOOGIE_CUR_LANG, function GoogieSpell(img_dir, server_url, has_dict) { var ref = this, - cookie_value = getCookie('language'); + cookie_value = rcmail.get_cookie('language'); GOOGIE_CUR_LANG = cookie_value != null ? cookie_value : GOOGIE_DEFAULT_LANG; @@ -150,7 +150,7 @@ this.setCurrentLanguage = function(lan_code) //Set cookie var now = new Date(); now.setTime(now.getTime() + 365 * 24 * 60 * 60 * 1000); - setCookie('language', lan_code, now); + rcmail.set_cookie('language', lan_code, now); }; this.setForceWidthHeight = function(width, height) diff --git a/skins/classic/splitter.js b/skins/classic/splitter.js index 59ebb5151..3f1c97302 100644 --- a/skins/classic/splitter.js +++ b/skins/classic/splitter.js @@ -47,7 +47,7 @@ function rcube_splitter(attrib) rcube_event.add_listener({element: window, event:'resize', object:this, method:'onResize'}); // read saved position from cookie - var cookie = bw.get_cookie(this.id); + var cookie = rcmail.get_cookie(this.id); if (cookie && !isNaN(cookie)) { this.pos = parseFloat(cookie); this.resize(); @@ -197,7 +197,7 @@ function rcube_splitter(attrib) { var exp = new Date(); exp.setYear(exp.getFullYear() + 1); - bw.set_cookie(this.id, this.pos, exp); + rcmail.set_cookie(this.id, this.pos, exp); }; } // end class rcube_splitter diff --git a/skins/larry/ui.js b/skins/larry/ui.js index b6056b6ee..ca1680759 100644 --- a/skins/larry/ui.js +++ b/skins/larry/ui.js @@ -461,7 +461,7 @@ function rcube_mail_ui() var button = $(e.target), frame = $('#mailpreviewframe'), visible = !frame.is(':visible'), - splitter = mailviewsplit.pos || parseInt(bw.get_cookie('mailviewsplitter') || 320), + splitter = mailviewsplit.pos || parseInt(rcmail.get_cookie('mailviewsplitter') || 320), topstyles, bottomstyles, uid; frame.toggle(); @@ -974,7 +974,7 @@ function rcube_splitter(p) $(window).resize(onResize); // read saved position from cookie - var cookie = bw.get_cookie(this.id); + var cookie = rcmail.get_cookie(this.id); if (cookie && !isNaN(cookie)) { this.pos = parseFloat(cookie); this.resize(); @@ -1135,7 +1135,7 @@ function rcube_splitter(p) { var exp = new Date(); exp.setYear(exp.getFullYear() + 1); - bw.set_cookie(this.id, this.pos, exp); + rcmail.set_cookie(this.id, this.pos, exp); }; } // end class rcube_splitter -- cgit v1.2.3 From 982353884898faf5a4cf0ee593b7078cb736c2d1 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 21 Aug 2012 14:23:07 +0200 Subject: Improved internal cache handling --- program/include/rcube_imap_cache.php | 46 +++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 16 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_imap_cache.php b/program/include/rcube_imap_cache.php index a061a1f6e..4d6d5e180 100644 --- a/program/include/rcube_imap_cache.php +++ b/program/include/rcube_imap_cache.php @@ -5,7 +5,7 @@ | program/include/rcube_imap_cache.php | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2011, The Roundcube Dev Team | + | Copyright (C) 2005-2012, The Roundcube Dev Team | | | | Licensed under the GNU General Public License version 3 or | | any later version with exceptions for skins & plugins. | @@ -350,11 +350,11 @@ class rcube_imap_cache function get_message($mailbox, $uid, $update = true, $cache = true) { // Check internal cache - if ($this->icache['message'] - && $this->icache['message']['mailbox'] == $mailbox - && $this->icache['message']['object']->uid == $uid + if ($this->icache['__message'] + && $this->icache['__message']['mailbox'] == $mailbox + && $this->icache['__message']['object']->uid == $uid ) { - return $this->icache['message']['object']; + return $this->icache['__message']['object']; } $sql_result = $this->db->query( @@ -386,7 +386,7 @@ class rcube_imap_cache // Save current message from internal cache $this->save_icache(); - $this->icache['message'] = array( + $this->icache['__message'] = array( 'object' => $message, 'mailbox' => $mailbox, 'exists' => $found, @@ -459,20 +459,28 @@ class rcube_imap_cache */ function change_flag($mailbox, $uids, $flag, $enabled = false) { + if (empty($uids)) { + return; + } + $flag = strtoupper($flag); $idx = (int) array_search($flag, $this->flags); + $uids = (array) $uids; if (!$idx) { return; } // Internal cache update - if ($uids && count($uids) == 1 && ($uid = current($uids)) - && ($message = $this->icache['message']) - && $message['mailbox'] == $mailbox && $message['object']->uid == $uid + if (($message = $this->icache['__message']) + && $message['mailbox'] === $mailbox + && in_array($message['object']->uid, $uids) ) { $message['object']->flags[$flag] = $enabled; - return; + + if (count($uids) == 1) { + return; + } } $this->db->query( @@ -481,7 +489,7 @@ class rcube_imap_cache .", flags = flags ".($enabled ? "+ $idx" : "- $idx") ." WHERE user_id = ?" ." AND mailbox = ?" - .($uids !== null ? " AND uid IN (".$this->db->array2list((array)$uids, 'integer').")" : "") + .($uids !== null ? " AND uid IN (".$this->db->array2list($uids, 'integer').")" : "") ." AND (flags & $idx) ".($enabled ? "= 0" : "= $idx"), $this->userid, $mailbox); } @@ -503,10 +511,11 @@ class rcube_imap_cache } else { // Remove the message from internal cache - if (!empty($uids) && !is_array($uids) && ($message = $this->icache['message']) - && $message['mailbox'] == $mailbox && $message['object']->uid == $uids + if (!empty($uids) && ($message = $this->icache['__message']) + && $message['mailbox'] === $mailbox + && in_array($message['object']->uid, (array)$uids) ) { - $this->icache['message'] = null; + $this->icache['__message'] = null; } $this->db->query( @@ -763,6 +772,11 @@ class rcube_imap_cache $object = $index['object']; $is_thread = is_a($object, 'rcube_result_thread'); + // sanity check + if (empty($object)) { + return false; + } + // Get mailbox data (UIDVALIDITY, counters, etc.) for status check $mbox_data = $this->imap->folder_data($mailbox); @@ -1078,7 +1092,7 @@ class rcube_imap_cache private function save_icache() { // Save current message from internal cache - if ($message = $this->icache['message']) { + if ($message = $this->icache['__message']) { // clean up some object's data $object = $this->message_object_prepare($message['object']); @@ -1089,7 +1103,7 @@ class rcube_imap_cache $this->add_message($message['mailbox'], $object, !$message['exists']); } - $this->icache['message']['md5sum'] = $md5sum; + $this->icache['__message']['md5sum'] = $md5sum; } } -- cgit v1.2.3 From da28121dcd160045c468b7028ee835b24f0cb965 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 24 Aug 2012 10:10:25 +0200 Subject: Improved email address validation with IPv6 support --- program/include/rcube_utils.php | 52 ++++++++++++++++++++++++++++++++++++++--- program/js/common.js | 4 +++- tests/Utils.php | 17 +++++++++++--- 3 files changed, 66 insertions(+), 7 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index 9f18b79c4..defb2aed1 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -92,9 +92,9 @@ class rcube_utils return false; } - // Check domain part - if (preg_match('/^\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\]*$/', $domain_part)) { - return true; // IP address + // Validate domain part + if (preg_match('/^\[((IPv6:[0-9a-f:.]+)|([0-9.]+))\]$/i', $domain_part, $matches)) { + return self::ip_check(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address } else { // If not an IP address @@ -146,6 +146,52 @@ class rcube_utils return false; } + + /** + * Validates IPv4 or IPv6 address + * + * @param string $ip IP address in v4 or v6 format + * + * @return bool True if the address is valid + */ + public static function ip_check($ip) + { + // IPv6, but there's no build-in IPv6 support + if (strpos($ip, ':') !== false && !defined('AF_INET6')) { + $parts = explode(':', $domain_part); + $count = count($parts); + + if ($count > 8 || $count < 2) { + return false; + } + + foreach ($parts as $idx => $part) { + $length = strlen($part); + if (!$length) { + // there can be only one :: + if ($found_empty) { + return false; + } + $found_empty = true; + } + // last part can be an IPv4 address + else if ($idx == $count - 1) { + if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { + return @inet_pton($part) !== false; + } + } + else if (!preg_match('/^[0-9a-f]{1,4}$/i', $part)) { + return false; + } + } + + return true; + } + + return @inet_pton($ip) !== false; + } + + /** * Check whether the HTTP referer matches the current request * diff --git a/program/js/common.js b/program/js/common.js index 2d8d9e176..f9e945c05 100644 --- a/program/js/common.js +++ b/program/js/common.js @@ -494,7 +494,9 @@ function rcube_check_email(input, inline) atom = '[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+', quoted_pair = '\\x5c[\\x00-\\x7f]', quoted_string = '\\x22('+qtext+'|'+quoted_pair+')*\\x22', - ip_addr = '\\[*(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\\]*', + ipv4 = '\\[(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])(\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])){3}\\]', + ipv6 = '\\[IPv6:[0-9a-f:.]+\\]', + ip_addr = '(' + ipv4 + ')|(' + ipv6 + ')', // Use simplified domain matching, because we need to allow Unicode characters here // So, e-mail address should be validated also on server side after idn_to_ascii() use //domain_literal = '\\x5b('+dtext+'|'+quoted_pair+')*\\x5d', diff --git a/tests/Utils.php b/tests/Utils.php index 648b39989..ad0aa1dde 100644 --- a/tests/Utils.php +++ b/tests/Utils.php @@ -18,8 +18,10 @@ class Utils extends PHPUnit_Framework_TestCase array('firstname.lastname@domain.com', 'Email contains dot in the address field'), array('email@subdomain.domain.com', 'Email contains dot with subdomain'), array('firstname+lastname@domain.com', 'Plus sign is considered valid character'), - array('email@123.123.123.123', 'Domain is valid IP address'), - array('email@[123.123.123.123]', 'Square bracket around IP address is considered valid'), + array('email@[123.123.123.123]', 'Square bracket around IP address'), + array('email@[IPv6:::1]', 'Square bracket around IPv6 address (1)'), + array('email@[IPv6:::1.2.3.4]', 'Square bracket around IPv6 address (2)'), + array('email@[IPv6:2001:2d12:c4fe:5afe::1]', 'Square bracket around IPv6 address (3)'), array('"email"@domain.com', 'Quotes around email is considered valid'), array('1234567890@domain.com', 'Digits in address are valid'), array('email@domain-one.com', 'Dash in domain name is valid'), @@ -50,7 +52,16 @@ class Utils extends PHPUnit_Framework_TestCase array('email@domain', 'Missing top level domain (.com/.net/.org/etc)'), array('email@-domain.com', 'Leading dash in front of domain is invalid'), // array('email@domain.web', '.web is not a valid top level domain'), - array('email@111.222.333.44444', 'Invalid IP format'), + array('email@123.123.123.123', 'IP address without brackets'), + array('email@2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets'), + array('email@IPv6:2001:2d12:c4fe:5afe::1', 'IPv6 address without brackets (2)'), + array('email@[111.222.333.44444]', 'Invalid IP format'), + array('email@[111.222.255.257]', 'Invalid IP format (2)'), + array('email@[.222.255.257]', 'Invalid IP format (3)'), + array('email@[::1]', 'Invalid IPv6 format (1)'), + array('email@[IPv6:2001:23x2:1]', 'Invalid IPv6 format (2)'), + array('email@[IPv6:1111:2222:33333::4444:5555]', 'Invalid IPv6 format (3)'), + array('email@[IPv6:1111::3333::4444:5555]', 'Invalid IPv6 format (4)'), array('email@domain..com', 'Multiple dot in the domain portion is invalid'), ); } -- cgit v1.2.3 From 553225e1be27f2c58c0995decc10651566add78f Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 24 Aug 2012 10:31:58 +0200 Subject: Fix setting locales under Solaris - use additional .UTF-8 suffix (#1488628) --- CHANGELOG | 1 + program/include/rcmail.php | 10 ++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index d529169e4..b8664b97a 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix setting locales under Solaris - use additional .UTF-8 suffix (#1488628) - Fix email address validation for addresses with IP address in domain part - Fix Larry skin issues in IE7 compat. mode (#1488618) - Fix so subscribed non-existing/non-accessible shared folder can be unsubscribed diff --git a/program/include/rcmail.php b/program/include/rcmail.php index d866919d6..336e9ca71 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -157,14 +157,16 @@ class rcmail extends rcube $this->config->set_user_prefs((array)$this->user->get_prefs()); } - $_SESSION['language'] = $this->user->language = $this->language_prop($this->config->get('language', $_SESSION['language'])); + $lang = $this->language_prop($this->config->get('language', $_SESSION['language'])); + $_SESSION['language'] = $this->user->language = $lang; // set localization - setlocale(LC_ALL, $_SESSION['language'] . '.utf8', 'en_US.utf8'); + setlocale(LC_ALL, $lang . '.utf8', $lang . '.UTF-8', 'en_US.utf8', 'en_US.UTF-8'); // workaround for http://bugs.php.net/bug.php?id=18556 - if (in_array($_SESSION['language'], array('tr_TR', 'ku', 'az_AZ'))) - setlocale(LC_CTYPE, 'en_US' . '.utf8'); + if (in_array($lang, array('tr_TR', 'ku', 'az_AZ'))) { + setlocale(LC_CTYPE, 'en_US.utf8', 'en_US.UTF-8'); + } } -- cgit v1.2.3 From e8be303af3b3cb5187101e9d6f160e0aa8f07285 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 24 Aug 2012 13:36:12 +0200 Subject: Added tests for shared functions, fixed small issues --- program/include/rcube_shared.inc | 8 ++++---- tests/phpunit.xml | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 5b839d8d2..276940cb6 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -108,11 +108,11 @@ function slashify($str) /** - * Remove slash at the end of the string + * Remove slashes at the end of the string */ function unslashify($str) { - return preg_replace('/\/$/', '', $str); + return preg_replace('/\/+$/', '', $str); } @@ -231,7 +231,7 @@ function array_keys_recursive($array) { $keys = array(); - if (!empty($array)) { + if (!empty($array) && is_array($array)) { foreach ($array as $key => $child) { $keys[] = $key; foreach (array_keys_recursive($child) as $val) { @@ -255,7 +255,7 @@ function asciiwords($str, $css_id = false, $replace_with = '') /** - * Remove single and double quotes from given string + * Remove single and double quotes from a given string * * @param string Input value * diff --git a/tests/phpunit.xml b/tests/phpunit.xml index cfd066e29..d6212f458 100644 --- a/tests/phpunit.xml +++ b/tests/phpunit.xml @@ -7,6 +7,7 @@ MailDecode.php MailFunc.php ModCss.php + Shared.php Utils.php VCards.php -- cgit v1.2.3 From 651c7b6e9df38a3b7cdf6daebed39021d88c3bea Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 26 Aug 2012 18:20:28 +0200 Subject: Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149) --- CHANGELOG | 1 + config/main.inc.php.dist | 7 +++++-- installer/rcube_install.php | 11 ++++++----- program/include/rcube_config.php | 1 + program/localization/en_US/labels.inc | 5 +++-- program/steps/mail/compose.inc | 9 +++++---- program/steps/settings/func.inc | 15 ++++++++------- program/steps/settings/save_prefs.inc | 4 ++-- 8 files changed, 31 insertions(+), 22 deletions(-) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 05540a5a4..f48ace4e8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149) - Fix Larry's messages list filter in IE (#1488632) - Fix more IE issues by disabling Compat. mode with X-UA-Compatible meta tag (#1488626) - Fix setting locales under Solaris - use additional .UTF-8 suffix (#1488628) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 504593028..69a6ea279 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -783,8 +783,11 @@ $rcmail_config['display_next'] = true; // 2 - Expand only threads with unread messages $rcmail_config['autoexpand_threads'] = 0; -// When replying place cursor above original message (top posting) -$rcmail_config['top_posting'] = false; +// When replying: +// -1 - don't cite the original message +// 0 - place cursor below the original message +// 1 - place cursor above original message (top posting) +$rcmail_config['reply_mode'] = 0; // When replying strip original signature from message $rcmail_config['strip_existing_sig'] = true; diff --git a/installer/rcube_install.php b/installer/rcube_install.php index bfb111f1d..5af871346 100644 --- a/installer/rcube_install.php +++ b/installer/rcube_install.php @@ -35,13 +35,14 @@ class rcube_install var $obsolete_config = array('db_backend', 'double_auth'); var $replaced_config = array( - 'skin_path' => 'skin', - 'locale_string' => 'language', - 'multiple_identities' => 'identities_level', + 'skin_path' => 'skin', + 'locale_string' => 'language', + 'multiple_identities' => 'identities_level', 'addrbook_show_images' => 'show_images', - 'imap_root' => 'imap_ns_personal', - 'pagesize' => 'mail_pagesize', + 'imap_root' => 'imap_ns_personal', + 'pagesize' => 'mail_pagesize', 'default_imap_folders' => 'default_folders', + 'top_posting' => 'reply_mode', ); // these config options are required for a working system diff --git a/program/include/rcube_config.php b/program/include/rcube_config.php index e2997906c..41acc80dd 100644 --- a/program/include/rcube_config.php +++ b/program/include/rcube_config.php @@ -42,6 +42,7 @@ class rcube_config 'default_folders' => 'default_imap_folders', 'mail_pagesize' => 'pagesize', 'addressbook_pagesize' => 'pagesize', + 'reply_mode' => 'top_posting', ); diff --git a/program/localization/en_US/labels.inc b/program/localization/en_US/labels.inc index 6085b3898..88de277ac 100644 --- a/program/localization/en_US/labels.inc +++ b/program/localization/en_US/labels.inc @@ -429,8 +429,9 @@ $labels['maintenance'] = 'Maintenance'; $labels['newmessage'] = 'New Message'; $labels['signatureoptions'] = 'Signature Options'; $labels['whenreplying'] = 'When replying'; -$labels['replytopposting'] = 'start new message above original'; -$labels['replybottomposting'] = 'start new message below original'; +$labels['replyempty'] = 'do not quote the original message'; +$labels['replytopposting'] = 'start new message above the quote'; +$labels['replybottomposting'] = 'start new message below the quote'; $labels['replyremovesignature'] = 'When replying remove original signature from message'; $labels['autoaddsignature'] = 'Automatically add signature'; $labels['newmessageonly'] = 'new message only'; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index 56f4a052b..c243c887e 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -139,7 +139,7 @@ if (!empty($CONFIG['drafts_mbox'])) { // set current mailbox in client environment $OUTPUT->set_env('mailbox', $RCMAIL->storage->get_folder()); $OUTPUT->set_env('sig_above', $RCMAIL->config->get('sig_above', false)); -$OUTPUT->set_env('top_posting', $RCMAIL->config->get('top_posting', false)); +$OUTPUT->set_env('top_posting', intval($RCMAIL->config->get('reply_mode')) > 0); $OUTPUT->set_env('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); // default font for HTML editor @@ -641,7 +641,7 @@ function rcmail_prepare_message_body() rcmail_write_forward_attachment($MESSAGE); } // reply/edit/draft/forward - else if ($compose_mode) { + else if ($compose_mode && ($compose_mode != RCUBE_COMPOSE_REPLY || $RCMAIL->config->get('reply_mode') != -1)) { $isHtml = rcmail_compose_editor_mode(); if (!empty($MESSAGE->parts)) { @@ -906,8 +906,9 @@ function rcmail_create_reply_body($body, $bodyIsHtml) $prefix .= "\n"; $suffix = ''; - if ($RCMAIL->config->get('top_posting')) + if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting $prefix = "\n\n\n" . $prefix; + } } else { // save inline images to files @@ -921,7 +922,7 @@ function rcmail_create_reply_body($body, $bodyIsHtml) $prefix = '

' . Q($prefix) . "

\n"; $prefix .= '
'; - if ($RCMAIL->config->get('top_posting')) { + if (intval($RCMAIL->config->get('reply_mode')) > 0) { // top-posting $prefix = '
' . $prefix; $suffix = '
'; } diff --git a/program/steps/settings/func.inc b/program/steps/settings/func.inc index 3f5ef5390..6d548ef36 100644 --- a/program/steps/settings/func.inc +++ b/program/steps/settings/func.inc @@ -544,16 +544,17 @@ function rcmail_user_prefs($current=null) ); } - if (!isset($no_override['top_posting'])) { - $field_id = 'rcmfd_top_posting'; - $select_replymode = new html_select(array('name' => '_top_posting', 'id' => $field_id, - 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex==0)")); + if (!isset($no_override['reply_mode'])) { + $field_id = 'rcmfd_reply_mode'; + $select_replymode = new html_select(array('name' => '_reply_mode', 'id' => $field_id, + 'onchange' => "\$('#rcmfd_sig_above').attr('disabled',this.selectedIndex<2)")); + $select_replymode->add(rcube_label('replyempty'), -1); $select_replymode->add(rcube_label('replybottomposting'), 0); $select_replymode->add(rcube_label('replytopposting'), 1); - $blocks['main']['options']['top_posting'] = array( + $blocks['main']['options']['reply_mode'] = array( 'title' => html::label($field_id, Q(rcube_label('whenreplying'))), - 'content' => $select_replymode->show($config['top_posting']?1:0), + 'content' => $select_replymode->show(intval($config['reply_mode'])), ); } @@ -597,7 +598,7 @@ function rcmail_user_prefs($current=null) if (!isset($no_override['sig_above'])) { $field_id = 'rcmfd_sig_above'; - $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => !$config['top_posting'])); + $select_sigabove = new html_select(array('name' => '_sig_above', 'id' => $field_id, 'disabled' => $config['reply_mode'] < 1)); $select_sigabove->add(rcube_label('belowquote'), 0); $select_sigabove->add(rcube_label('abovequote'), 1); diff --git a/program/steps/settings/save_prefs.inc b/program/steps/settings/save_prefs.inc index 88fa5298a..dc149929e 100644 --- a/program/steps/settings/save_prefs.inc +++ b/program/steps/settings/save_prefs.inc @@ -82,9 +82,9 @@ switch ($CURR_SECTION) 'spellcheck_ignore_nums' => isset($_POST['_spellcheck_ignore_nums']) ? TRUE : FALSE, 'spellcheck_ignore_caps' => isset($_POST['_spellcheck_ignore_caps']) ? TRUE : FALSE, 'show_sig' => isset($_POST['_show_sig']) ? intval($_POST['_show_sig']) : 1, - 'top_posting' => !empty($_POST['_top_posting']), + 'reply_mode' => isset($_POST['_reply_mode']) ? intval($_POST['_reply_mode']) : 0, 'strip_existing_sig' => isset($_POST['_strip_existing_sig']), - 'sig_above' => !empty($_POST['_sig_above']) && !empty($_POST['_top_posting']), + 'sig_above' => !empty($_POST['_sig_above']) && $_POST['_reply_mode'] < 1, 'default_font' => get_input_value('_default_font', RCUBE_INPUT_POST), 'forward_attachment' => !empty($_POST['_forward_attachment']), ); -- cgit v1.2.3 From 6075f084ecbff71490fc5594f2d9470d87938317 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 27 Aug 2012 11:47:42 +0200 Subject: Fix deprecated functions usage --- program/include/rcmail.php | 4 ++-- program/include/rcube_bc.inc | 5 ----- program/include/rcube_imap_cache.php | 6 +++--- program/include/rcube_mime.php | 4 ++-- program/include/rcube_vcard.php | 2 +- 5 files changed, 8 insertions(+), 13 deletions(-) (limited to 'program/include') diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 336e9ca71..02f38e647 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -308,7 +308,7 @@ class rcmail extends rcube /** * Init output object for GUI and add common scripts. - * This will instantiate a rcmail_template object and set + * This will instantiate a rcube_output_html object and set * environment vars according to the current session and configuration * * @param boolean True if this request is loaded in a (i)frame @@ -1209,7 +1209,7 @@ class rcmail extends rcube } else { if (!empty($date)) { - $timestamp = rcube_strtotime($date); + $timestamp = rcube_utils::strtotime($date); } if (empty($timestamp)) { diff --git a/program/include/rcube_bc.inc b/program/include/rcube_bc.inc index 1932f86e2..1894873e6 100644 --- a/program/include/rcube_bc.inc +++ b/program/include/rcube_bc.inc @@ -38,11 +38,6 @@ function get_table_name($table) return rcmail::get_instance()->db->table_name($table); } -function get_sequence_name($sequence) -{ - return rcmail::get_instance()->db->sequence_name($sequence); -} - function rcube_label($p, $domain=null) { return rcmail::get_instance()->gettext($p, $domain); diff --git a/program/include/rcube_imap_cache.php b/program/include/rcube_imap_cache.php index 4d6d5e180..f36ace0eb 100644 --- a/program/include/rcube_imap_cache.php +++ b/program/include/rcube_imap_cache.php @@ -617,13 +617,13 @@ class rcube_imap_cache // get expiration timestamp $ts = get_offset_time($ttl, -1); - $this->db->query("DELETE FROM ".get_table_name('cache_messages') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_messages') ." WHERE changed < " . $this->db->fromunixtime($ts)); - $this->db->query("DELETE FROM ".get_table_name('cache_index') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_index') ." WHERE changed < " . $this->db->fromunixtime($ts)); - $this->db->query("DELETE FROM ".get_table_name('cache_thread') + $this->db->query("DELETE FROM ".$this->db->table_name('cache_thread') ." WHERE changed < " . $this->db->fromunixtime($ts)); } diff --git a/program/include/rcube_mime.php b/program/include/rcube_mime.php index e1f736a78..d8e04a97c 100644 --- a/program/include/rcube_mime.php +++ b/program/include/rcube_mime.php @@ -541,10 +541,10 @@ class rcube_mime $prefix = $regs[0]; $level = strlen($prefix); $line = rtrim(substr($line, $level)); - $line = $prefix . rc_wordwrap($line, $length - $level - 2, " \r\n$prefix "); + $line = $prefix . self::wordwrap($line, $length - $level - 2, " \r\n$prefix "); } else if ($line) { - $line = rc_wordwrap(rtrim($line), $length - 2, " \r\n"); + $line = self::wordwrap(rtrim($line), $length - 2, " \r\n"); // space-stuffing $line = preg_replace('/(^|\r\n)(From| |>)/', '\\1 \\2', $line); } diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php index 37cd3ab26..2bfd474c6 100644 --- a/program/include/rcube_vcard.php +++ b/program/include/rcube_vcard.php @@ -313,7 +313,7 @@ class rcube_vcard case 'birthday': case 'anniversary': - if (($val = rcube_strtotime($value)) && ($fn = self::$fieldmap[$field])) + if (($val = rcube_utils::strtotime($value)) && ($fn = self::$fieldmap[$field])) $this->raw[$fn][] = array(0 => date('Y-m-d', $val), 'value' => array('date')); break; -- cgit v1.2.3 From a65ce5d3b07deb578cc4c4aba5695bcea8c07a87 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 27 Aug 2012 12:23:30 +0200 Subject: Rename ip_check to check_ip, add IP checking tests --- program/include/rcube_utils.php | 4 ++-- tests/Framework/Utils.php | 47 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 2 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index defb2aed1..aa748dc7f 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -94,7 +94,7 @@ class rcube_utils // Validate domain part if (preg_match('/^\[((IPv6:[0-9a-f:.]+)|([0-9.]+))\]$/i', $domain_part, $matches)) { - return self::ip_check(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address + return self::check_ip(preg_replace('/^IPv6:/i', '', $matches[1])); // valid IPv4 or IPv6 address } else { // If not an IP address @@ -154,7 +154,7 @@ class rcube_utils * * @return bool True if the address is valid */ - public static function ip_check($ip) + public static function check_ip($ip) { // IPv6, but there's no build-in IPv6 support if (strpos($ip, ':') !== false && !defined('AF_INET6')) { diff --git a/tests/Framework/Utils.php b/tests/Framework/Utils.php index b6cc5d577..503b69a4a 100644 --- a/tests/Framework/Utils.php +++ b/tests/Framework/Utils.php @@ -82,6 +82,53 @@ class Framework_Utils extends PHPUnit_Framework_TestCase $this->assertFalse(rcube_utils::check_email($email, false), $title); } + /** + * Valid IP addresses for test_valid_ip() + */ + function data_valid_ip() + { + return array( + array('0.0.0.0'), + array('123.123.123.123'), + array('::'), + array('::1'), + array('::1.2.3.4'), + array('2001:2d12:c4fe:5afe::1'), + ); + } + + /** + * Valid IP addresses for test_invalid_ip() + */ + function data_invalid_ip() + { + return array( + array(''), + array(0), + array('123.123.123.1234'), + array('1.1.1.1.1'), + array('::1.2.3.260'), + array('::1.0'), + array('2001::c4fe:5afe::1'), + ); + } + + /** + * @dataProvider data_valid_ip + */ + function test_valid_ip($ip) + { + $this->assertTrue(rcube_utils::check_ip($ip)); + } + + /** + * @dataProvider data_invalid_ip + */ + function test_invalid_ip($ip) + { + $this->assertFalse(rcube_utils::check_ip($ip)); + } + /** * rcube_utils::mod_css_styles() */ -- cgit v1.2.3 From be71abeff913611086e4f36f6888bf57666042c5 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Mon, 27 Aug 2012 12:25:55 +0200 Subject: Fix deprecated function usage --- program/include/rcube_utils.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_utils.php b/program/include/rcube_utils.php index aa748dc7f..23bf556e4 100644 --- a/program/include/rcube_utils.php +++ b/program/include/rcube_utils.php @@ -200,8 +200,8 @@ class rcube_utils public static function check_referer() { $uri = parse_url($_SERVER['REQUEST_URI']); - $referer = parse_url(rcube_request_header('Referer')); - return $referer['host'] == rcube_request_header('Host') && $referer['path'] == $uri['path']; + $referer = parse_url(self::request_header('Referer')); + return $referer['host'] == self::request_header('Host') && $referer['path'] == $uri['path']; } -- cgit v1.2.3 From 397cf794b975e8128a6d155957229106cd065d0c Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 28 Aug 2012 12:22:43 +0200 Subject: Don't terminate script execution when parsed template doesn't exist and parse was called with 3rd argument = false. --- program/include/rcube_output_html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/include') diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index a071ee354..00378d7d2 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -400,7 +400,7 @@ class rcube_output_html extends rcube_output 'line' => __LINE__, 'file' => __FILE__, 'message' => 'Error loading template for '.$realname - ), true, true); + ), true, $write); return false; } -- cgit v1.2.3 From b7d33e35186d47dcd3609e1ef97cdb011c6f82f6 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Tue, 28 Aug 2012 12:59:29 +0200 Subject: Added template object 'frame' --- CHANGELOG | 1 + program/include/rcube_output_html.php | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 8e9dcb3c7..028eb196e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Added template object 'frame' - Fix identity selection on reply (#1488101) - Add option to enable HTML editor on forwarding (#1488517) - Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149) diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 00378d7d2..960002112 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -698,6 +698,11 @@ class rcube_output_html extends rcube_output } break; + // frame + case 'frame': + return $this->frame($attrib); + break; + // show a label case 'label': if ($attrib['name'] || $attrib['command']) { @@ -1275,6 +1280,30 @@ class rcube_output_html extends rcube_output } + /** + * Returns iframe object, registers some related env variables + * + * @param array $attrib HTML attributes + * + * @return string IFRAME element + */ + public function frame($attrib) + { + if (!$attrib['id']) { + $attrib['id'] = 'rcmframe'; + } + + if (!$attrib['name']) { + $attrib['name'] = $attrib['id']; + } + + $this->set_env('contentframe', $attrib['name']); + $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif'); + + return html::iframe($attrib); + } + + /* ************* common functions delivering gui objects ************** */ -- cgit v1.2.3 From c59a822a65bac2aa53eb41978b1ac2d5192710e6 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 29 Aug 2012 08:04:36 +0200 Subject: Remove reference to MDB2 from autoloader --- program/include/rcube_shared.inc | 2 -- 1 file changed, 2 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc index 276940cb6..c15305c08 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -422,7 +422,6 @@ function rcube_autoload($classname) { $filename = preg_replace( array( - '/MDB2_(.+)/', '/Mail_(.+)/', '/Net_(.+)/', '/Auth_(.+)/', @@ -430,7 +429,6 @@ function rcube_autoload($classname) '/^utf8$/', ), array( - 'Mail/\\1', 'Mail/\\1', 'Net/\\1', 'Auth/\\1', -- cgit v1.2.3 From 764641d4a38e0a9856cced092ac7c294a2956dcf Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 29 Aug 2012 09:22:07 +0200 Subject: Fix decoding of HTML messages with UTF-16 charset specified (#1488654) --- CHANGELOG | 1 + program/include/rcube_charset.php | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index cddfc96f8..7654cdef8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix decoding of HTML messages with UTF-16 charset specified (#1488654) - Fix quota capability detection so it can be overwritten by a plugin (#1488655) - Added template object 'frame' - Fix identity selection on reply (#1488101) diff --git a/program/include/rcube_charset.php b/program/include/rcube_charset.php index 380d14978..1740a6096 100644 --- a/program/include/rcube_charset.php +++ b/program/include/rcube_charset.php @@ -181,6 +181,12 @@ class rcube_charset $to = empty($to) ? strtoupper(RCMAIL_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; } -- cgit v1.2.3 From 10562d8a7760eae6b6cb22aa336605e09c7a8bce Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 29 Aug 2012 11:05:02 +0200 Subject: Keep current folder context when fetching message parts, to make sure that proper folder is used in case when current folder has been changed in meantime. --- program/include/rcube_message.php | 26 +++++++++++++++++++------- program/include/rcube_storage.php | 2 +- 2 files changed, 20 insertions(+), 8 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php index f550b574e..6af1d0133 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -52,7 +52,8 @@ class rcube_message private $opt = array(); private $parse_alternative = false; - public $uid = null; + public $uid; + public $folder; public $headers; public $parts = array(); public $mime_parts = array(); @@ -68,17 +69,22 @@ class rcube_message * * Provide a uid, and parse message structure. * - * @param string $uid The message UID. + * @param string $uid The message UID. + * @param string $folder Folder name * * @see self::$app, self::$storage, self::$opt, self::$parts */ - function __construct($uid) + function __construct($uid, $folder = null) { $this->uid = $uid; $this->app = rcube::get_instance(); $this->storage = $this->app->get_storage(); + $this->folder = strlen($folder) ? $folder : $this->storage->get_folder(); $this->storage->set_options(array('all_headers' => true)); + // Set current folder + $this->storage->set_folder($this->folder); + $this->headers = $this->storage->get_message($uid); if (!$this->headers) @@ -179,10 +185,12 @@ class rcube_message } return $fp ? true : $part->body; } + // get from IMAP + $this->storage->set_folder($this->folder); + return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv); - } else - return null; + } } @@ -637,8 +645,10 @@ class rcube_message function tnef_decode(&$part) { // @TODO: attachment may be huge, hadle it via file - if (!isset($part->body)) + if (!isset($part->body)) { + $this->storage->set_folder($this->folder); $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); + } $parts = array(); $tnef = new tnef_decoder; @@ -673,8 +683,10 @@ class rcube_message function uu_decode(&$part) { // @TODO: messages may be huge, hadle body via file - if (!isset($part->body)) + if (!isset($part->body)) { + $this->storage->set_folder($this->folder); $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part); + } $parts = array(); // FIXME: line length is max.65? diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php index 768a26d73..f83e24041 100644 --- a/program/include/rcube_storage.php +++ b/program/include/rcube_storage.php @@ -195,7 +195,7 @@ abstract class rcube_storage */ public function set_folder($folder) { - if ($this->folder == $folder) { + if ($this->folder === $folder) { return; } -- cgit v1.2.3 From d317a59d3041223c5070ae72d3d7187ef0c089ca Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Wed, 29 Aug 2012 12:40:09 +0200 Subject: Fix frame() so frame ID is set as 'contentframe' env variable (not name) --- program/include/rcube_output_html.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'program/include') diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php index 960002112..2743e7705 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -1297,7 +1297,7 @@ class rcube_output_html extends rcube_output $attrib['name'] = $attrib['id']; } - $this->set_env('contentframe', $attrib['name']); + $this->set_env('contentframe', $attrib['id']); $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif'); return html::iframe($attrib); -- cgit v1.2.3 From b5652641be7ebf4f1b7f115333989d2c971275ea Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Fri, 31 Aug 2012 09:55:00 +0200 Subject: Small improvements in APPEND command handling --- program/include/rcube_imap.php | 4 ++++ program/include/rcube_imap_generic.php | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'program/include') diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index 5dd9c1250..66b5c4bd6 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -2222,6 +2222,10 @@ class rcube_imap extends rcube_storage $folder = $this->folder; } + if (!$this->check_connection()) { + return false; + } + // make sure folder exists if ($this->folder_exists($folder)) { if ($is_file) { diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index 915a11aad..c3cfabc3a 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -2538,7 +2538,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2603,7 +2603,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2612,6 +2612,7 @@ class rcube_imap_generic if (file_exists(realpath($path))) { $in_fp = fopen($path, 'r'); } + if (!$in_fp) { $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading"); return false; -- cgit v1.2.3 From 8f66aa06f58ba2c9f99541a191ee4bcaa98500b3 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 9 Sep 2012 16:50:14 +0200 Subject: Fix encoding vCard file when contains PHOTO;ENCODING=b (#1488683) --- CHANGELOG | 1 + program/include/rcube_vcard.php | 1 + tests/Framework/VCard.php | 14 +++++++++++++ tests/src/photo.vcf | 45 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+) create mode 100644 tests/src/photo.vcf (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index 0706009c4..bb74cf22e 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix encoding vCard file when contains PHOTO;ENCODING=b (#1488683) - Fix focus issue in IE when selecting message row (#1488620) - Remove (too big) min-width on mail screen - Add full headers view in message preview window (#1488538) diff --git a/program/include/rcube_vcard.php b/program/include/rcube_vcard.php index 2bfd474c6..49b312c5c 100644 --- a/program/include/rcube_vcard.php +++ b/program/include/rcube_vcard.php @@ -555,6 +555,7 @@ class rcube_vcard if ((list($key, $value) = explode('=', $attr)) && $value) { $value = trim($value); if ($key == 'ENCODING') { + $value = strtoupper($value); // add next line(s) to value string if QP line end detected if ($value == 'QUOTED-PRINTABLE') { while (preg_match('/=$/', $lines[$i])) diff --git a/tests/Framework/VCard.php b/tests/Framework/VCard.php index a830c2cbc..56ca9d721 100644 --- a/tests/Framework/VCard.php +++ b/tests/Framework/VCard.php @@ -49,6 +49,20 @@ class Framework_VCard extends PHPUnit_Framework_TestCase $this->assertEquals("Iksiñski", $vcards2[0]->surname, "Detect charset in encoded values"); } + function test_import_photo_encoding() + { + $input = file_get_contents($this->_srcpath('photo.vcf')); + + $vcards = rcube_vcard::import($input); + $vcard = $vcards[0]->get_assoc(); + + $this->assertCount(1, $vcards, "Detected 1 vcard"); + + // ENCODING=b case (#1488683) + $this->assertEquals("/9j/4AAQSkZJRgABAQA", substr(base64_encode($vcard['photo']), 0, 19), "Photo decoding"); + $this->assertEquals("Müller", $vcard['surname'], "Unicode characters"); + } + function test_encodings() { $input = file_get_contents($this->_srcpath('utf-16_sample.vcf')); diff --git a/tests/src/photo.vcf b/tests/src/photo.vcf new file mode 100644 index 000000000..c3a805009 --- /dev/null +++ b/tests/src/photo.vcf @@ -0,0 +1,45 @@ +BEGIN:VCARD +VERSION:3.0 +N:Müller;Jörg;;; +FN:Apple Computer AG +ORG:Apple Computer AG; +PHOTO;ENCODING=b: + /9j/4AAQSkZJRgABAQAAAQABAAD/7QAcUGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAD/2wBDAAEB + AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB + AQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB + AQEBAQEBAQEBAQEBAQEBAQH/wAARCAAwADADAREAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAA + AAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEI + I0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlq + c3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW + 19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL + /8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLR + ChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOE + hYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn + 6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD+/igAoAKAPmH43ftT+CfgzqNt4bNjeeLvGV2IHXw7 + pVxDbLZx3LBbdtU1GVLhbN7jIMFvHa3VzIpWRoY4mWQ9dDCTrrmuoQ/mavfvZXV7dW2jkr4ynQfL + Zzn1inZL1lZ6+ST87H0lp1zLe6fY3k9s1nNd2dtczWjv5j2ss8KSyWzybU3tA7mJn2JuKk7Vzgcr + Vm1e9m1fvrv8zqi+aKbVm0nbtdXt8i5SGFAHHeOfH3hH4b6DP4k8Z61a6JpUBCCW4LPNdTsCUtbK + 1iD3F5dSYO2C3jd8AuwVFZhdOnOrLlhFyf4Lzb6IzqVYUo81SSiundvslu3/AEz5i0n9u74Fanqo + 064m8UaNbvII49X1TRUGnnORvmFje3t5BGTjDtatwcyCPBrreArpX9xvqlJ3/FJP7zlWYUHKzVRL + +ZxVvnaTa+4+QLvVPgJ4U+LutfFXx78QJfi3q954iuvEOieHvBmkyzaVbO1x5ulPruq6pLawTvp1 + uLdIrK08xFmgXzl2oI67LV50o0qdP2K5VGUptKXnyxi29XfVtb6HDehGtKpUm6zcnNKCfK9brnlK + 3pZJ37pH3/8ACj9qb4T/ABd1FdD0TUr3SPETozwaJ4ht47C5vQgLONOnjnuLS8dVBYwJOt1tDMIN + oLV51XCVqK5pJSj1cW3b1uk1vvqvM9Kji6VZ8qbjJ7KVlf0abTf4n0dXMdQUAfhf+2J8UNW8ffFz + W9Cnlki0XwDqOp+GdNsFdhB9qsr6a31DUDHu2tcXbwojSEbhFCka4Uc+9hKUadGMl8VRKcn11V0v + RJng4urKpWkntTlKCXTRtN+re58n11HKFAH6PfsGfBvQfEl3rHxR8Q2y38vhrUrfT/DNpIT5FvqY + iF1carIgI8ye2R4Y7RXykbySTbS6xlfOx9aUVGlF2503N+W1vnrc9HAUYzlKrJX5GuVf3t7+dvu3 + vc/WKvIPXCgD8FP2s/BF/wCC/jd4wlu0It/Fmp6h4usJf4JINZ1G7ndVbu0UpZZBztY446V9BhZq + dCnZ/DFQfrFJHz+Kg4V6l/tSc15qTb/rzPmqug5woA++v2J/j74d+HN9rHgLxndrpmjeJr62vtJ1 + mbP2Sw1dY/s0ltfOM+Rb30Yh8u5ZTHFPEFlZEk3Dgx2HlVUZw1lBNOPVp9vNa6X22137sFiI0ZSh + N2jNpqXSLSe/k+/R+p+w1eMe0FAHxz+2P8DLr4r+CIPEPhy2Nx4y8FJdXVnaxrmfWdGlAk1DSosK + WkuozEt5p8fHmTLNADuuQR24KuqU3GTtCpbXtLo32T2b9GcWNw7qwU4q84X06yi9Wl531XfXyPxK + ME4nNsYZRciUwG3MbicTh/LMJix5glD/ACGPbv3/AC4zxXtniFvUNJ1XSmjXVNM1DTWmUvCuoWVz + ZtKgxloxcRxmRRkZZcgZGTzSTT2afo7hqtz6w/ZB+BV78UPHtl4n1eykHgfwdewajfXE0bCDVtVt + mE+n6PAzAJOBOkdxqAUssdsnlSDNwoPLi66pU3FP95NNJdk95P06d35XOvCUHWqJtfu4NOT7vdR8 + 7vfsvVH7gV4R7oUAFAHKReA/A8Gsy+IofBnhSHxBO7ST67F4d0iPWZnb7zy6mlmL2R2/iZ5yT3NX + 7Spbl9pPl/l55W+69iPZUubm9nDmvfm5I81+97XuX9a8MeGvEtsLLxF4e0PX7MMGFprWk2Gq2wZe + jCC+t54tw7HZkdqUZzg7wlKL7xk4v700OUIT0lCMl2lFS/NMuaXpOlaHZQ6Zoumafo+nW4It9P0u + yttPsoATkiG1tI4oIgTyQkagnmk5Sk7ybk+7bb+96jjGMVaMVFdopJfcjQpDP//Z +X-ABShowAs:COMPANY +X-ABUID:2E4CB084-4767-4C85-BBCA-805B1DCB1C8E\:ABPerson +END:VCARD -- cgit v1.2.3 From 4e383e2ec8b4184c0fe74d02cf30fd3a4078128e Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Sun, 9 Sep 2012 17:35:19 +0200 Subject: Fix PLAIN authentication for some IMAP servers (#1488674) --- CHANGELOG | 1 + program/include/rcube_imap_generic.php | 2 ++ 2 files changed, 3 insertions(+) (limited to 'program/include') diff --git a/CHANGELOG b/CHANGELOG index bb74cf22e..1d2225a1f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ CHANGELOG Roundcube Webmail =========================== +- Fix PLAIN authentication for some IMAP servers (#1488674) - Fix encoding vCard file when contains PHOTO;ENCODING=b (#1488683) - Fix focus issue in IE when selecting message row (#1488620) - Remove (too big) min-width on mail screen diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index c3cfabc3a..25e6fc421 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -530,6 +530,7 @@ class rcube_imap_generic } else { $authc = $user; + $user = ''; } $auth_sasl = Auth_SASL::factory('digestmd5'); $reply = base64_encode($auth_sasl->getResponse($authc, $pass, @@ -568,6 +569,7 @@ class rcube_imap_generic } else { $authc = $user; + $user = ''; } $reply = base64_encode($user . chr(0) . $authc . chr(0) . $pass); -- cgit v1.2.3