From 9684dc018f68b037e8ee369e7ed08f4c760fe736 Mon Sep 17 00:00:00 2001
From: Thomas <tb@woodcrest.local>
Date: Thu, 16 Jan 2014 05:32:47 -0500
Subject: [PATCH] Support globally unique message UIDs with IMAP folder name appended

---
 program/steps/mail/search.inc   |   16 +++++
 program/steps/mail/func.inc     |   44 ++++++++++++++
 program/steps/mail/mark.inc     |   10 ++-
 program/js/app.js               |   27 ++++++---
 program/steps/mail/copy.inc     |    6 +-
 program/steps/mail/move_del.inc |   22 ++++---
 6 files changed, 98 insertions(+), 27 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index 55387c0..2717e35 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -685,7 +685,7 @@
 
       case 'open':
         if (uid = this.get_single_uid()) {
-          obj.href = this.url('show', {_mbox: this.env.mailbox, _uid: uid});
+          obj.href = this.url('show', {_mbox: this.get_message_mailbox(uid), _uid: uid});
           return true;
         }
         break;
@@ -788,9 +788,9 @@
           this.load_contact(cid, 'edit');
         else if (this.task == 'settings' && props)
           this.load_identity(props, 'edit-identity');
-        else if (this.task == 'mail' && (cid = this.get_single_uid())) {
-          url = { _mbox: this.env.mailbox };
-          url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = cid;
+        else if (this.task == 'mail' && (uid = this.get_single_uid())) {
+          url = { _mbox: this.get_message_mailbox(uid) };
+          url[this.env.mailbox == this.env.drafts_mailbox && props != 'new' ? '_draft_uid' : '_uid'] = uid;
           this.open_compose_step(url);
         }
         break;
@@ -1070,7 +1070,7 @@
       case 'reply-list':
       case 'reply':
         if (uid = this.get_single_uid()) {
-          url = {_reply_uid: uid, _mbox: this.env.mailbox};
+          url = {_reply_uid: uid, _mbox: this.get_message_mailbox(uid)};
           if (command == 'reply-all')
             // do reply-list, when list is detected and popup menu wasn't used
             url._all = (!props && this.env.reply_all_mode == 1 && this.commands['reply-list'] ? 'list' : 'all');
@@ -1098,7 +1098,7 @@
           this.gui_objects.messagepartframe.contentWindow.print();
         }
         else if (uid = this.get_single_uid()) {
-          ref.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.env.mailbox)+(this.env.safemode ? '&_safe=1' : ''), true, true);
+          ref.printwin = this.open_window(this.env.comm_path+'&_action=print&_uid='+uid+'&_mbox='+urlencode(this.get_message_mailbox(uid))+(this.env.safemode ? '&_safe=1' : ''), true, true);
           if (this.printwin) {
             if (this.env.action != 'show')
               this.mark_message('read', uid);
@@ -1115,8 +1115,9 @@
         if (this.env.action == 'get') {
           location.href = location.href.replace(/_frame=/, '_download=');
         }
-        else if (uid = this.get_single_uid())
-          this.goto_url('viewsource', { _uid: uid, _mbox: this.env.mailbox, _save: 1 });
+        else if (uid = this.get_single_uid()) {
+          this.goto_url('viewsource', { _uid: uid, _mbox: this.get_message_mailbox(uid), _save: 1 });
+        }
         break;
 
       // quicksearch
@@ -1820,6 +1821,7 @@
       selected: this.select_all_mode || this.message_list.in_selection(uid),
       ml: flags.ml?1:0,
       ctype: flags.ctype,
+      mbox: flags.mbox,
       // flags from plugins
       flags: flags.extra_flags
     });
@@ -2022,7 +2024,7 @@
 
     var win, target = window,
       action = preview ? 'preview': 'show',
-      url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox);
+      url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.get_message_mailbox(id));
 
     if (preview && (win = this.get_frame_window(this.env.contentframe))) {
       target = win;
@@ -7424,6 +7426,13 @@
     return this.env.cid ? this.env.cid : (this.contact_list ? this.contact_list.get_single_selection() : null);
   };
 
+  // get the IMP mailbox of the message with the given UID
+  this.get_message_mailbox = function(uid)
+  {
+    var msg = this.env.messages ? this.env.messages[uid] : {};
+    return msg.mbox || this.env.mailbox;
+  }
+
   // gets cursor position
   this.get_caret_pos = function(obj)
   {
diff --git a/program/steps/mail/copy.inc b/program/steps/mail/copy.inc
index a392f30..0f7b1a0 100644
--- a/program/steps/mail/copy.inc
+++ b/program/steps/mail/copy.inc
@@ -26,11 +26,11 @@
 
 // move messages
 if (!empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) {
-    $uids   = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST);
     $target = rcube_utils::get_input_value('_target_mbox', rcube_utils::INPUT_POST, true);
-    $mbox   = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
 
-    $copied = $RCMAIL->storage->copy_message($uids, $target, $mbox);
+    foreach (rcmail_get_uids() as $mbox => $uids) {
+      $copied += (int)$RCMAIL->storage->copy_message($uids, $target, $mbox);
+    }
 
     if (!$copied) {
         // send error message
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 7436544..cc2c7e0 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -68,6 +68,21 @@
     $OUTPUT->set_env('search_text', $_SESSION['last_text_search']);
 }
 
+// remove mbox part from _uid
+if (($_uid  = get_input_value('_uid', RCUBE_INPUT_GPC)) && preg_match('/^\d+-[^,]+$/', $_uid)) {
+  list($_uid, $mbox) = explode('-', $_uid);
+  if (isset($_GET['_uid']))  $_GET['_uid']  = $_uid;
+  if (isset($_POST['_uid'])) $_POST['_uid'] = $_uid;
+  $_REQUEST['_uid'] = $_uid;
+  unset($_uid);
+
+  if (empty($_REQUEST['_mbox']) && !empty($mbox)) {
+    $_GET['_mbox']  = $mbox;
+    $_POST['_mbox'] = $mbox;
+  }
+}
+
+
 // set main env variables, labels and page title
 if (empty($RCMAIL->action) || $RCMAIL->action == 'list') {
     // connect to storage server and trigger error on failure
@@ -166,6 +181,35 @@
 ));
 
 
+/**
+ * Returns message UID(s) and IMAP folder(s) from GET/POST data
+ *
+ * @return array List of message UIDs per folder
+ */
+function rcmail_get_uids()
+{
+    // message UID (or comma-separated list of IDs) is provided in
+    // the form of <ID>-<MBOX>[,<ID>-<MBOX>]*
+
+    $_uid  = get_input_value('_uid', RCUBE_INPUT_GPC);
+    $_mbox = (string)get_input_value('_mbox', RCUBE_INPUT_GPC);
+
+    if (is_array($uid)) {
+        return $uid;
+    }
+
+    // create a per-folder UIDs array
+    $result = array();
+    foreach (explode(',', $_uid) as $uid) {
+        list($uid, $mbox) = explode('-', $uid, 2);
+        if (empty($mbox))
+            $mbox = $_mbox;
+        $result[$mbox][] = $uid;
+    }
+
+    return $result;
+}
+
 
 /**
  * Returns default search mods
diff --git a/program/steps/mail/mark.inc b/program/steps/mail/mark.inc
index daa8c7e..50243c6 100644
--- a/program/steps/mail/mark.inc
+++ b/program/steps/mail/mark.inc
@@ -36,7 +36,7 @@
     'unflagged' => 'UNFLAGGED',
 );
 
-if (($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST))
+if (($_uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST))
     && ($flag = rcube_utils::get_input_value('_flag', rcube_utils::INPUT_POST))
 ) {
     $flag = $a_flags_map[$flag] ? $a_flags_map[$flag] : strtoupper($flag);
@@ -45,10 +45,12 @@
         // count messages before changing anything
         $old_count = $RCMAIL->storage->count(NULL, $threading ? 'THREADS' : 'ALL');
         $old_pages = ceil($old_count / $RCMAIL->storage->get_pagesize());
-        $count     = sizeof(explode(',', $uids));
     }
 
-    $marked = $RCMAIL->storage->set_flag($uids, $flag);
+    foreach (rcmail_get_uids() as $mbox => $uids) {
+        $marked += (int)$RCMAIL->storage->set_flag($uids, $flag, $mbox);
+        $count += count($uids);
+    }
 
     if (!$marked) {
         // send error message
@@ -128,7 +130,7 @@
             }
 
             // add new rows from next page (if any)
-            if ($count && $uids != '*' && ($jump_back || $nextpage_count > 0)) {
+            if ($old_count && $_uids != '*' && ($jump_back || $nextpage_count > 0)) {
                 $a_headers = $RCMAIL->storage->list_messages($mbox, NULL,
                     rcmail_sort_column(), rcmail_sort_order(), $jump_back ? NULL : $count);
 
diff --git a/program/steps/mail/move_del.inc b/program/steps/mail/move_del.inc
index 7564bb8..26c7245 100644
--- a/program/steps/mail/move_del.inc
+++ b/program/steps/mail/move_del.inc
@@ -5,7 +5,7 @@
  | program/steps/mail/move_del.inc                                       |
  |                                                                       |
  | This file is part of the Roundcube Webmail client                     |
- | Copyright (C) 2005-2009, The Roundcube Dev Team                       |
+ | Copyright (C) 2005-2013, The Roundcube Dev Team                       |
  |                                                                       |
  | Licensed under the GNU General Public License version 3 or            |
  | any later version with exceptions for skins & plugins.                |
@@ -32,11 +32,13 @@
 
 // move messages
 if ($RCMAIL->action == 'move' && !empty($_POST['_uid']) && strlen($_POST['_target_mbox'])) {
-    $count  = sizeof(explode(',', ($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST))));
     $target = rcube_utils::get_input_value('_target_mbox', rcube_utils::INPUT_POST, true);
-    $mbox   = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
+    $trash  = $RCMAIL->config->get('trash_mbox');
 
-    $moved = $RCMAIL->storage->move_message($uids, $target, $mbox);
+    foreach (rcmail_get_uids() as $mbox => $uids) {
+        $moved += (int)$RCMAIL->storage->move_message($uids, $target, $mbox);
+        $count += count($uids);
+    }
 
     if (!$moved) {
         // send error message
@@ -47,17 +49,17 @@
         exit;
     }
     else {
-      $OUTPUT->show_message('messagemoved', 'confirmation');
+        $OUTPUT->show_message('messagemoved', 'confirmation');
     }
 
     $addrows = true;
 }
 // delete messages 
 else if ($RCMAIL->action=='delete' && !empty($_POST['_uid'])) {
-    $count = sizeof(explode(',', ($uids = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST))));
-    $mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST, true);
-
-    $del = $RCMAIL->storage->delete_message($uids, $mbox);
+    foreach (rcmail_get_uids() as $mbox => $uids) {
+        $del += (int)$RCMAIL->storage->delete_message($uids, $mbox);
+        $count += count($uids);
+    }
 
     if (!$del) {
         // send error message
@@ -68,7 +70,7 @@
         exit;
     }
     else {
-      $OUTPUT->show_message('messagedeleted', 'confirmation');
+        $OUTPUT->show_message('messagedeleted', 'confirmation');
     }
 
     $addrows = true;
diff --git a/program/steps/mail/search.inc b/program/steps/mail/search.inc
index a808872..9f4cdc9 100644
--- a/program/steps/mail/search.inc
+++ b/program/steps/mail/search.inc
@@ -127,9 +127,23 @@
 $result_h = $RCMAIL->storage->list_messages($mbox, 1, $sort_column, rcmail_sort_order());
 $count    = $RCMAIL->storage->count($mbox, $RCMAIL->storage->get_threading() ? 'THREADS' : 'ALL');
 
+// Add 'folder' column to list
+if ($multi_folder_search) {
+  $a_show_cols = $_SESSION['list_attrib']['columns'] ? $_SESSION['list_attrib']['columns'] : (array)$CONFIG['list_cols'];
+  if (!in_array($a_show_cols))
+    $a_show_cols[] = 'folder';
+
+  // make message UIDs unique by appending the folder name
+  foreach ($result_h as $i => $header) {
+    $header->uid .= '-'.$header->folder;
+    if ($header->parent_uid)
+      $header->parent_uid .= '-'.$header->folder;
+  }
+}
+
 // Make sure we got the headers
 if (!empty($result_h)) {
-    rcmail_js_message_list($result_h);
+    rcmail_js_message_list($result_h, false, $a_show_cols);
     if ($search_str) {
         $OUTPUT->show_message('searchsuccessful', 'confirmation', array('nr' => $RCMAIL->storage->count(NULL, 'ALL')));
     }

--
Gitblit v1.9.1