From 56040b941c20d1d2ed2abf3f1993ea550bc13086 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Fri, 06 Feb 2015 03:27:44 -0500
Subject: [PATCH] Fix so JSON.parse() errors on localStorage items are ignored (#1490249)

---
 program/js/app.js |  151 ++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 108 insertions(+), 43 deletions(-)

diff --git a/program/js/app.js b/program/js/app.js
index fd0d2e1..227ba32 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -58,7 +58,6 @@
     request_timeout: 180,  // seconds
     draft_autosave: 0,     // seconds
     comm_path: './',
-    blankpage: 'program/resources/blank.gif',
     recipients_separator: ',',
     recipients_delimiter: ', ',
     popup_width: 1150,
@@ -162,6 +161,9 @@
       this.goto_url('error', '_code=0x199');
       return;
     }
+
+    if (!this.env.blankpage)
+      this.env.blankpage = this.assets_path('program/resources/blank.gif');
 
     // find all registered gui containers
     for (n in this.gui_containers)
@@ -572,6 +574,7 @@
       this.treelist
         .addEventListener('collapse', function(node) { ref.folder_collapsed(node) })
         .addEventListener('expand', function(node) { ref.folder_collapsed(node) })
+        .addEventListener('beforeselect', function(node) { return !ref.busy; })
         .addEventListener('select', function(node) { ref.triggerEvent('selectfolder', { folder:node.id, prefix:'rcmli' }) });
     }
 
@@ -630,8 +633,9 @@
     if (obj && obj.blur && !(event && rcube_event.is_keyboard(event)))
       obj.blur();
 
-    // do nothing if interface is locked by other command (with exception for searching reset)
-    if (this.busy && !(command == 'reset-search' && this.last_command == 'search'))
+    // do nothing if interface is locked by another command
+    // with exception for searching reset and menu
+    if (this.busy && !(command == 'reset-search' && this.last_command == 'search') && !command.match(/^menu-/))
       return false;
 
     // let the browser handle this click (shift/ctrl usually opens the link in a new window/tab)
@@ -1051,12 +1055,9 @@
         url = {};
 
         if (this.task == 'mail') {
-          url._mbox = this.env.mailbox;
+          url = {_mbox: this.env.mailbox, _search: this.env.search_request};
           if (props)
             url._to = props;
-          // also send search request so we can go back to search result after message is sent
-          if (this.env.search_request)
-            url._search = this.env.search_request;
         }
         // modify url if we're in addressbook
         else if (this.task == 'addressbook') {
@@ -1151,7 +1152,7 @@
       case 'reply-list':
       case 'reply':
         if (uid = this.get_single_uid()) {
-          url = {_reply_uid: uid, _mbox: this.get_message_mailbox(uid)};
+          url = {_reply_uid: uid, _mbox: this.get_message_mailbox(uid), _search: this.env.search_request};
           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');
@@ -1212,7 +1213,8 @@
 
       // reset quicksearch
       case 'reset-search':
-        var n, s = this.env.search_request || this.env.qsearch;
+        var n, s = this.env.search_request || this.env.qsearch,
+            ss = this.gui_objects.qsearchbox && this.gui_objects.qsearchbox.value != '';
 
         this.reset_qsearch();
         this.select_all_mode = false;
@@ -1221,7 +1223,7 @@
           if (this.contact_list)
             this.list_contacts_clear();
         }
-        else if (s && this.env.mailbox) {
+        else if (s && ss && this.env.mailbox) {
           this.list_mailbox(this.env.mailbox, 1);
         }
         else if (s && this.task == 'addressbook') {
@@ -1405,8 +1407,10 @@
 
     if (task == 'mail')
       url += '&_mbox=INBOX';
-    else if (task == 'logout' && !this.env.server_error)
+    else if (task == 'logout' && !this.env.server_error) {
+      url += '&_token=' + this.env.request_token;
       this.clear_compose_data();
+    }
 
     this.redirect(url);
   };
@@ -1416,7 +1420,10 @@
     if (!url)
       url = this.env.comm_path;
 
-    return url.replace(/_task=[a-z0-9_-]+/i, '_task='+task);
+    if (url.match(/[?&]_task=[a-zA-Z0-9_-]+/))
+        return url.replace(/_task=[a-zA-Z0-9_-]+/, '_task=' + task);
+    else
+        return url.replace(/\?.*$/, '') + '?_task=' + task;
   };
 
   this.reload = function(delay)
@@ -1947,7 +1954,7 @@
     // attach events
     $.each(fn, function(i, f) {
       row[i].onclick = function(e) { f(e); return rcube_event.cancel(e); };
-      if (bw.touch) {
+      if (bw.touch && row[i].addEventListener) {
         row[i].addEventListener('touchend', function(e) {
           if (e.changedTouches.length == 1) {
             f(e);
@@ -2027,7 +2034,7 @@
     }
     if (flags.forwarded) {
       status_class += ' forwarded';
-      status_label += this.get_label('replied') + ' ';
+      status_label += this.get_label('forwarded') + ' ';
     }
 
     // update selection
@@ -2251,7 +2258,7 @@
       if (preview && this.message_list && this.message_list.rows[id] && this.message_list.rows[id].unread && this.env.preview_pane_mark_read > 0) {
         this.preview_read_timer = setTimeout(function() {
           ref.set_unread_message(id, ref.env.mailbox);
-          ref.http_post('mark', {_uid: id, _flag: 'read', _quiet: 1});
+          ref.http_post('mark', {_uid: id, _flag: 'read', _mbox: ref.env.mailbox, _quiet: 1});
         }, this.env.preview_pane_mark_read * 1000);
       }
     }
@@ -2477,6 +2484,16 @@
         selection.push(selected[i]);
 
     this.message_list.selection = selection;
+
+    // reset preview frame, if currently previewed message is not selected (has been removed)
+    try {
+      var win = this.get_frame_window(this.env.contentframe),
+        id = win.rcmail.env.uid;
+
+      if (id && $.inArray(id, selection) < 0)
+        this.show_contentframe(false);
+    }
+    catch (e) {};
   };
 
   // expand all threads with unread children
@@ -3426,6 +3443,7 @@
           this.get_label('restoremessage'),
           [{
             text: this.get_label('restore'),
+            'class': 'mainaction',
             click: function(){
               ref.restore_compose_form(key, html_mode);
               ref.remove_compose_data(key);  // remove old copy
@@ -3435,6 +3453,7 @@
           },
           {
             text: this.get_label('delete'),
+            'class': 'delete',
             click: function(){
               ref.remove_compose_data(key);
               $(this).dialog('close');
@@ -3687,7 +3706,7 @@
       $(this).dialog('close');
     };
 
-    this.show_popup_dialog(html, this.gettext('newresponse'), buttons);
+    this.show_popup_dialog(html, this.gettext('newresponse'), buttons, {button_classes: ['mainaction']});
 
     $('#ffresponsetext').val(text);
     $('#ffresponsename').select();
@@ -3769,14 +3788,13 @@
 
   this.set_draft_id = function(id)
   {
-    var rc;
-
     if (id && id != this.env.draft_id) {
-      if (rc = this.opener()) {
-        // refresh the drafts folder in opener window
-        if (rc.env.task == 'mail' && rc.env.action == '' && rc.env.mailbox == this.env.drafts_mailbox)
-          rc.command('checkmail');
-      }
+      var filter = {task: 'mail', action: ''},
+        rc = this.opener(false, filter) || this.opener(true, filter);
+
+      // refresh the drafts folder in the opener window
+      if (rc && rc.env.mailbox == this.env.drafts_mailbox)
+        rc.command('checkmail');
 
       this.env.draft_id = id;
       $("input[name='_draft_saveid']").val(id);
@@ -3962,7 +3980,6 @@
 
     this.local_storage_remove_item('compose.index');
   };
-
 
   this.change_identity = function(obj, show_sig)
   {
@@ -4332,6 +4349,7 @@
       (this.env.search_request && (this.env.search_scope || 'base') != 'base');
   };
 
+  // action executed after mail is sent
   this.sent_successfully = function(type, msg, folders)
   {
     this.display_message(msg, type);
@@ -4340,11 +4358,13 @@
     if (this.env.extwin) {
       this.lock_form(this.gui_objects.messageform);
 
-      var rc = this.opener();
+      var filter = {task: 'mail', action: ''},
+        rc = this.opener(false, filter) || this.opener(true, filter);
+
       if (rc) {
         rc.display_message(msg, type);
         // refresh the folder where sent message was saved or replied message comes from
-        if (folders && rc.env.task == 'mail' && rc.env.action == '' && $.inArray(rc.env.mailbox, folders) >= 0) {
+        if (folders && $.inArray(rc.env.mailbox, folders) >= 0) {
           rc.command('checkmail');
         }
       }
@@ -4762,6 +4782,9 @@
     if (!src)
       src = this.env.source;
 
+    if (refresh)
+      group = this.env.group;
+
     if (page && this.current_page == page && src == this.env.source && group == this.env.group)
       return false;
 
@@ -5150,10 +5173,10 @@
         dateFormat: this.env.date_format,
         changeMonth: true,
         changeYear: true,
-        yearRange: '-100:+10',
+        yearRange: '-120:+10',
         showOtherMonths: true,
-        selectOtherMonths: true,
-        onSelect: function(dateText) { $(this).focus().val(dateText) }
+        selectOtherMonths: true
+//        onSelect: function(dateText) { $(this).focus().val(dateText); }
       });
       $('input.datepicker').datepicker();
     }
@@ -5173,6 +5196,7 @@
     this.show_popup_dialog(content, this.get_label('newgroup'),
       [{
         text: this.get_label('save'),
+        'class': 'mainaction',
         click: function() {
           var name;
 
@@ -5200,6 +5224,7 @@
     this.show_popup_dialog(content, this.get_label('grouprename'),
       [{
         text: this.get_label('save'),
+        'class': 'mainaction',
         click: function() {
           var name;
 
@@ -5563,6 +5588,7 @@
     this.show_popup_dialog(content, this.get_label('searchsave'),
       [{
         text: this.get_label('save'),
+        'class': 'mainaction',
         click: function() {
           var name;
 
@@ -5781,6 +5807,9 @@
         // on the list when dragging starts (and stops), this is slow, but
         // I didn't find a method to check droptarget on over event
         accept: function(node) {
+          if (!$(node).is('.mailbox'))
+            return false;
+
           var source_folder = ref.folder_id2name($(node).attr('id')),
             dest_folder = ref.folder_id2name(this.id),
             source = ref.env.subscriptionrows[source_folder],
@@ -5801,7 +5830,7 @@
 
   this.folder_id2name = function(id)
   {
-    return ref.html_identifier_decode(id.replace(/^rcmli/, ''));
+    return id ? ref.html_identifier_decode(id.replace(/^rcmli/, '')) : null;
   };
 
   this.subscription_select = function(id)
@@ -6548,14 +6577,16 @@
     else
       popup.html(content);
 
-    popup.dialog($.extend({
+    options = $.extend({
         title: title,
         buttons: buttons,
         modal: true,
         resizable: true,
         width: 500,
         close: function(event, ui) { $(this).remove(); }
-      }, options || {}));
+      }, options || {});
+
+    popup.dialog(options);
 
     // resize and center popup
     var win = $(window), w = win.width(), h = win.height(),
@@ -6564,6 +6595,11 @@
     popup.dialog('option', {
       height: Math.min(h - 40, height + 75 + (buttons ? 50 : 0)),
       width: Math.min(w - 20, width + 36)
+    });
+
+    // assign special classes to dialog buttons
+    $.each(options.button_classes || [], function(i, v) {
+      if (v) $($('.ui-dialog-buttonpane button.ui-button', popup.parent()).get(i)).addClass(v);
     });
 
     return popup;
@@ -6702,7 +6738,7 @@
   this.set_quota = function(content)
   {
     if (this.gui_objects.quotadisplay && content && content.type == 'text')
-      $(this.gui_objects.quotadisplay).html(content.percent+'%').attr('title', content.title);
+      $(this.gui_objects.quotadisplay).text((content.percent||0) + '%').attr('title', content.title);
 
     this.triggerEvent('setquota', content);
     this.env.quota_content = content;
@@ -7869,12 +7905,24 @@
   };
 
   // get window.opener.rcmail if available
-  this.opener = function()
+  this.opener = function(deep, filter)
   {
+    var i, win = window.opener;
+
     // catch Error: Permission denied to access property rcmail
     try {
-      if (window.opener && !opener.closed && opener.rcmail)
-        return opener.rcmail;
+      if (win && !win.closed) {
+        // try parent of the opener window, e.g. preview frame
+        if (deep && (!win.rcmail || win.rcmail.env.framed) && win.parent && win.parent.rcmail)
+          win = win.parent;
+
+        if (win.rcmail && filter)
+          for (i in filter)
+            if (win.rcmail.env[i] != filter[i])
+              return;
+
+        return win.rcmail;
+      }
     }
     catch (e) {}
   };
@@ -8028,7 +8076,7 @@
 
     img.onload = function() { ref.env.browser_capabilities.tif = 1; };
     img.onerror = function() { ref.env.browser_capabilities.tif = 0; };
-    img.src = 'program/resources/blank.tif';
+    img.src = this.assets_path('program/resources/blank.tif');
   };
 
   this.pdf_support_check = function()
@@ -8041,7 +8089,7 @@
     if (plugin && plugin.enabledPlugin)
         return 1;
 
-    if (window.ActiveXObject) {
+    if ('ActiveXObject' in window) {
       try {
         if (plugin = new ActiveXObject("AcroPDF.PDF"))
           return 1;
@@ -8074,7 +8122,7 @@
     if (plugin && plugin.enabledPlugin)
         return 1;
 
-    if (window.ActiveXObject) {
+    if ('ActiveXObject' in window) {
       try {
         if (plugin = new ActiveXObject("ShockwaveFlash.ShockwaveFlash"))
           return 1;
@@ -8083,6 +8131,15 @@
     }
 
     return 0;
+  };
+
+  this.assets_path = function(path)
+  {
+    if (this.env.assets_path && !path.startsWith(this.env.assets_path)) {
+      path = this.env.assets_path + path;
+    }
+
+    return path;
   };
 
   // Cookie setter
@@ -8102,15 +8159,16 @@
   // wrapper for localStorage.getItem(key)
   this.local_storage_get_item = function(key, deflt, encrypted)
   {
-    var item;
+    var item, result;
 
     // TODO: add encryption
     try {
       item = localStorage.getItem(this.get_local_storage_prefix() + key);
+      result = JSON.parse(item);
     }
     catch (e) { }
 
-    return item !== null ? JSON.parse(item) : (deflt || null);
+    return result || deflt || null;
   };
 
   // wrapper for localStorage.setItem(key, data)
@@ -8149,7 +8207,7 @@
   if (!elem.title) {
     var $elem = $(elem);
     if ($elem.width() + (indent || 0) * 15 > $elem.parent().width())
-      elem.title = $elem.text();
+      elem.title = rcube_webmail.subject_text(elem);
   }
 };
 
@@ -8166,10 +8224,17 @@
 
     tmp.remove();
     if (w + $('span.branch', $elem).width() * 15 > $elem.width())
-      elem.title = txt;
+      elem.title = rcube_webmail.subject_text(elem);
   }
 };
 
+rcube_webmail.subject_text = function(elem)
+{
+  var t = $(elem).clone();
+  t.find('.skip-on-drag').remove();
+  return t.text();
+};
+
 rcube_webmail.prototype.get_cookie = getCookie;
 
 // copy event engine prototype

--
Gitblit v1.9.1