From 037af6890fe6fdb84a08d3c86083e847c90ec0ad Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Tue, 22 Oct 2013 08:17:26 -0400
Subject: [PATCH] Fix vulnerability in handling _session argument of utils/save-prefs (#1489382)

---
 program/js/list.js |  195 +++++++++++++++++++++++++++---------------------
 1 files changed, 109 insertions(+), 86 deletions(-)

diff --git a/program/js/list.js b/program/js/list.js
index 9f5ae3c..368ee5b 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -15,8 +15,6 @@
  +-----------------------------------------------------------------------+
  | Requires: common.js                                                   |
  +-----------------------------------------------------------------------+
-
-  $Id$
 */
 
 
@@ -90,10 +88,8 @@
     this.frame = this.list.parentNode;
 
     // set body events
-    if (this.keyboard) {
-      rcube_event.add_listener({event:bw.opera?'keypress':'keydown', object:this, method:'key_press'});
-      rcube_event.add_listener({event:'keydown', object:this, method:'key_down'});
-    }
+    if (this.keyboard)
+      rcube_event.add_listener({event:'keydown', object:this, method:'key_press'});
   }
 },
 
@@ -114,17 +110,25 @@
     row.onmousedown = function(e){ return self.drag_row(e, this.uid); };
     row.onmouseup = function(e){ return self.click_row(e, this.uid); };
 
-    if (bw.iphone || bw.ipad) {
+    if (bw.touch) {
       row.addEventListener('touchstart', function(e) {
         if (e.touches.length == 1) {
-          if (!self.drag_row(rcube_event.touchevent(e.touches[0]), this.uid))
-            e.preventDefault();
+          self.touchmoved = false;
+          self.drag_row(rcube_event.touchevent(e.touches[0]), this.uid)
         }
       }, false);
       row.addEventListener('touchend', function(e) {
-        if (e.changedTouches.length == 1)
-          if (!self.click_row(rcube_event.touchevent(e.changedTouches[0]), this.uid))
+        if (e.changedTouches.length == 1) {
+          if (!self.touchmoved && !self.click_row(rcube_event.touchevent(e.changedTouches[0]), this.uid))
             e.preventDefault();
+        }
+      }, false);
+      row.addEventListener('touchmove', function(e) {
+        if (e.changedTouches.length == 1) {
+          self.touchmoved = true;
+          if (self.drag_active)
+            e.preventDefault();
+        }
       }, false);
     }
 
@@ -233,9 +237,10 @@
     }
   }
 
-  // Un-focus already focused elements
-  $('*:focus', window).blur();
-  $('iframe').each(function() { this.blur(); });
+  // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
+  // It looks that window.focus() does the job for all browsers, but not Firefox (#1489058)
+  $(':focus:not(body)').blur();
+  window.focus();
 
   if (e || (e = window.event))
     rcube_event.cancel(e);
@@ -302,7 +307,7 @@
   if (rcube_event.get_button(e) == 2)
     return true;
 
-  this.in_selection_before = this.in_selection(id) ? id : false;
+  this.in_selection_before = e && e.istouch || this.in_selection(id) ? id : false;
 
   // selects currently unselected row
   if (!this.in_selection_before) {
@@ -310,12 +315,12 @@
     this.select_row(id, mod_key, false);
   }
 
-  if (this.draggable && this.selection.length) {
+  if (this.draggable && this.selection.length && this.in_selection(id)) {
     this.drag_start = true;
     this.drag_mouse_start = rcube_event.get_mouse_pos(e);
     rcube_event.add_listener({event:'mousemove', object:this, method:'drag_mouse_move'});
     rcube_event.add_listener({event:'mouseup', object:this, method:'drag_mouse_up'});
-    if (bw.iphone || bw.ipad) {
+    if (bw.touch) {
       rcube_event.add_listener({event:'touchmove', object:this, method:'drag_mouse_move'});
       rcube_event.add_listener({event:'touchend', object:this, method:'drag_mouse_up'});
     }
@@ -530,6 +535,7 @@
   return false;
 },
 
+
 expand_all: function(row)
 {
   var depth, new_row, r;
@@ -564,6 +570,7 @@
   }
   return false;
 },
+
 
 update_expando: function(uid, expanded)
 {
@@ -737,7 +744,7 @@
 
 
 /**
- * Select last row 
+ * Select last row
  */
 select_last: function(mod_key)
 {
@@ -758,25 +765,13 @@
 /**
  * Add all childs of the given row to selection
  */
-select_childs: function(uid)
+select_children: function(uid)
 {
-  if (!this.rows[uid] || !this.rows[uid].has_children)
-    return;
+  var i, children = this.row_children(uid), len = children.length;
 
-  var depth = this.rows[uid].depth,
-    row = this.rows[uid].obj.nextSibling;
-
-  while (row) {
-    if (row.nodeType == 1) {
-      if ((r = this.rows[row.uid])) {
-        if (!r.depth || r.depth <= depth)
-          break;
-        if (!this.in_selection(r.uid))
-          this.select_row(r.uid, CONTROL_KEY);
-      }
-    }
-    row = row.nextSibling;
-  }
+  for (i=0; i<len; i++)
+    if (!this.in_selection(children[i]))
+      this.select_row(children[i], CONTROL_KEY);
 },
 
 
@@ -788,10 +783,16 @@
   if (!this.rows[this.shift_start] || !this.selection.length)
     this.shift_start = id;
 
-  var n, from_rowIndex = this.rows[this.shift_start].obj.rowIndex,
-    to_rowIndex = this.rows[id].obj.rowIndex,
-    i = ((from_rowIndex < to_rowIndex)? from_rowIndex : to_rowIndex),
-    j = ((from_rowIndex > to_rowIndex)? from_rowIndex : to_rowIndex);
+  var n, i, j, to_row = this.rows[id],
+    from_rowIndex = this.rows[this.shift_start].obj.rowIndex,
+    to_rowIndex = to_row.obj.rowIndex;
+
+  if (!to_row.expanded && to_row.has_children)
+    if (to_row = this.rows[(this.row_children(id)).pop()])
+      to_rowIndex = to_row.obj.rowIndex;
+
+  i = ((from_rowIndex < to_rowIndex) ? from_rowIndex : to_rowIndex),
+  j = ((from_rowIndex > to_rowIndex) ? from_rowIndex : to_rowIndex);
 
   // iterate through the entire message list
   for (n in this.rows) {
@@ -837,7 +838,7 @@
   for (n in this.rows) {
     if (!filter || this.rows[n][filter] == true) {
       this.last_selected = n;
-      this.highlight_row(n, true);
+      this.highlight_row(n, true, true);
     }
     else {
       $(this.rows[n].obj).removeClass('selected').removeClass('unfocused');
@@ -932,19 +933,24 @@
 /**
  * Highlight/unhighlight a row
  */
-highlight_row: function(id, multiple)
+highlight_row: function(id, multiple, norecur)
 {
-  if (this.rows[id] && !multiple) {
+  if (!this.rows[id])
+    return;
+
+  if (!multiple) {
     if (this.selection.length > 1 || !this.in_selection(id)) {
       this.clear_selection();
       this.selection[0] = id;
       $(this.rows[id].obj).addClass('selected');
     }
   }
-  else if (this.rows[id]) {
+  else {
     if (!this.in_selection(id)) { // select row
-      this.selection[this.selection.length] = id;
+      this.selection.push(id);
       $(this.rows[id].obj).addClass('selected');
+      if (!norecur && !this.rows[id].expanded)
+        this.highlight_children(id, true);
     }
     else { // unselect row
       var p = $.inArray(id, this.selection),
@@ -953,7 +959,25 @@
 
       this.selection = a_pre.concat(a_post);
       $(this.rows[id].obj).removeClass('selected').removeClass('unfocused');
+      if (!norecur && !this.rows[id].expanded)
+        this.highlight_children(id, false);
     }
+  }
+},
+
+
+/**
+ * Highlight/unhighlight all childs of the given row
+ */
+highlight_children: function(id, status)
+{
+  var i, selected,
+    children = this.row_children(id), len = children.length;
+
+  for (i=0; i<len; i++) {
+    selected = this.in_selection(children[i]);
+    if ((status && !selected) || (!status && selected))
+      this.highlight_row(children[i], true, true);
   }
 },
 
@@ -996,6 +1020,14 @@
     case 35: // End
       this.select_last(mod_key);
       return rcube_event.cancel(e);
+    case 27:
+      if (this.drag_active)
+        return this.drag_mouse_up(e);
+      if (this.col_drag_active) {
+        this.selected_column = null;
+        return this.column_drag_mouse_up(e);
+      }
+      return rcube_event.cancel(e);
     default:
       this.key_pressed = keyCode;
       this.modkey = mod_key;
@@ -1004,41 +1036,6 @@
 
       if (this.key_pressed == this.BACKSPACE_KEY)
         return rcube_event.cancel(e);
-  }
-
-  return true;
-},
-
-/**
- * Handler for keydown events
- */
-key_down: function(e)
-{
-  var target = e.target || {};
-  if (this.focused != true || target.nodeName == 'INPUT' || target.nodeName == 'TEXTAREA' || target.nodeName == 'SELECT')
-    return true;
-  
-  switch (rcube_event.get_keycode(e)) {
-    case 27:
-      if (this.drag_active)
-      return this.drag_mouse_up(e);
-      if (this.col_drag_active) {
-        this.selected_column = null;
-        return this.column_drag_mouse_up(e);
-      }
-
-    case 40:
-    case 38: 
-    case 63233:
-    case 63232:
-    case 61:
-    case 107:
-    case 109:
-    case 32:
-      if (!rcube_event.get_modifier(e) && this.focused)
-        return rcube_event.cancel(e);
-
-    default:
   }
 
   return true;
@@ -1126,12 +1123,12 @@
 {
   // convert touch event
   if (e.type == 'touchmove') {
-    if (e.changedTouches.length == 1)
+    if (e.touches.length == 1 && e.changedTouches.length == 1)
       e = rcube_event.touchevent(e.changedTouches[0]);
     else
       return rcube_event.cancel(e);
   }
-  
+
   if (this.drag_start) {
     // check mouse movement, of less than 3 pixels, don't start dragging
     var m = rcube_event.get_mouse_pos(e);
@@ -1148,8 +1145,8 @@
     var n, uid, selection = $.merge([], this.selection);
     for (n in selection) {
       uid = selection[n];
-      if (this.rows[uid].has_children && !this.rows[uid].expanded)
-        this.select_childs(uid);
+      if (!this.rows[uid].expanded)
+        this.select_children(uid);
     }
 
     // reset content
@@ -1222,7 +1219,7 @@
 drag_mouse_up: function(e)
 {
   document.onmousemove = null;
-  
+
   if (e.type == 'touchend') {
     if (e.changedTouches.length != 1)
       return rcube_event.cancel(e);
@@ -1241,8 +1238,8 @@
 
   rcube_event.remove_listener({event:'mousemove', object:this, method:'drag_mouse_move'});
   rcube_event.remove_listener({event:'mouseup', object:this, method:'drag_mouse_up'});
-  
-  if (bw.iphone || bw.ipad) {
+
+  if (bw.touch) {
     rcube_event.remove_listener({event:'touchmove', object:this, method:'drag_mouse_move'});
     rcube_event.remove_listener({event:'touchend', object:this, method:'drag_mouse_up'});
   }
@@ -1370,6 +1367,32 @@
 
 
 /**
+ * Returns IDs of all rows in a thread (except root) for specified root
+ */
+row_children: function(uid)
+{
+  if (!this.rows[uid] || !this.rows[uid].has_children)
+    return [];
+
+  var res = [], depth = this.rows[uid].depth,
+    row = this.rows[uid].obj.nextSibling;
+
+  while (row) {
+    if (row.nodeType == 1) {
+      if ((r = this.rows[row.uid])) {
+        if (!r.depth || r.depth <= depth)
+          break;
+        res.push(r.uid);
+      }
+    }
+    row = row.nextSibling;
+  }
+
+  return res;
+},
+
+
+/**
  * Creates a layer for drag&drop over iframes
  */
 add_dragfix: function()

--
Gitblit v1.9.1