From 00290a603237e719cc4ec3db65e6661ba7d46a51 Mon Sep 17 00:00:00 2001
From: alecpl <alec@alec.pl>
Date: Tue, 09 Nov 2010 02:54:34 -0500
Subject: [PATCH] - Add support for shared folders (#1403507)

---
 CHANGELOG                              |    1 
 installer/rcube_install.php            |   20 +-
 program/include/rcube_imap.php         |  214 ++++++++++++++++++++++++++---------
 program/include/rcmail.php             |    6 -
 config/main.inc.php.dist               |   12 +
 program/include/rcube_imap_generic.php |   69 -----------
 6 files changed, 181 insertions(+), 141 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index de5c149..ce4509a 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -65,6 +65,7 @@
 - Improve responsiveness of messages displaying (#1486986)
 - Add option for minimum length of autocomplete's string (#1486428)
 - Fix operations on messages in unsubscribed folder (#1487107)
+- Add support for shared folders (#1403507)
 
 RELEASE 0.4.2
 -------------
diff --git a/config/main.inc.php.dist b/config/main.inc.php.dist
index 0d71c44..996b2ec 100644
--- a/config/main.inc.php.dist
+++ b/config/main.inc.php.dist
@@ -74,11 +74,17 @@
 // best server supported one)
 $rcmail_config['imap_auth_type'] = null;
 
-// If you know your imap's root directory and its folder delimiter,
-// you can specify them here. Otherwise they will be determined automatically.
-$rcmail_config['imap_root'] = null;
+// If you know your imap's folder delimiter, you can specify it here.
+// Otherwise it will be determined automatically
 $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
 // 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.
diff --git a/installer/rcube_install.php b/installer/rcube_install.php
index 677dda1..69be025 100644
--- a/installer/rcube_install.php
+++ b/installer/rcube_install.php
@@ -38,6 +38,7 @@
     'locale_string' => 'language',
     'multiple_identities' => 'identities_level',
     'addrbook_show_images' => 'show_images',
+    'imap_root' => 'imap_ns_personal',
   );
   
   // these config options are required for a working system
@@ -169,16 +170,17 @@
       else if ($prop == 'smtp_pass' && !empty($_POST['_smtp_user_u'])) {
         $value = '%p';
       }
-      else if ($prop == 'default_imap_folders'){
-	$value = Array();
-	foreach($this->config['default_imap_folders'] as $_folder){
-	  switch($_folder) {
-	  case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
-	  case 'Sent':   $_folder = $this->config['sent_mbox']; break;
-	  case 'Junk':   $_folder = $this->config['junk_mbox']; break;
-	  case 'Trash':  $_folder = $this->config['trash_mbox']; break;
+      else if ($prop == 'default_imap_folders') {
+	    $value = Array();
+	    foreach ($this->config['default_imap_folders'] as $_folder) {
+	      switch($_folder) {
+	      case 'Drafts': $_folder = $this->config['drafts_mbox']; break;
+	      case 'Sent':   $_folder = $this->config['sent_mbox']; break;
+	      case 'Junk':   $_folder = $this->config['junk_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)) {
diff --git a/program/include/rcmail.php b/program/include/rcmail.php
index c7ba419..8fa9df7 100644
--- a/program/include/rcmail.php
+++ b/program/include/rcmail.php
@@ -503,8 +503,6 @@
       'auth_method' => $this->config->get('imap_auth_type', 'check'),
       'auth_cid'    => $this->config->get('imap_auth_cid'),
       '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),
       'force_caps'  => (bool) $this->config->get('imap_force_caps'),
       'timeout'     => (int) $this->config->get('imap_timeout', 0),
@@ -790,10 +788,6 @@
     if (isset($_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;
   }
 
 
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index acf0aa0..2b77bf5 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -33,10 +33,8 @@
 {
     public $debug_level = 1;
     public $skip_deleted = false;
-    public $root_dir = '';
     public $page_size = 10;
     public $list_page = 1;
-    public $delimiter = NULL;
     public $threading = false;
     public $fetch_add_headers = '';
     public $get_all_headers = false;
@@ -54,8 +52,9 @@
      * @var rcube_mdb2
      */
     private $db;
-    private $root_ns = '';
     private $mailbox = 'INBOX';
+    private $delimiter = NULL;
+    private $namespace = NULL;
     private $sort_field = '';
     private $sort_order = 'DESC';
     private $caching_enabled = false;
@@ -156,18 +155,13 @@
         $this->port = $port;
         $this->ssl  = $use_ssl;
 
-        // print trace messages
         if ($this->conn->connected()) {
+            // print trace messages
             if ($this->conn->message && ($this->debug_level & 8)) {
                 console($this->conn->message);
             }
-
-            // get server properties
-            $rootdir = $this->conn->getRootDir();
-            if (!empty($rootdir))
-                $this->set_rootdir($rootdir);
-            if (empty($this->delimiter))
-	            $this->get_hierarchy_delimiter();
+            // get namespace and delimiter
+            $this->set_env();
 
             return true;
         }
@@ -243,28 +237,6 @@
     function set_options($opt)
     {
         $this->options = array_merge($this->options, (array)$opt);
-    }
-
-
-    /**
-     * 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();
     }
 
 
@@ -482,13 +454,101 @@
      */
     function get_hierarchy_delimiter()
     {
-        if ($this->conn && empty($this->delimiter))
-            $this->delimiter = $this->conn->getHierarchyDelimiter();
-
-        if (empty($this->delimiter))
-            $this->delimiter = '/';
-
         return $this->delimiter;
+    }
+
+
+    /**
+     * 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))
+                $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 @@
         // flatten threads array
         // @TODO: fetch children only in expanded mode (?)
         $all_ids = array();
-        foreach($msg_index as $root) {
+        foreach ($msg_index as $root) {
             $all_ids[] = $root;
             if (!empty($thread_tree[$root]))
                 $all_ids = array_merge($all_ids, array_keys_recursive($thread_tree[$root]));
@@ -1463,7 +1523,7 @@
 
         // flatten threads array
         $all_ids = array();
-        foreach($msg_index as $root) {
+        foreach ($msg_index as $root) {
             $all_ids[] = $root;
             if (!empty($thread_tree[$root])) {
                 foreach (array_keys_recursive($thread_tree[$root]) as $val)
@@ -3073,10 +3133,11 @@
             $a_mboxes = explode(',', $mbox_name);
 
         if (is_array($a_mboxes)) {
+            $delimiter = $this->get_hierarchy_delimiter();
+        
             foreach ($a_mboxes as $mbox_name) {
                 $mailbox = $this->mod_mailbox($mbox_name);
-                $sub_mboxes = $this->conn->listMailboxes($this->mod_mailbox(''),
-	                $mbox_name . $this->delimiter . '*');
+                $sub_mboxes = $this->conn->listMailboxes('', $mbox_name . $delimiter . '*');
 
                 // unsubscribe mailbox before deleting
                 $this->conn->unsubscribe($mailbox);
@@ -3137,19 +3198,20 @@
             if ($mbox_name == 'INBOX')
                 return true;
 
-            $key = $subscription ? 'subscribed' : 'existing';
-            if (is_array($this->icache[$key]) && in_array($mbox_name, $this->icache[$key]))
+            $key  = $subscription ? 'subscribed' : 'existing';
+            $mbox = $this->mod_mailbox($mbox_name)
+            if (is_array($this->icache[$key]) && in_array($mbox, $this->icache[$key]))
                 return true;
 
             if ($subscription) {
-                $a_folders = $this->conn->listSubscribed($this->mod_mailbox(''), $mbox_name);
+                $a_folders = $this->conn->listSubscribed('', $mbox);
             }
             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)) {
-                $this->icache[$key][] = $mbox_name;
+            if (is_array($a_folders) && in_array($mbox, $a_folders)) {
+                $this->icache[$key][] = $mbox;
                 return true;
             }
         }
@@ -3167,14 +3229,52 @@
      */
     function mod_mailbox($mbox_name, $mode='in')
     {
-        if ($mbox_name == 'INBOX')
-            return $mbox_name;
+        if (empty($mbox_name))
+            return '';
 
-        if (!empty($this->root_dir)) {
-            if ($mode=='in')
-                $mbox_name = $this->root_dir.$this->delimiter.$mbox_name;
-            else if (!empty($mbox_name)) // $mode=='out'
-                $mbox_name = substr($mbox_name, strlen($this->root_dir)+1);
+        if ($mode == 'in') {
+            // If folder contains namespace prefix, don't modify it
+            if (is_array($this->namespace['shared'])) {
+                foreach ($this->namespace['shared'] as $ns) {
+                    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;
@@ -3200,7 +3300,7 @@
 
         if (!is_array($this->conn->data['LIST']) || !is_array($this->conn->data['LIST'][$mbox])) {
             if ($force) {
-                $this->conn->listMailboxes($this->mod_mailbox(''), $mbox_name);
+                $this->conn->listMailboxes('', $mbox_name);
             }
             else {
                 return array();
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index 0cb9f24..6de27e8 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -549,53 +549,14 @@
     }
 
     /**
-     * Gets the root directory and delimiter (of personal namespace)
+     * Gets the delimiter
      *
-     * @return mixed A root directory name, or false.
-     */
-    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()
+     * @return string The delimiter
      */
     function getHierarchyDelimiter()
     {
 	    if ($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)
@@ -611,26 +572,7 @@
 	        }
         }
 
-	    // if that fails, try namespace extension
-	    // 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];
+        return NULL;
     }
 
     /**
@@ -830,7 +772,6 @@
             if ($this->prefs['force_caps']) {
 			    $this->clearCapability();
             }
-		    $this->getRootDir();
             $this->logged = true;
 
 		    return true;
@@ -1941,10 +1882,6 @@
     {
 		if (empty($mailbox)) {
 	        $mailbox = '*';
-	    }
-
-	    if (empty($ref) && $this->prefs['rootdir']) {
-	        $ref = $this->prefs['rootdir'];
 	    }
 
         $args = array();

--
Gitblit v1.9.1