- Add support for shared folders (#1403507)

release-0.6
alecpl 14 years ago
parent ec211b7592
commit 00290a6032

@ -65,6 +65,7 @@ CHANGELOG Roundcube Webmail
- Improve responsiveness of messages displaying (#1486986) - Improve responsiveness of messages displaying (#1486986)
- Add option for minimum length of autocomplete's string (#1486428) - Add option for minimum length of autocomplete's string (#1486428)
- Fix operations on messages in unsubscribed folder (#1487107) - Fix operations on messages in unsubscribed folder (#1487107)
- Add support for shared folders (#1403507)
RELEASE 0.4.2 RELEASE 0.4.2
------------- -------------

@ -74,11 +74,17 @@ $rcmail_config['default_port'] = 143;
// best server supported one) // best server supported one)
$rcmail_config['imap_auth_type'] = null; $rcmail_config['imap_auth_type'] = null;
// If you know your imap's root directory and its folder delimiter, // If you know your imap's folder delimiter, you can specify it here.
// you can specify them here. Otherwise they will be determined automatically. // Otherwise it will be determined automatically
$rcmail_config['imap_root'] = null;
$rcmail_config['imap_delimiter'] = null; $rcmail_config['imap_delimiter'] = null;
// If IMAP server doesn't support NAMESPACE extension, but you're
// using shared folders or personal root folder is non-empty, you'll need to
// set these options. All can be strings or arrays of strings.
$rcmail_config['imap_ns_personal'] = null;
$rcmail_config['imap_ns_other'] = null;
$rcmail_config['imap_ns_shared'] = null;
// By default IMAP capabilities are readed after connection to IMAP server // By default IMAP capabilities are readed after connection to IMAP server
// In some cases, e.g. when using IMAP proxy, there's a need to refresh the list // In some cases, e.g. when using IMAP proxy, there's a need to refresh the list
// after login. Set to True if you've got this case. // after login. Set to True if you've got this case.

@ -38,6 +38,7 @@ class rcube_install
'locale_string' => 'language', 'locale_string' => 'language',
'multiple_identities' => 'identities_level', 'multiple_identities' => 'identities_level',
'addrbook_show_images' => 'show_images', 'addrbook_show_images' => 'show_images',
'imap_root' => 'imap_ns_personal',
); );
// these config options are required for a working system // these config options are required for a working system
@ -169,16 +170,17 @@ class rcube_install
else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) { else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
$value = '%p'; $value = '%p';
} }
else if ($prop == 'default_imap_folders'){ else if ($prop == 'default_imap_folders') {
$value = Array(); $value = Array();
foreach($this->config['default_imap_folders'] as $_folder){ foreach ($this->config['default_imap_folders'] as $_folder) {
switch($_folder) { switch($_folder) {
case 'Drafts': $_folder = $this->config['drafts_mbox']; break; case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
case 'Sent': $_folder = $this->config['sent_mbox']; break; case 'Sent': $_folder = $this->config['sent_mbox']; break;
case 'Junk': $_folder = $this->config['junk_mbox']; break; case 'Junk': $_folder = $this->config['junk_mbox']; break;
case 'Trash': $_folder = $this->config['trash_mbox']; break; case 'Trash': $_folder = $this->config['trash_mbox']; break;
} }
if (!in_array($_folder, $value)) $value[] = $_folder; if (!in_array($_folder, $value))
$value[] = $_folder;
} }
} }
else if (is_bool($default)) { else if (is_bool($default)) {

@ -503,8 +503,6 @@ class rcmail
'auth_method' => $this->config->get('imap_auth_type', 'check'), 'auth_method' => $this->config->get('imap_auth_type', 'check'),
'auth_cid' => $this->config->get('imap_auth_cid'), 'auth_cid' => $this->config->get('imap_auth_cid'),
'auth_pw' => $this->config->get('imap_auth_pw'), 'auth_pw' => $this->config->get('imap_auth_pw'),
'delimiter' => isset($_SESSION['imap_delimiter']) ? $_SESSION['imap_delimiter'] : $this->config->get('imap_delimiter'),
'rootdir' => isset($_SESSION['imap_root']) ? $_SESSION['imap_root'] : $this->config->get('imap_root'),
'debug_mode' => (bool) $this->config->get('imap_debug', 0), 'debug_mode' => (bool) $this->config->get('imap_debug', 0),
'force_caps' => (bool) $this->config->get('imap_force_caps'), 'force_caps' => (bool) $this->config->get('imap_force_caps'),
'timeout' => (int) $this->config->get('imap_timeout', 0), 'timeout' => (int) $this->config->get('imap_timeout', 0),
@ -790,10 +788,6 @@ class rcmail
if (isset($_SESSION['page'])) { if (isset($_SESSION['page'])) {
$this->imap->set_page($_SESSION['page']); $this->imap->set_page($_SESSION['page']);
} }
// cache IMAP root and delimiter in session for performance reasons
$_SESSION['imap_root'] = $this->imap->root_dir;
$_SESSION['imap_delimiter'] = $this->imap->delimiter;
} }

@ -33,10 +33,8 @@ class rcube_imap
{ {
public $debug_level = 1; public $debug_level = 1;
public $skip_deleted = false; public $skip_deleted = false;
public $root_dir = '';
public $page_size = 10; public $page_size = 10;
public $list_page = 1; public $list_page = 1;
public $delimiter = NULL;
public $threading = false; public $threading = false;
public $fetch_add_headers = ''; public $fetch_add_headers = '';
public $get_all_headers = false; public $get_all_headers = false;
@ -54,8 +52,9 @@ class rcube_imap
* @var rcube_mdb2 * @var rcube_mdb2
*/ */
private $db; private $db;
private $root_ns = '';
private $mailbox = 'INBOX'; private $mailbox = 'INBOX';
private $delimiter = NULL;
private $namespace = NULL;
private $sort_field = ''; private $sort_field = '';
private $sort_order = 'DESC'; private $sort_order = 'DESC';
private $caching_enabled = false; private $caching_enabled = false;
@ -156,18 +155,13 @@ class rcube_imap
$this->port = $port; $this->port = $port;
$this->ssl = $use_ssl; $this->ssl = $use_ssl;
// print trace messages
if ($this->conn->connected()) { if ($this->conn->connected()) {
// print trace messages
if ($this->conn->message && ($this->debug_level & 8)) { if ($this->conn->message && ($this->debug_level & 8)) {
console($this->conn->message); console($this->conn->message);
} }
// get namespace and delimiter
// get server properties $this->set_env();
$rootdir = $this->conn->getRootDir();
if (!empty($rootdir))
$this->set_rootdir($rootdir);
if (empty($this->delimiter))
$this->get_hierarchy_delimiter();
return true; return true;
} }
@ -246,28 +240,6 @@ class rcube_imap
} }
/**
* Set a root folder for the IMAP connection.
*
* Only folders within this root folder will be displayed
* and all folder paths will be translated using this folder name
*
* @param string $root Root folder
* @access public
*/
function set_rootdir($root)
{
if (preg_match('/[.\/]$/', $root)) //(substr($root, -1, 1)==='/')
$root = substr($root, 0, -1);
$this->root_dir = $root;
$this->options['rootdir'] = $root;
if (empty($this->delimiter))
$this->get_hierarchy_delimiter();
}
/** /**
* Set default message charset * Set default message charset
* *
@ -482,13 +454,101 @@ class rcube_imap
*/ */
function get_hierarchy_delimiter() function get_hierarchy_delimiter()
{ {
if ($this->conn && empty($this->delimiter)) return $this->delimiter;
$this->delimiter = $this->conn->getHierarchyDelimiter(); }
/**
* Get namespace
*
* @return array Namespace data
* @access public
*/
function get_namespace()
{
return $this->namespace;
}
/**
* Sets delimiter and namespaces
*
* @access private
*/
private function set_env()
{
if ($this->delimiter !== null && $this->namespace !== null) {
return;
}
if (isset($_SESSION['imap_namespace']) && isset($_SESSION['imap_delimiter'])) {
$this->namespace = $_SESSION['imap_namespace'];
$this->delimiter = $_SESSION['imap_delimiter'];
return;
}
$config = rcmail::get_instance()->config;
$imap_personal = $config->get('imap_ns_personal');
$imap_other = $config->get('imap_ns_other');
$imap_shared = $config->get('imap_ns_shared');
$imap_delimiter = $config->get('imap_delimiter');
if ($imap_delimiter) {
$this->delimiter = $imap_delimiter;
}
if (!$this->conn)
return;
$ns = $this->conn->getNamespace();
// NAMESPACE supported
if (is_array($ns)) {
$this->namespace = $ns;
if (empty($this->delimiter))
$this->delimiter = $ns['personal'][0][1];
if (empty($this->delimiter))
$this->delimiter = $this->conn->getHierarchyDelimiter();
if (empty($this->delimiter))
$this->delimiter = '/';
}
// not supported, get namespace from config
else if ($imap_personal !== null || $imap_shared !== null || $imap_other !== null) {
if (empty($this->delimiter))
$this->delimiter = $this->conn->getHierarchyDelimiter();
if (empty($this->delimiter)) if (empty($this->delimiter))
$this->delimiter = '/'; $this->delimiter = '/';
return $this->delimiter; $this->namespace = array(
'personal' => NULL,
'other' => NULL,
'shared' => NULL,
);
if ($imap_personal !== null) {
foreach ((array)$imap_personal as $dir) {
$this->namespace['personal'][] = array($dir, $this->delimiter);
}
}
if ($imap_other !== null) {
foreach ((array)$imap_other as $dir) {
if ($dir) {
$this->namespace['other'][] = array($dir, $this->delimiter);
}
}
}
if ($imap_shared !== null) {
foreach ((array)$imap_shared as $dir) {
if ($dir) {
$this->namespace['shared'][] = array($dir, $this->delimiter);
}
}
}
}
$_SESSION['imap_namespace'] = $this->namespace;
$_SESSION['imap_delimiter'] = $this->delimiter;
} }
@ -891,7 +951,7 @@ class rcube_imap
// flatten threads array // flatten threads array
// @TODO: fetch children only in expanded mode (?) // @TODO: fetch children only in expanded mode (?)
$all_ids = array(); $all_ids = array();
foreach($msg_index as $root) { foreach ($msg_index as $root) {
$all_ids[] = $root; $all_ids[] = $root;
if (!empty($thread_tree[$root])) if (!empty($thread_tree[$root]))
$all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root])); $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root]));
@ -1463,7 +1523,7 @@ class rcube_imap
// flatten threads array // flatten threads array
$all_ids = array(); $all_ids = array();
foreach($msg_index as $root) { foreach ($msg_index as $root) {
$all_ids[] = $root; $all_ids[] = $root;
if (!empty($thread_tree[$root])) { if (!empty($thread_tree[$root])) {
foreach (array_keys_recursive($thread_tree[$root]) as $val) foreach (array_keys_recursive($thread_tree[$root]) as $val)
@ -3073,10 +3133,11 @@ class rcube_imap
$a_mboxes = explode(',', $mbox_name); $a_mboxes = explode(',', $mbox_name);
if (is_array($a_mboxes)) { if (is_array($a_mboxes)) {
$delimiter = $this->get_hierarchy_delimiter();
foreach ($a_mboxes as $mbox_name) { foreach ($a_mboxes as $mbox_name) {
$mailbox = $this->mod_mailbox($mbox_name); $mailbox = $this->mod_mailbox($mbox_name);
$sub_mboxes = $this->conn->listMailboxes($this->mod_mailbox(''), $sub_mboxes = $this->conn->listMailboxes('', $mbox_name . $delimiter . '*');
$mbox_name . $this->delimiter . '*');
// unsubscribe mailbox before deleting // unsubscribe mailbox before deleting
$this->conn->unsubscribe($mailbox); $this->conn->unsubscribe($mailbox);
@ -3138,18 +3199,19 @@ class rcube_imap
return true; return true;
$key = $subscription ? 'subscribed' : 'existing'; $key = $subscription ? 'subscribed' : 'existing';
if (is_array($this->icache[$key]) && in_array($mbox_name, $this->icache[$key])) $mbox = $this->mod_mailbox($mbox_name)
if (is_array($this->icache[$key]) && in_array($mbox, $this->icache[$key]))
return true; return true;
if ($subscription) { if ($subscription) {
$a_folders = $this->conn->listSubscribed($this->mod_mailbox(''), $mbox_name); $a_folders = $this->conn->listSubscribed('', $mbox);
} }
else { else {
$a_folders = $this->conn->listMailboxes($this->mod_mailbox(''), $mbox_name); $a_folders = $this->conn->listMailboxes('', $mbox);
} }
if (is_array($a_folders) && in_array($this->mod_mailbox($mbox_name), $a_folders)) { if (is_array($a_folders) && in_array($mbox, $a_folders)) {
$this->icache[$key][] = $mbox_name; $this->icache[$key][] = $mbox;
return true; return true;
} }
} }
@ -3167,14 +3229,52 @@ class rcube_imap
*/ */
function mod_mailbox($mbox_name, $mode='in') function mod_mailbox($mbox_name, $mode='in')
{ {
if ($mbox_name == 'INBOX') if (empty($mbox_name))
return $mbox_name; return '';
if (!empty($this->root_dir)) { if ($mode == 'in') {
if ($mode=='in') // If folder contains namespace prefix, don't modify it
$mbox_name = $this->root_dir.$this->delimiter.$mbox_name; if (is_array($this->namespace['shared'])) {
else if (!empty($mbox_name)) // $mode=='out' foreach ($this->namespace['shared'] as $ns) {
$mbox_name = substr($mbox_name, strlen($this->root_dir)+1); foreach ((array)$ns as $root) {
if (strpos($mbox_name, $root[0]) === 0) {
return $mbox_name;
}
}
}
}
if (is_array($this->namespace['other'])) {
foreach ($this->namespace['other'] as $ns) {
foreach ((array)$ns as $root) {
if (strpos($mbox_name, $root[0]) === 0) {
return $mbox_name;
}
}
}
}
if (is_array($this->namespace['personal'])) {
foreach ($this->namespace['personal'] as $ns) {
foreach ((array)$ns as $root) {
if ($root[0] && strpos($mbox_name, $root[0]) === 0) {
return $mbox_name;
}
}
}
// Add prefix if first personal namespace is non-empty
if ($this->namespace['personal'][0][0]) {
return $this->namespace['personal'][0][0].$mbox_name;
}
}
}
else {
// Remove prefix if folder is from first ("non-empty") personal namespace
if (is_array($this->namespace['personal'])) {
if ($prefix = $this->namespace['personal'][0][0]) {
if (strpos($mbox_name, $prefix) === 0) {
return substr($mbox_name, strlen($prefix));
}
}
}
} }
return $mbox_name; return $mbox_name;
@ -3200,7 +3300,7 @@ class rcube_imap
if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mbox])) { if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mbox])) {
if ($force) { if ($force) {
$this->conn->listMailboxes($this->mod_mailbox(''), $mbox_name); $this->conn->listMailboxes('', $mbox_name);
} }
else { else {
return array(); return array();

@ -549,54 +549,15 @@ class rcube_imap_generic
} }
/** /**
* Gets the root directory and delimiter (of personal namespace) * Gets the delimiter
* *
* @return mixed A root directory name, or false. * @return string The delimiter
*/
function getRootDir()
{
if (isset($this->prefs['rootdir']) && is_string($this->prefs['rootdir'])) {
return $this->prefs['rootdir'];
}
if (!is_array($data = $this->getNamespace())) {
return false;
}
$user_space_data = $data['personal'];
if (!is_array($user_space_data)) {
return false;
}
$first_userspace = $user_space_data[0];
if (count($first_userspace) !=2) {
return false;
}
$rootdir = $first_userspace[0];
$this->prefs['delimiter'] = $first_userspace[1];
$this->prefs['rootdir'] = $rootdir ? substr($rootdir, 0, -1) : '';
return $this->prefs['rootdir'];
}
/**
* Gets the delimiter, for example:
* INBOX.foo -> .
* INBOX/foo -> /
* INBOX\foo -> \
*
* @return mixed A delimiter (string), or false.
* @see connect()
*/ */
function getHierarchyDelimiter() function getHierarchyDelimiter()
{ {
if ($this->prefs['delimiter']) { if ($this->prefs['delimiter']) {
return $this->prefs['delimiter']; return $this->prefs['delimiter'];
} }
if (!empty($this->prefs['delimiter'])) {
return $this->prefs['delimiter'];
}
// try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8) // try (LIST "" ""), should return delimiter (RFC2060 Sec 6.3.8)
list($code, $response) = $this->execute('LIST', list($code, $response) = $this->execute('LIST',
@ -611,26 +572,7 @@ class rcube_imap_generic
} }
} }
// if that fails, try namespace extension return NULL;
// try to fetch namespace data
if (!is_array($data = $this->getNamespace())) {
return false;
}
// extract user space data (opposed to global/shared space)
$user_space_data = $data['personal'];
if (!is_array($user_space_data)) {
return false;
}
// get first element
$first_userspace = $user_space_data[0];
if (!is_array($first_userspace)) {
return false;
}
// extract delimiter
return $this->prefs['delimiter'] = $first_userspace[1];
} }
/** /**
@ -830,7 +772,6 @@ class rcube_imap_generic
if ($this->prefs['force_caps']) { if ($this->prefs['force_caps']) {
$this->clearCapability(); $this->clearCapability();
} }
$this->getRootDir();
$this->logged = true; $this->logged = true;
return true; return true;
@ -1943,10 +1884,6 @@ class rcube_imap_generic
$mailbox = '*'; $mailbox = '*';
} }
if (empty($ref) && $this->prefs['rootdir']) {
$ref = $this->prefs['rootdir'];
}
$args = array(); $args = array();
if (!empty($select_opts) && $this->getCapability('LIST-EXTENDED')) { if (!empty($select_opts) && $this->getCapability('LIST-EXTENDED')) {

Loading…
Cancel
Save