From 51f7a5b2a09777d3a279757af620e42985ff9a86 Mon Sep 17 00:00:00 2001 From: alecpl Date: Wed, 9 Nov 2011 10:03:54 +0000 Subject: - Apply fixes from trunk up to r5401 --- CHANGELOG | 6 +++ config/main.inc.php.dist | 5 +- program/include/main.inc | 86 ++++++++++++++++++------------- program/include/rcmail.php | 1 - program/include/rcube_contacts.php | 2 +- program/include/rcube_imap.php | 83 +++++++++++++++++++---------- program/include/rcube_imap_generic.php | 56 ++++++++++---------- program/include/rcube_ldap.php | 12 +++++ program/include/rcube_mdb2.php | 61 ++++++++++------------ program/include/rcube_string_replacer.php | 2 +- program/js/app.js | 10 ++-- program/steps/addressbook/func.inc | 13 +++-- program/steps/mail/autocomplete.inc | 7 +-- program/steps/mail/compose.inc | 12 +++-- program/steps/mail/func.inc | 2 +- program/steps/mail/sendmail.inc | 31 +++++------ program/steps/settings/edit_folder.inc | 3 +- program/steps/settings/folders.inc | 4 +- 18 files changed, 235 insertions(+), 161 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index fb3bb4e65..2b5e7094b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,12 @@ CHANGELOG Roundcube Webmail =========================== +- Make email recipients separator configurable +- Fix so folders with \Noinferiors attribute aren't listed in parent selector +- Fix handling of curly brackets in URLs (#1488168) +- Fix handling of dates (birthday/anniversary) in contact data (#1488147) +- Fix error on opening searched LDAP contact (#1488144) +- Fix redundant line break in flowed format (#1488146) - Fix IDN address validation issue (#1488137) - Fix JS error when dst_active checkbox doesn't exist (#1488133) - Autocomplete LDAP records when adding contacts from mail (#1488073) diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist index 896fcbe65..9493b3057 100644 --- a/config/main.inc.php.dist +++ b/config/main.inc.php.dist @@ -5,7 +5,7 @@ | Main configuration file | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | Copyright (C) 2005-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | +-----------------------------------------------------------------------+ @@ -460,6 +460,9 @@ $rcmail_config['spellcheck_ignore_nums'] = false; // Makes that words with symbols will be ignored (e.g. g@@gle) $rcmail_config['spellcheck_ignore_syms'] = false; +// Use this char/string to separate recipients when composing a new message +$rcmail_config['recipients_separator'] = ','; + // don't let users set pagesize to more than this value if set $rcmail_config['max_pagesize'] = 200; diff --git a/program/include/main.inc b/program/include/main.inc index 3980794eb..95d422d6a 100644 --- a/program/include/main.inc +++ b/program/include/main.inc @@ -1018,15 +1018,15 @@ function rcube_strtotime($date) * Convert the given date to a human readable form * This uses the date formatting properties from config * - * @param mixed Date representation (string or timestamp) + * @param mixed Date representation (string or timestamp) * @param string Date format to use + * @param bool Enables date convertion according to user timezone + * * @return string Formatted date string */ -function format_date($date, $format=NULL) +function format_date($date, $format=NULL, $convert=true) { global $RCMAIL, $CONFIG; - - $ts = NULL; if (!empty($date)) $ts = rcube_strtotime($date); @@ -1034,23 +1034,29 @@ function format_date($date, $format=NULL) if (empty($ts)) return ''; - // get user's timezone offset - $tz = $RCMAIL->config->get_timezone(); - - // convert time to user's timezone - $timestamp = $ts - date('Z', $ts) + ($tz * 3600); + if ($convert) { + // get user's timezone offset + $tz = $RCMAIL->config->get_timezone(); - // get current timestamp in user's timezone - $now = time(); // local time - $now -= (int)date('Z'); // make GMT time - $now += ($tz * 3600); // user's time - $now_date = getdate($now); + // convert time to user's timezone + $timestamp = $ts - date('Z', $ts) + ($tz * 3600); - $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); - $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); + // get current timestamp in user's timezone + $now = time(); // local time + $now -= (int)date('Z'); // make GMT time + $now += ($tz * 3600); // user's time + } + else { + $now = time(); + $timestamp = $ts; + } // define date format depending on current time if (!$format) { + $now_date = getdate($now); + $today_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday'], $now_date['year']); + $week_limit = mktime(0, 0, 0, $now_date['mon'], $now_date['mday']-6, $now_date['year']); + if ($CONFIG['prettydate'] && $timestamp > $today_limit && $timestamp < $now) { $format = $RCMAIL->config->get('date_today', $RCMAIL->config->get('time_format', 'H:i')); $today = true; @@ -1226,7 +1232,7 @@ function rcmail_mailbox_select($p = array()) if ($p['noselection']) $select->add($p['noselection'], ''); - rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p['exceptions']); + rcmail_render_folder_tree_select($a_mailboxes, $mbox, $p['maxlength'], $select, $p['realnames'], 0, $p); return $select; } @@ -1275,9 +1281,9 @@ function rcmail_build_folder_tree(&$arrFolders, $folder, $delm='/', $path='') $path .= $prefix.$currentFolder; if (!isset($arrFolders[$currentFolder])) { - // Check \Noselect option (if options are in cache) - if (!$virtual && ($opts = $RCMAIL->imap->mailbox_options($path))) { - $virtual = in_array('\\Noselect', $opts); + // Check \Noselect attribute (if attributes are in cache) + if (!$virtual && ($attrs = $RCMAIL->imap->mailbox_attributes($path))) { + $virtual = in_array('\\Noselect', $attrs); } $arrFolders[$currentFolder] = array( @@ -1396,30 +1402,40 @@ function rcmail_render_folder_tree_html(&$arrFolders, &$mbox_name, &$jslist, $at * @access private * @return string */ -function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $exceptions=array()) +function rcmail_render_folder_tree_select(&$arrFolders, &$mbox_name, $maxlength, &$select, $realnames=false, $nestLevel=0, $opts=array()) { + global $RCMAIL; + $out = ''; foreach ($arrFolders as $key => $folder) { - if (empty($exceptions) || !in_array($folder['id'], $exceptions)) { - if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) - $foldername = rcube_label($folder_class); - else { - $foldername = $folder['name']; - - // shorten the folder name to a given length - if ($maxlength && $maxlength>1) - $foldername = abbreviate_string($foldername, $maxlength); - } - - $select->add(str_repeat(' ', $nestLevel*4) . $foldername, $folder['id']); + // skip exceptions (and its subfolders) + if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) { + continue; } - else if ($nestLevel) + + // skip folders in which it isn't possible to create subfolders + if (!empty($opts['skip_noinferiors']) && ($attrs = $RCMAIL->imap->mailbox_attributes($folder['id'])) + && in_array('\\Noinferiors', $attrs) + ) { continue; + } + + if (!$realnames && ($folder_class = rcmail_folder_classname($folder['id']))) + $foldername = rcube_label($folder_class); + else { + $foldername = $folder['name']; + + // shorten the folder name to a given length + if ($maxlength && $maxlength>1) + $foldername = abbreviate_string($foldername, $maxlength); + } + + $select->add(str_repeat(' ', $nestLevel*4) . $foldername, $folder['id']); if (!empty($folder['folders'])) $out .= rcmail_render_folder_tree_select($folder['folders'], $mbox_name, $maxlength, - $select, $realnames, $nestLevel+1, $exceptions); + $select, $realnames, $nestLevel+1, $opts); } return $out; diff --git a/program/include/rcmail.php b/program/include/rcmail.php index 1ecdfcde0..969e101f7 100644 --- a/program/include/rcmail.php +++ b/program/include/rcmail.php @@ -594,7 +594,6 @@ class rcmail return; $this->imap = new rcube_imap(); - $this->imap->debug_level = $this->config->get('debug_level'); $this->imap->skip_deleted = $this->config->get('skip_deleted'); // enable caching of imap data diff --git a/program/include/rcube_contacts.php b/program/include/rcube_contacts.php index a2eeffc8a..e822d2c24 100644 --- a/program/include/rcube_contacts.php +++ b/program/include/rcube_contacts.php @@ -41,7 +41,6 @@ class rcube_contacts extends rcube_addressbook private $user_id = 0; private $filter = null; private $result = null; - private $name; private $cache; private $table_cols = array('name', 'email', 'firstname', 'surname'); private $fulltext_cols = array('name', 'firstname', 'surname', 'middlename', 'nickname', @@ -50,6 +49,7 @@ class rcube_contacts extends rcube_addressbook // public properties public $primary_key = 'contact_id'; + public $name; public $readonly = false; public $groups = true; public $undelete = true; diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php index d2f954733..7508acda5 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -32,7 +32,6 @@ */ class rcube_imap { - public $debug_level = 1; public $skip_deleted = false; public $page_size = 10; public $list_page = 1; @@ -317,6 +316,19 @@ class rcube_imap } + /** + * Activate/deactivate debug mode + * + * @param boolean $dbg True if IMAP conversation should be logged + * @access public + */ + function set_debug($dbg = true) + { + $this->options['debug'] = $dbg; + $this->conn->setDebug($dbg, array($this, 'debug_handler')); + } + + /** * Set default message charset * @@ -3075,7 +3087,19 @@ class rcube_imap */ function list_unsubscribed($root='', $name='*', $filter=null, $rights=null, $skip_sort=false) { - // @TODO: caching + $cache_key = $root.':'.$name; + if (!empty($filter)) { + $cache_key .= ':'.(is_string($filter) ? $filter : serialize($filter)); + } + $cache_key .= ':'.$rights; + $cache_key = 'mailboxes.list.'.md5($cache_key); + + // get cached folder list + $a_mboxes = $this->get_cache($cache_key); + if (is_array($a_mboxes)) { + return $a_mboxes; + } + // Give plugins a chance to provide a list of mailboxes $data = rcmail::get_instance()->plugins->exec_hook('mailboxes_list', array('root' => $root, 'name' => $name, 'filter' => $filter, 'mode' => 'LIST')); @@ -3097,6 +3121,11 @@ class rcube_imap array_unshift($a_mboxes, 'INBOX'); } + // cache folder attributes + if ($root == '' && $name == '*' && empty($filter)) { + $this->update_cache('mailboxes.attributes', $this->conn->data['LIST']); + } + // filter folders list according to rights requirements if ($rights && $this->get_capability('ACL')) { $a_folders = $this->filter_rights($a_folders, $rights); @@ -3107,6 +3136,9 @@ class rcube_imap $a_mboxes = $this->_sort_mailbox_list($a_mboxes); } + // write mailboxlist to cache + $this->update_cache($cache_key, $a_mboxes); + return $a_mboxes; } @@ -3438,30 +3470,29 @@ class rcube_imap /** - * Gets folder options from LIST response, e.g. \Noselect, \Noinferiors + * Gets folder attributes from LIST response, e.g. \Noselect, \Noinferiors * * @param string $mailbox Folder name - * @param bool $force Set to True if options should be refreshed - * Options are available after LIST command only + * @param bool $force Set to True if attributes should be refreshed * * @return array Options list */ - function mailbox_options($mailbox, $force=false) + function mailbox_attributes($mailbox, $force=false) { - if ($mailbox == 'INBOX') { - return array(); + // get attributes directly from LIST command + if (!empty($this->conn->data['LIST']) && is_array($this->conn->data['LIST'][$mailbox])) { + $opts = $this->conn->data['LIST'][$mailbox]; } - - if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mailbox])) { - if ($force) { - $this->conn->listMailboxes('', $mailbox); - } - else { - return array(); - } + // get cached folder attributes + else if (!$force) { + $opts = $this->get_cache('mailboxes.attributes'); + $opts = $opts[$mailbox]; } - $opts = $this->conn->data['LIST'][$mailbox]; + if (!is_array($opts)) { + $this->conn->listMailboxes('', $mailbox); + $opts = $this->conn->data['LIST'][$mailbox]; + } return is_array($opts) ? $opts : array(); } @@ -3544,17 +3575,17 @@ class rcube_imap } } - $options['name'] = $mailbox; - $options['options'] = $this->mailbox_options($mailbox, true); - $options['namespace'] = $this->mailbox_namespace($mailbox); - $options['rights'] = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array(); - $options['special'] = in_array($mailbox, $this->default_folders); + $options['name'] = $mailbox; + $options['attributes'] = $this->mailbox_attributes($mailbox, true); + $options['namespace'] = $this->mailbox_namespace($mailbox); + $options['rights'] = $acl && !$options['is_root'] ? (array)$this->my_rights($mailbox) : array(); + $options['special'] = in_array($mailbox, $this->default_folders); // Set 'noselect' and 'norename' flags - if (is_array($options['options'])) { - foreach ($options['options'] as $opt) { - $opt = strtolower($opt); - if ($opt == '\noselect' || $opt == '\nonexistent') { + if (is_array($options['attributes'])) { + foreach ($options['attributes'] as $attrib) { + $attrib = strtolower($attrib); + if ($attrib == '\noselect' || $attrib == '\nonexistent') { $options['noselect'] = true; } } diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php index 5c7a41c73..a4e921fe6 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -2242,12 +2242,29 @@ class rcube_imap_generic list($code, $response) = $this->execute($subscribed ? 'LSUB' : 'LIST', $args); if ($code == self::ERROR_OK) { - $folders = array(); - while ($this->tokenizeResponse($response, 1) == '*') { - $cmd = strtoupper($this->tokenizeResponse($response, 1)); + $folders = array(); + $last = 0; + $pos = 0; + $response .= "\r\n"; + + while ($pos = strpos($response, "\r\n", $pos+1)) { + // literal string, not real end-of-command-line + if ($response[$pos-1] == '}') { + continue; + } + + $line = substr($response, $last, $pos - $last); + $last = $pos + 2; + + if (!preg_match('/^\* (LIST|LSUB|STATUS) /i', $line, $m)) { + continue; + } + $cmd = strtoupper($m[1]); + $line = substr($line, strlen($m[0])); + // * LIST () if ($cmd == 'LIST' || $cmd == 'LSUB') { - list($opts, $delim, $mailbox) = $this->tokenizeResponse($response, 3); + list($opts, $delim, $mailbox) = $this->tokenizeResponse($line, 3); // Add to result array if (!$lstatus) { @@ -2258,31 +2275,21 @@ class rcube_imap_generic } // Add to options array - if (!empty($opts)) { - if (empty($this->data['LIST'][$mailbox])) - $this->data['LIST'][$mailbox] = $opts; - else - $this->data['LIST'][$mailbox] = array_unique(array_merge( - $this->data['LIST'][$mailbox], $opts)); - } + if (empty($this->data['LIST'][$mailbox])) + $this->data['LIST'][$mailbox] = $opts; + else if (!empty($opts)) + $this->data['LIST'][$mailbox] = array_unique(array_merge( + $this->data['LIST'][$mailbox], $opts)); } // * STATUS () else if ($cmd == 'STATUS') { - list($mailbox, $status) = $this->tokenizeResponse($response, 2); + list($mailbox, $status) = $this->tokenizeResponse($line, 2); for ($i=0, $len=count($status); $i<$len; $i += 2) { list($name, $value) = $this->tokenizeResponse($status, 2); $folders[$mailbox][$name] = $value; } } - // other untagged response line, skip it - else { - $response = ltrim($response); - if (($position = strpos($response, "\n")) !== false) - $response = substr($response, $position+1); - else - $response = ''; - } } return $folders; @@ -3392,15 +3399,10 @@ class rcube_imap_generic // String atom, number, NIL, *, % default: - // empty or one character - if ($str === '') { + // empty string + if ($str === '' || $str === null) { break 2; } - if (strlen($str) < 2) { - $result[] = $str; - $str = ''; - break; - } // excluded chars: SP, CTL, ), [, ] if (preg_match('/^([^\x00-\x20\x29\x5B\x5D\x7F]+)/', $str, $m)) { diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php index e20c8b430..00ee1c87b 100644 --- a/program/include/rcube_ldap.php +++ b/program/include/rcube_ldap.php @@ -1341,6 +1341,18 @@ class rcube_ldap extends rcube_addressbook } + /** + * Activate/deactivate debug mode + * + * @param boolean $dbg True if LDAP commands should be logged + * @access public + */ + function set_debug($dbg = true) + { + $this->debug = $dbg; + } + + /** * Quotes attribute value string * diff --git a/program/include/rcube_mdb2.php b/program/include/rcube_mdb2.php index b3976c37d..3b7a6129b 100644 --- a/program/include/rcube_mdb2.php +++ b/program/include/rcube_mdb2.php @@ -35,16 +35,16 @@ */ class rcube_mdb2 { - var $db_dsnw; // DSN for write operations - var $db_dsnr; // DSN for read operations - var $db_connected = false; // Already connected ? - var $db_mode = ''; // Connection mode - var $db_handle = 0; // Connection handle - var $db_error = false; - var $db_error_msg = ''; + public $db_dsnw; // DSN for write operations + public $db_dsnr; // DSN for read operations + public $db_connected = false; // Already connected ? + public $db_mode = ''; // Connection mode + public $db_handle = 0; // Connection handle + public $db_error = false; + public $db_error_msg = ''; private $debug_mode = false; - private $write_failure = false; + private $conn_failure = false; private $a_query_results = array('dummy'); private $last_res_id = 0; private $tables; @@ -58,7 +58,7 @@ class rcube_mdb2 */ function __construct($db_dsnw, $db_dsnr='', $pconn=false) { - if ($db_dsnr == '') + if (empty($db_dsnr)) $db_dsnr = $db_dsnw; $this->db_dsnw = $db_dsnw; @@ -122,30 +122,33 @@ class rcube_mdb2 */ function db_connect($mode) { + // previous connection failed, don't attempt to connect again + if ($this->conn_failure) { + return; + } + + // no replication + if ($this->db_dsnw == $this->db_dsnr) { + $mode = 'w'; + } + // Already connected if ($this->db_connected) { - // connected to read-write db, current connection is ok - if ($this->db_mode == 'w' && !$this->write_failure) - return; - - // no replication, current connection is ok for read and write - if (empty($this->db_dsnr) || $this->db_dsnw == $this->db_dsnr) { - $this->db_mode = 'w'; + // connected to db with the same or "higher" mode + if ($this->db_mode == 'w' || $this->db_mode == $mode) { return; } - - // Same mode, current connection is ok - if ($this->db_mode == $mode) - return; } $dsn = ($mode == 'r') ? $this->db_dsnr : $this->db_dsnw; - $this->db_handle = $this->dsn_connect($dsn); + $this->db_handle = $this->dsn_connect($dsn); $this->db_connected = !PEAR::isError($this->db_handle); if ($this->db_connected) - $this->db_mode = $mode; + $this->db_mode = $mode; + else + $this->conn_failure = true; } @@ -256,10 +259,6 @@ class rcube_mdb2 // Read or write ? $mode = (strtolower(substr(trim($query),0,6)) == 'select') ? 'r' : 'w'; - // don't event attempt to connect if previous write-operation failed - if ($this->write_failure && $mode == 'w') - return false; - $this->db_connect($mode); // check connection before proceeding @@ -284,7 +283,7 @@ class rcube_mdb2 raise_error(array('code' => 500, 'type' => 'db', 'line' => __LINE__, 'file' => __FILE__, 'message' => $this->db_error_msg), true, false); - + $result = false; } else { @@ -293,10 +292,6 @@ class rcube_mdb2 } } - // remember that write-operation failed - if ($mode == 'w' && ($result === false || PEAR::isError($result))) - $this->write_failure = true; - // add result, even if it's an error return $this->_add_result($result); } @@ -447,7 +442,7 @@ class rcube_mdb2 if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) { return $result; } - + return null; } @@ -530,7 +525,7 @@ class rcube_mdb2 */ function now() { - switch($this->db_provider) { + switch ($this->db_provider) { case 'mssql': case 'sqlsrv': return "getdate()"; diff --git a/program/include/rcube_string_replacer.php b/program/include/rcube_string_replacer.php index 5d743bfff..8997ca342 100644 --- a/program/include/rcube_string_replacer.php +++ b/program/include/rcube_string_replacer.php @@ -39,7 +39,7 @@ class rcube_string_replacer // Support unicode/punycode in top-level domain part $utf_domain = '[^?&@"\'\\/()\s\r\t\n]+\\.([^\\x00-\\x2f\\x3b-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,})'; $url1 = '.:;,'; - $url2 = 'a-z0-9%=#@+?&\\/_~\\[\\]-'; + $url2 = 'a-z0-9%=#@+?&\\/_~\\[\\]{}-'; $this->link_pattern = "/([\w]+:\/\/|\Wwww\.)($utf_domain([$url1]?[$url2]+)*)/i"; $this->mailto_pattern = "/(" diff --git a/program/js/app.js b/program/js/app.js index ce0f52f86..4ca19b7cd 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -20,7 +20,7 @@ function rcube_webmail() { - this.env = {}; + this.env = { recipients_separator:',', recipients_delimiter:', ' }; this.labels = {}; this.buttons = {}; this.buttons_sel = {}; @@ -2926,6 +2926,8 @@ function rcube_webmail() this.init_address_input_events = function(obj, props) { + this.env.recipients_delimiter = this.env.recipients_separator + ' '; + obj[bw.ie || bw.safari || bw.chrome ? 'keydown' : 'keypress'](function(e) { return ref.ksearch_keydown(e, this, props); }) .attr('autocomplete', 'off'); }; @@ -3590,13 +3592,13 @@ function rcube_webmail() // insert all members of a group if (typeof this.env.contacts[id] === 'object' && this.env.contacts[id].id) { - insert += this.env.contacts[id].name + ', '; + insert += this.env.contacts[id].name + this.env.recipients_delimiter; this.group2expand = $.extend({}, this.env.contacts[id]); this.group2expand.input = this.ksearch_input; this.http_request('mail/group-expand', '_source='+urlencode(this.env.contacts[id].source)+'&_gid='+urlencode(this.env.contacts[id].id), false); } else if (typeof this.env.contacts[id] === 'string') { - insert = this.env.contacts[id] + ', '; + insert = this.env.contacts[id] + this.env.recipients_delimiter; trigger = true; } @@ -3633,7 +3635,7 @@ function rcube_webmail() // get string from current cursor pos to last comma var cpos = this.get_caret_pos(this.ksearch_input), - p = inp_value.lastIndexOf(',', cpos-1), + p = inp_value.lastIndexOf(this.env.recipients_separator, cpos-1), q = inp_value.substring(p+1, cpos), min = this.env.autocomplete_min_length, ac = this.ksearch_data; diff --git a/program/steps/addressbook/func.inc b/program/steps/addressbook/func.inc index 2082dbd1f..2f1fbb70f 100644 --- a/program/steps/addressbook/func.inc +++ b/program/steps/addressbook/func.inc @@ -619,7 +619,7 @@ function rcmail_contact_form($form, $record, $attrib = null) $RCMAIL->output->set_env('month_names', $month_names); } $colprop['class'] .= ($colprop['class'] ? ' ' : '') . 'datepicker'; - $val = format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d')); + $val = format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d'), false); } $val = rcmail_get_edit_field($col, $val, $colprop, $colprop['type']); @@ -728,7 +728,7 @@ function rcmail_contact_photo($attrib) function rcmail_format_date_col($val) { global $RCMAIL; - return format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d')); + return format_date($val, $RCMAIL->config->get('date_format', 'Y-m-d'), false); } @@ -758,9 +758,12 @@ function rcmail_get_cids() foreach ($cid as $id) { // if _source is not specified we'll find it from decoded ID if (!$got_source) { - list ($c, $s) = explode('-', $id, 2); - if (strlen($s)) { - $result[(string)$s][] = $c; + if ($sep = strrpos($id, '-')) { + $contact_id = substr($id, 0, $sep); + $source_id = substr($id, $sep+1); + if (strlen($source_id)) { + $result[(string)$source_id][] = $contact_id; + } } } else { diff --git a/program/steps/mail/autocomplete.inc b/program/steps/mail/autocomplete.inc index 5b935ad2c..8b13f574d 100644 --- a/program/steps/mail/autocomplete.inc +++ b/program/steps/mail/autocomplete.inc @@ -32,7 +32,8 @@ if ($RCMAIL->action == 'group-expand') { $members[] = format_email_recipient($email, $sql_arr['name']); } - $OUTPUT->command('replace_group_recipients', $gid, join(', ', $members)); + $separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' '; + $OUTPUT->command('replace_group_recipients', $gid, join($separator, array_unique($members))); } $OUTPUT->send(); @@ -70,8 +71,8 @@ if (!empty($book_types) && strlen($search)) { if ($email_cnt > 1 && stripos($contact, $search) === false) { continue; } - // when we've got more than one book, we need to skip duplicates - if ($books_num == 1 || !in_array($contact, $contacts)) { + // skip duplicates + if (!in_array($contact, $contacts)) { $contacts[] = $contact; if (count($contacts) >= $MAXNUM) break 2; diff --git a/program/steps/mail/compose.inc b/program/steps/mail/compose.inc index c31ec9b2b..6961bf86a 100644 --- a/program/steps/mail/compose.inc +++ b/program/steps/mail/compose.inc @@ -5,7 +5,7 @@ | program/steps/mail/compose.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2009, The Roundcube Dev Team | + | Copyright (C) 2005-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -122,8 +122,9 @@ if (!empty($CONFIG['drafts_mbox'])) { } // set current mailbox in client environment $OUTPUT->set_env('mailbox', $IMAP->get_mailbox_name()); -$OUTPUT->set_env('sig_above', $CONFIG['sig_above']); -$OUTPUT->set_env('top_posting', $CONFIG['top_posting']); +$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('recipients_separator', trim($RCMAIL->config->get('recipients_separator', ','))); // get reference message and set compose mode if ($msg_uid = $_SESSION['compose']['param']['draft_uid']) { @@ -324,6 +325,7 @@ else if (count($MESSAGE->identities)) { // Set other headers $a_recipients = array(); $parts = array('to', 'cc', 'bcc', 'replyto', 'followupto'); +$separator = trim($RCMAIL->config->get('recipients_separator', ',')) . ' '; foreach ($parts as $header) { $fvalue = ''; @@ -367,7 +369,7 @@ foreach ($parts as $header) { if ($v = $MESSAGE->headers->to) $fvalue .= $v; if ($v = $MESSAGE->headers->cc) - $fvalue .= (!empty($fvalue) ? ', ' : '') . $v; + $fvalue .= (!empty($fvalue) ? $separator : '') . $v; } } else if (in_array($compose_mode, array(RCUBE_COMPOSE_DRAFT, RCUBE_COMPOSE_EDIT))) { @@ -410,7 +412,7 @@ foreach ($parts as $header) { } } - $fvalue = implode(', ', $fvalue); + $fvalue = implode($separator, $fvalue); } $MESSAGE->compose[$header] = $fvalue; diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc index 69724c554..39c25f19c 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -766,7 +766,7 @@ function rcmail_plain_body($body, $flowed=false) // previous line is flowed? if (isset($body[$last]) && $body[$n] - && $last != $last_sig + && $last !== $last_sig && $body[$last][strlen($body[$last])-1] == ' ' ) { $body[$last] .= $body[$n]; diff --git a/program/steps/mail/sendmail.inc b/program/steps/mail/sendmail.inc index 5022444a7..0fdcd78cd 100644 --- a/program/steps/mail/sendmail.inc +++ b/program/steps/mail/sendmail.inc @@ -5,7 +5,7 @@ | program/steps/mail/sendmail.inc | | | | This file is part of the Roundcube Webmail client | - | Copyright (C) 2005-2010, The Roundcube Dev Team | + | Copyright (C) 2005-2011, The Roundcube Dev Team | | Licensed under the GNU GPL | | | | PURPOSE: | @@ -138,22 +138,30 @@ function rcmail_fix_emoticon_paths(&$mime_message) return $body; } -// parse email address input (and count addresses) +/** + * Parse and cleanup email address input (and count addresses) + * + * @param string Address input + * @param boolean Do count recipients (saved in global $RECIPIENT_COUNT) + * @param boolean Validate addresses (errors saved in global $EMAIL_FORMAT_ERROR) + * @return string Canonical recipients string separated by comma + */ function rcmail_email_input_format($mailto, $count=false, $check=true) { - global $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT; + global $RCMAIL, $EMAIL_FORMAT_ERROR, $RECIPIENT_COUNT; // simplified email regexp, supporting quoted local part $email_regexp = '(\S+|("[^"]+"))@\S+'; - $regexp = array('/[,;]\s*[\r\n]+/', '/[\r\n]+/', '/[,;]\s*$/m', '/;/', '/(\S{1})(<'.$email_regexp.'>)/U'); - $replace = array(', ', ', ', '', ',', '\\1 \\2'); + $delim = trim($RCMAIL->config->get('recipients_separator', ',')); + $regexp = array("/[,;$delim]\s*[\r\n]+/", '/[\r\n]+/', "/[,;$delim]\s*\$/m", '/;/', '/(\S{1})(<'.$email_regexp.'>)/U'); + $replace = array($delim.' ', ', ', '', $delim, '\\1 \\2'); // replace new lines and strip ending ', ', make address input more valid $mailto = trim(preg_replace($regexp, $replace, $mailto)); $result = array(); - $items = rcube_explode_quoted_string(',', $mailto); + $items = rcube_explode_quoted_string($delim, $mailto); foreach($items as $item) { $item = trim($item); @@ -168,16 +176,9 @@ function rcmail_email_input_format($mailto, $count=false, $check=true) // address with name (handle name) } else if (preg_match('/<*'.$email_regexp.'>*$/', $item, $matches)) { $address = $matches[0]; - $name = str_replace($address, '', $item); - $name = trim($name); - if ($name && ($name[0] != '"' || $name[strlen($name)-1] != '"') - && preg_match('/[\(\)\<\>\\\.\[\]@,;:"]/', $name)) { - $name = '"'.addcslashes($name, '"').'"'; - } + $name = trim(str_replace($address, '', $item), '" '); $address = rcube_idn_to_ascii(trim($address, '<>')); - $address = '<' . $address . '>'; - - $result[] = $name.' '.$address; + $result[] = format_email_recipient($address, $name); $item = $address; } else if (trim($item)) { continue; diff --git a/program/steps/settings/edit_folder.inc b/program/steps/settings/edit_folder.inc index 36a4b28ba..e9566f7aa 100644 --- a/program/steps/settings/edit_folder.inc +++ b/program/steps/settings/edit_folder.inc @@ -119,7 +119,8 @@ function rcmail_folder_form($attrib) 'realnames' => false, 'maxlength' => 150, 'unsubscribed' => true, - 'exceptions' => array($mbox_imap), + 'skip_noinferiors' => true, + 'exceptions' => array($mbox_imap), )); $form['props']['fieldsets']['location']['content']['path'] = array( diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc index 206f62c60..77cbb5571 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -283,8 +283,8 @@ function rcube_subscription_form($attrib) } if (!$protected) { - $opts = $IMAP->mailbox_options($folder['id']); - $noselect = in_array('\\Noselect', $opts); + $attrs = $IMAP->mailbox_attributes($folder['id']); + $noselect = in_array('\\Noselect', $attrs); } $disabled = (($protected && $subscribed) || $noselect); -- cgit v1.2.3