From 51f7a5b2a09777d3a279757af620e42985ff9a86 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Wed, 09 Nov 2011 05:03:54 -0500
Subject: [PATCH] - Apply fixes from trunk up to r5401

---
 CHANGELOG                                 |    6 
 program/steps/settings/folders.inc        |    4 
 program/include/main.inc                  |   88 +++++++-----
 program/include/rcube_ldap.php            |   12 +
 program/include/rcube_string_replacer.php |    2 
 program/steps/mail/compose.inc            |   12 +
 program/steps/addressbook/func.inc        |   13 +
 config/main.inc.php.dist                  |    5 
 program/steps/mail/sendmail.inc           |   31 ++--
 program/include/rcube_mdb2.php            |   61 ++++----
 program/steps/settings/edit_folder.inc    |    3 
 program/include/rcube_imap.php            |   83 ++++++++---
 program/steps/mail/autocomplete.inc       |    7 
 program/include/rcmail.php                |    1 
 program/steps/mail/func.inc               |    2 
 program/include/rcube_imap_generic.php    |   56 ++++---
 program/js/app.js                         |   10 
 program/include/rcube_contacts.php        |    2 
 18 files changed, 236 insertions(+), 162 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index fb3bb4e..2b5e709 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 896fcbe..9493b30 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 @@
 // 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 3980794..95d422d 100644
--- a/program/include/main.inc
+++ b/program/include/main.inc
@@ -1018,15 +1018,15 @@
  * 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 @@
   if (empty($ts))
     return '';
 
-  // get user's timezone offset
-  $tz = $RCMAIL->config->get_timezone();
+  if ($convert) {
+    // get user's timezone offset
+    $tz = $RCMAIL->config->get_timezone();
 
-  // convert time to user's timezone
-  $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
+    // convert time to user's timezone
+    $timestamp = $ts - date('Z', $ts) + ($tz * 3600);
 
-  // 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);
-
-  $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 @@
   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 @@
   $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 @@
  * @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('&nbsp;', $nestLevel*4) . $foldername, $folder['id']);
-    }
-    else if ($nestLevel)
+    // skip exceptions (and its subfolders)
+    if (!empty($opts['exceptions']) && in_array($folder['id'], $opts['exceptions'])) {
       continue;
+    }
+
+    // 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('&nbsp;', $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 1ecdfcd..969e101 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -594,7 +594,6 @@
       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 a2eeffc..e822d2c 100644
--- a/program/include/rcube_contacts.php
+++ b/program/include/rcube_contacts.php
@@ -41,7 +41,6 @@
     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 @@
 
     // 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 d2f9547..7508acd 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;
@@ -314,6 +313,19 @@
     function set_options($opt)
     {
         $this->options = array_merge($this->options, (array)$opt);
+    }
+
+
+    /**
+     * 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'));
     }
 
 
@@ -3075,7 +3087,19 @@
      */
     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 @@
             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);
@@ -3106,6 +3135,9 @@
         if (!$skip_sort) {
             $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 @@
 
 
     /**
-     * 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];
+        }
+        // get cached folder attributes
+        else if (!$force) {
+            $opts = $this->get_cache('mailboxes.attributes');
+            $opts = $opts[$mailbox];
         }
 
-        if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mailbox])) {
-            if ($force) {
-                $this->conn->listMailboxes('', $mailbox);
-            }
-            else {
-                return array();
-            }
+        if (!is_array($opts)) {
+            $this->conn->listMailboxes('', $mailbox);
+            $opts = $this->conn->data['LIST'][$mailbox];
         }
-
-        $opts = $this->conn->data['LIST'][$mailbox];
 
         return is_array($opts) ? $opts : array();
     }
@@ -3544,17 +3575,17 @@
             }
         }
 
-        $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 5c7a41c..a4e921f 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -2242,12 +2242,29 @@
         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 (<options>) <delimiter> <mailbox>
                 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,30 +2275,20 @@
                     }
 
                     // 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 <mailbox> (<result>)
                 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 = '';
                 }
             }
 
@@ -3392,14 +3399,9 @@
 
             // 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, ), [, ]
diff --git a/program/include/rcube_ldap.php b/program/include/rcube_ldap.php
index e20c8b4..00ee1c8 100644
--- a/program/include/rcube_ldap.php
+++ b/program/include/rcube_ldap.php
@@ -1342,6 +1342,18 @@
 
 
     /**
+     * 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
      *
      * @param string $str Attribute value
diff --git a/program/include/rcube_mdb2.php b/program/include/rcube_mdb2.php
index b3976c3..3b7a612 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 @@
      */
     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 @@
      */
     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 @@
         // 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 @@
                 raise_error(array('code' => 500, 'type' => 'db',
                     'line' => __LINE__, 'file' => __FILE__,
                     'message' => $this->db_error_msg), true, false);
-                
+
                 $result = false;
             }
             else {
@@ -292,10 +291,6 @@
                 $q->free();
             }
         }
-
-        // 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 @@
         if (!PEAR::isError($result = $this->db_handle->listTableFields($table))) {
             return $result;
         }
-        
+
         return null;
     }
 
@@ -530,7 +525,7 @@
      */
     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 5d743bf..8997ca3 100644
--- a/program/include/rcube_string_replacer.php
+++ b/program/include/rcube_string_replacer.php
@@ -39,7 +39,7 @@
     // 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 ce0f52f..4ca19b7 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 @@
 
   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 @@
 
     // 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 @@
 
     // 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 2082dbd..2f1fbb7 100644
--- a/program/steps/addressbook/func.inc
+++ b/program/steps/addressbook/func.inc
@@ -619,7 +619,7 @@
                                 $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_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 @@
     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 5b935ad..8b13f57 100644
--- a/program/steps/mail/autocomplete.inc
+++ b/program/steps/mail/autocomplete.inc
@@ -32,7 +32,8 @@
         $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 ($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 c31ec9b..6961bf8 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 @@
 }
 // 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 @@
 // 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 @@
       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 @@
       }
     }
 
-    $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 69724c5..39c25f1 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -766,7 +766,7 @@
 
           // 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 5022444..0fdcd78 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 @@
   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 @@
     // 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 36a4b28..e9566f7 100644
--- a/program/steps/settings/edit_folder.inc
+++ b/program/steps/settings/edit_folder.inc
@@ -119,7 +119,8 @@
             '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 206f62c..77cbb55 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -283,8 +283,8 @@
         }
 
         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);

--
Gitblit v1.9.1