Support for IMAP folders that cannot contain both folders and messages (#5057)

pull/5859/head
Aleksander Machniak 7 years ago
parent cfed954a46
commit 76adb49454

@ -1,6 +1,7 @@
CHANGELOG Roundcube Webmail
===========================
- Support for IMAP folders that cannot contain both folders and messages (#5057)
- Added .user.ini file for php-fpm (#5846)
- Email Resent (Bounce) feature (#4985)
- Various improvements for templating engine and skin behaviours

@ -207,6 +207,11 @@ $config['imap_force_ns'] = false;
// Enable this option to hide them and disable possibility to create such.
$config['imap_skip_hidden_folders'] = false;
// Some servers do not support folders with both folders and messages inside
// If your server supports that use true, if it does not, use false.
// By default it will be determined automatically (once per user session).
$config['imap_dual_use_folders'] = null;
// List of disabled imap extensions.
// Use if your IMAP server has broken implementation of some feature
// and you can't remove it from CAPABILITY string on server-side.

@ -404,23 +404,27 @@ class archive extends rcube_plugin
'content' => $select->show($mbox, array('id' => '_archive_mbox', 'name' => '_archive_mbox'))
);
// add option for structuring the archive folder
$archive_type = new html_select(array('name' => '_archive_type', 'id' => 'ff_archive_type'));
$archive_type->add($this->gettext('none'), '');
$archive_type->add($this->gettext('archivetypeyear'), 'year');
$archive_type->add($this->gettext('archivetypemonth'), 'month');
$archive_type->add($this->gettext('archivetypetbmonth'), 'tbmonth');
$archive_type->add($this->gettext('archivetypesender'), 'sender');
$archive_type->add($this->gettext('archivetypefolder'), 'folder');
$args['blocks']['archive'] = array(
'name' => rcube::Q($this->gettext('settingstitle')),
'options' => array('archive_type' => array(
'title' => html::label('ff_archive_type', rcube::Q($this->gettext('archivetype'))),
'content' => $archive_type->show($type)
// If the server supports only either messages or folders in a folder
// we do not allow archive splitting, for simplicity (#5057)
if ($rcmail->get_storage()->get_capability(rcube_storage::DUAL_USE_FOLDERS)) {
// add option for structuring the archive folder
$archive_type = new html_select(array('name' => '_archive_type', 'id' => 'ff_archive_type'));
$archive_type->add($this->gettext('none'), '');
$archive_type->add($this->gettext('archivetypeyear'), 'year');
$archive_type->add($this->gettext('archivetypemonth'), 'month');
$archive_type->add($this->gettext('archivetypetbmonth'), 'tbmonth');
$archive_type->add($this->gettext('archivetypesender'), 'sender');
$archive_type->add($this->gettext('archivetypefolder'), 'folder');
$args['blocks']['archive'] = array(
'name' => rcube::Q($this->gettext('settingstitle')),
'options' => array('archive_type' => array(
'title' => html::label('ff_archive_type', rcube::Q($this->gettext('archivetype'))),
'content' => $archive_type->show($type)
)
)
)
);
);
}
}
else if ($args['section'] == 'server' && !in_array('read_on_archive', $dont_override)) {
$chbox = new html_checkbox(array('name' => '_read_on_archive', 'id' => 'ff_read_on_archive', 'value' => 1));

@ -380,7 +380,12 @@ class rcube_imap extends rcube_storage
return false;
}
$_SESSION[$sess_key] = $this->conn->getCapability($cap);
if ($cap == rcube_storage::DUAL_USE_FOLDERS) {
$_SESSION[$sess_key] = $this->detect_dual_use_folders();
}
else {
$_SESSION[$sess_key] = $this->conn->getCapability($cap);
}
}
return $_SESSION[$sess_key];
@ -3209,15 +3214,22 @@ class rcube_imap extends rcube_storage
* @param string $folder New folder name
* @param boolean $subscribe True if the new folder should be subscribed
* @param string $type Optional folder type (junk, trash, drafts, sent, archive)
* @param boolean $noselect Make the folder a \NoSelect folder by adding hierarchy
* separator at the end (useful for server that do not support
* both folders and messages as folder children)
*
* @return boolean True on success
*/
public function create_folder($folder, $subscribe = false, $type = null)
public function create_folder($folder, $subscribe = false, $type = null, $noselect = false)
{
if (!$this->check_connection()) {
return false;
}
if ($noselect) {
$folder .= $this->delimiter;
}
$result = $this->conn->createFolder($folder, $type ? array("\\" . ucfirst($type)) : null);
// try to subscribe it
@ -3225,7 +3237,7 @@ class rcube_imap extends rcube_storage
// clear cache
$this->clear_cache('mailboxes', true);
if ($subscribe) {
if ($subscribe && !$noselect) {
$this->subscribe($folder);
}
}
@ -3299,7 +3311,7 @@ class rcube_imap extends rcube_storage
*
* @return boolean True on success, False on failure
*/
function delete_folder($folder)
public function delete_folder($folder)
{
if (!$this->check_connection()) {
return false;
@ -4176,6 +4188,38 @@ class rcube_imap extends rcube_storage
* protected methods
* --------------------------------*/
/**
* Determines if server supports dual use folders (those can
* contain both sub-folders and messages).
*
* @return bool
*/
protected function detect_dual_use_folders()
{
$val = rcube::get_instance()->config->get('imap_dual_use_folders');
if ($val !== null) {
return (bool) $val;
}
if (!$this->check_connection()) {
return false;
}
$folder = str_replace('.', '', 'foldertest' . microtime(true));
$folder = $this->mod_folder($folder, 'in');
$subfolder = $folder . $this->delimiter . 'foldertest';
if ($this->conn->createFolder($folder)) {
if ($created = $this->conn->createFolder($subfolder)) {
$this->conn->deleteFolder($subfolder);
}
$this->conn->deleteFolder($folder);
return $created;
}
}
/**
* Validate the given input and save to local properties
*

@ -1118,16 +1118,7 @@ class rcube_imap_generic
if ($this->selected === $mailbox) {
return true;
}
/*
Temporary commented out because Courier returns \Noselect for INBOX
Requires more investigation
if (is_array($this->data['LIST']) && is_array($opts = $this->data['LIST'][$mailbox])) {
if (in_array('\\Noselect', $opts)) {
return false;
}
}
*/
$params = array($this->escape($mailbox));
// QRESYNC data items

@ -80,6 +80,8 @@ abstract class rcube_storage
const NONEXISTENT = 7;
const CONTACTADMIN = 8;
const DUAL_USE_FOLDERS = 'X-DUAL-USE-FOLDERS';
/**
* Connect to the server
@ -660,10 +662,14 @@ abstract class rcube_storage
*
* @param string $folder New folder name
* @param boolean $subscribe True if the newvfolder should be subscribed
* @param string $type Optional folder type (junk, trash, drafts, sent, archive)
* @param boolean $noselect Make the folder \NoSelect folder by adding hierarchy
* separator at the end (useful for server that do not support
* both folders and messages as folder children)
*
* @return boolean True on success, False on error
*/
abstract function create_folder($folder, $subscribe = false);
abstract function create_folder($folder, $subscribe = false, $type = null, $noselect = false);
/**
* Set a new name to an existing folder

@ -596,6 +596,9 @@ $labels['findmail'] = 'Find mail messages';
$labels['namespace.personal'] = 'Personal';
$labels['namespace.other'] = 'Other users';
$labels['namespace.shared'] = 'Shared';
$labels['dualuselabel'] = 'Can contain only';
$labels['dualusemail'] = 'messages';
$labels['dualusefolder'] = 'folders';
$labels['sortby'] = 'Sort by';
$labels['sortasc'] = 'Sort ascending';

@ -44,7 +44,8 @@ function rcmail_folder_form($attrib)
$parent = rcube_utils::get_input_value('_path', rcube_utils::INPUT_GPC, true);
$threading_supported = $storage->get_capability('THREAD');
$delimiter = $storage->get_hierarchy_delimiter();
$dual_use_supported = $storage->get_capability(rcube_storage::DUAL_USE_FOLDERS);
$delimiter = $storage->get_hierarchy_delimiter();
// Get mailbox parameters
if (strlen($mbox)) {
@ -152,6 +153,27 @@ function rcmail_folder_form($attrib)
'name' => $RCMAIL->gettext('settings'),
);
// For servers that do not support both sub-folders and messages in a folder
if (!$dual_use_supported) {
if (!strlen($mbox)) {
$select = new html_select(array('name' => '_type', 'id' => '_type'));
$select->add($RCMAIL->gettext('dualusemail'), 'mail');
$select->add($RCMAIL->gettext('dualusefolder'), 'folder');
$value = rcube_utils::get_input_value('_type', rcube_utils::INPUT_POST);
$value = $select->show($value ?: 'mail');
}
else {
$value = $options['noselect'] ? 'folder' : 'mail';
$value = $RCMAIL->gettext('dualuse' . $value);
}
$form['props']['fieldsets']['settings']['content']['type'] = array(
'label' => $RCMAIL->gettext('dualuselabel'),
'value' => $value,
);
}
// Settings: threading
if ($threading_supported && ($mbox == 'INBOX' || (!$options['noselect'] && !$options['is_root']))) {
$select = new html_select(array('name' => '_viewmode', 'id' => '_viewmode'));

@ -1357,25 +1357,24 @@ function rcmail_folder_options($mailbox)
*/
function rcmail_update_folder_row($name, $oldname=null, $subscribe=false, $class_name=null)
{
global $RCMAIL, $OUTPUT;
$protect_folders = $RCMAIL->config->get('protect_default_folders');
$storage = $RCMAIL->get_storage();
$delimiter = $storage->get_hierarchy_delimiter();
global $RCMAIL;
$storage = $RCMAIL->get_storage();
$delimiter = $storage->get_hierarchy_delimiter();
$options = rcmail_folder_options($name);
$name_utf8 = rcube_charset::convert($name, 'UTF7-IMAP');
$protected = $protect_folders && $storage->is_special_folder($name);
$foldersplit = explode($delimiter, $storage->mod_folder($name));
$level = count($foldersplit) - 1;
$display_name = $protected ? $RCMAIL->localize_foldername($name) : rcube_charset::convert($foldersplit[$level], 'UTF7-IMAP');
$display_name = $options['protected'] ? $RCMAIL->localize_foldername($name) : rcube_charset::convert($foldersplit[$level], 'UTF7-IMAP');
$class_name = trim($class_name . ' mailbox');
if ($oldname === null) {
$OUTPUT->command('add_folder_row', $name, $name_utf8, $display_name, $protected, $subscribe,
$class_name);
$RCMAIL->output->command('add_folder_row', $name, $name_utf8, $display_name,
$options['protected'] || $options['noselect'], $subscribe, $class_name);
}
else {
$OUTPUT->command('replace_folder_row', $oldname, $name, $name_utf8, $display_name, $protected, $class_name);
$RCMAIL->output->command('replace_folder_row', $oldname, $name, $name_utf8, $display_name,
$options['protected'] || $options['noselect'], $class_name);
}
}

@ -27,6 +27,7 @@ $STORAGE = $RCMAIL->get_storage();
$name = trim(rcube_utils::get_input_value('_name', rcube_utils::INPUT_POST, true));
$path = rcube_utils::get_input_value('_parent', rcube_utils::INPUT_POST, true);
$old_imap = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
$type = rcube_utils::get_input_value('_type', rcube_utils::INPUT_POST);
$name_imap = rcube_charset::convert($name, RCUBE_CHARSET, 'UTF7-IMAP');
// $path is in UTF7-IMAP already
@ -70,10 +71,11 @@ else {
}
}
$dual_use_supported = $STORAGE->get_capability(rcube_storage::DUAL_USE_FOLDERS);
$acl_supported = $STORAGE->get_capability('ACL');
// Check access rights to the parent folder
if (!$error && strlen($path) && (!strlen($old_imap) || $old_imap != $name_imap)
&& $STORAGE->get_capability('ACL')
) {
if (!$error && $acl_supported && strlen($path) && (!strlen($old_imap) || $old_imap != $name_imap)) {
$parent_opts = $STORAGE->folder_info($path);
if ($parent_opts['namespace'] != 'personal'
&& (empty($parent_opts['rights']) || !preg_match('/[ck]/', implode($parent_opts['rights'])))
@ -102,12 +104,21 @@ else {
if (!$error && !strlen($old_imap)) {
$folder['subscribe'] = true;
// Server does not support both sub-folders and messages in a folder
// For folders that are supposed to contain other folders we will:
// - disable subscribtion
// - add a separator at the end to make them \NoSelect
if (!$dual_use_supported && $type == 'folder') {
$folder['subscribe'] = false;
$folder['noselect'] = true;
}
$plugin = $RCMAIL->plugins->exec_hook('folder_create', array('record' => $folder));
$folder = $plugin['record'];
if (!$plugin['abort']) {
$created = $STORAGE->create_folder($folder['name'], $folder['subscribe']);
$created = $STORAGE->create_folder($folder['name'], $folder['subscribe'], null, $folder['noselect']);
}
else {
$created = $plugin['result'];

Loading…
Cancel
Save