From 921408eba600a7dc51271c35480e9114cac5ecec Mon Sep 17 00:00:00 2001
From: defa <defa@so36.net>
Date: Wed, 05 Sep 2012 08:48:55 -0400
Subject: [PATCH] Merge https://github.com/roundcube/roundcubemail into crypt_hash_branch

---
 plugins/managesieve/tests/src/parser_include    |    7 
 plugins/managesieve/tests/src/parser_relational |    6 
 plugins/password/drivers/virtualmin.php         |    4 
 plugins/acl/acl.php                             |    3 
 plugins/managesieve/tests/src/parser_prefix     |    5 
 skins/larry/ie7hacks.css                        |    6 
 skins/larry/addressbook.css                     |    1 
 program/include/rcube_charset.php               |    6 
 skins/larry/templates/messagepreview.html       |    5 
 tests/MailFunc.php                              |    2 
 skins/larry/images/contactpic_48px.png          |    0 
 CHANGELOG                                       |    8 
 plugins/acl/skins/larry/templates/table.html    |    8 
 skins/larry/templates/message.html              |   63 ++-
 plugins/managesieve/tests/src/parser_vacation   |   12 
 skins/larry/svggradients.css                    |    2 
 program/steps/mail/headers.inc                  |    3 
 plugins/managesieve/tests/src/parser_kep14.out  |    3 
 skins/larry/templates/messageerror.html         |   10 
 program/include/rcube_output_html.php           |   31 ++
 program/steps/settings/save_folder.inc          |    5 
 program/js/app.js                               |   95 +++--
 skins/larry/mail.css                            |   90 ++---
 tests/Framework/Shared.php                      |   43 ++
 plugins/managesieve/tests/src/parser_variables  |   12 
 plugins/acl/skins/larry/acl.css                 |    4 
 program/include/rcube_shared.inc                |    2 
 plugins/managesieve/tests/src/parser            |   52 +++
 tests/HtmlToText.php                            |    4 
 plugins/managesieve/tests/src/parser.out        |   52 +++
 plugins/managesieve/tests/src/parser_imapflags  |    7 
 program/steps/mail/func.inc                     |   13 
 tests/phpunit.xml                               |    4 
 program/include/rcube_storage.php               |    2 
 skins/classic/templates/message.html            |   15 
 plugins/managesieve/tests/src/parser_kep14      |    2 
 program/js/list.js                              |    4 
 plugins/managesieve/tests/src/parser_body       |   17 +
 plugins/managesieve/tests/Parser.php            |   54 +++
 skins/classic/templates/messageerror.html       |   15 
 program/steps/settings/folders.inc              |    4 
 plugins/managesieve/tests/src/parser_subaddress |   11 
 skins/larry/images/contactpic_32px.png          |    0 
 program/include/rcube_message.php               |   26 +
 skins/larry/iehacks.css                         |    2 
 skins/larry/svggradient.php                     |    2 
 /dev/null                                       |   66 ----
 plugins/managesieve/tests/Tokenizer.php         |   33 ++
 program/include/rcube_imap.php                  |    4 
 skins/larry/styles.css                          |    6 
 program/include/rcube_imap_generic.php          |    5 
 skins/larry/ui.js                               |   56 ++-
 52 files changed, 626 insertions(+), 266 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index 8e9dcb3..0706009 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,14 @@
 CHANGELOG Roundcube Webmail
 ===========================
 
+- Fix focus issue in IE when selecting message row (#1488620)
+- Remove (too big) min-width on mail screen
+- Add full headers view in message preview window (#1488538)
+- Fix message display page issues - unified with message preview (#1488590, #1488642)
+- Fix displaying all headers when they contain malformed characters (#1488666)
+- Fix decoding of HTML messages with UTF-16 charset specified (#1488654)
+- Fix quota capability detection so it can be overwritten by a plugin (#1488655)
+- Added template object 'frame'
 - Fix identity selection on reply (#1488101)
 - Add option to enable HTML editor on forwarding (#1488517)
 - Add option to not include original message on reply, rename option top_posting to reply_mode (#1485149)
diff --git a/plugins/acl/acl.php b/plugins/acl/acl.php
index 1442504..1952dad 100644
--- a/plugins/acl/acl.php
+++ b/plugins/acl/acl.php
@@ -233,8 +233,7 @@
 
         // Advanced rights
         $attrib['id'] = 'advancedrights';
-        foreach ($supported as $val) {
-            $id = "acl$val";
+        foreach ($supported as $idx => $val) {
             $ul .= html::tag('li', null,
                 $input->show('', array(
                     'name' => "acl[$val]", 'value' => $val, 'id' => $id))
diff --git a/plugins/acl/skins/larry/acl.css b/plugins/acl/skins/larry/acl.css
index 5e2448e..e392a26 100644
--- a/plugins/acl/skins/larry/acl.css
+++ b/plugins/acl/skins/larry/acl.css
@@ -123,3 +123,7 @@
 {
   margin-left: 0.5em;
 }
+
+ul.toolbarmenu li span.delete {
+  background-position: 0 -1509px;
+}
diff --git a/plugins/acl/skins/larry/templates/table.html b/plugins/acl/skins/larry/templates/table.html
index 7f99f6f..3cf8292 100644
--- a/plugins/acl/skins/larry/templates/table.html
+++ b/plugins/acl/skins/larry/templates/table.html
@@ -3,14 +3,14 @@
     <roundcube:object name="acltable" id="acltable" class="records-table" />
 </div>
 <div id="acllist-footer" class="boxfooter">
-    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu');return false" innerClass="inner" content="&#9881;" />
+    <roundcube:button command="acl-create" id="aclcreatelink" type="link" title="acl.newuser" class="listbutton add disabled" classAct="listbutton add" innerClass="inner" content="+" /><roundcube:button name="aclmenulink" id="aclmenulink" type="link" title="acl.actions" class="listbutton groupactions"onclick="UI.show_popup('aclmenu', undefined, {above:1});return false" innerClass="inner" content="&#9881;" />
 </div>
 </div>
 
 <div id="aclmenu" class="popupmenu">
-    <ul class="toolbarmenu selectable">
-        <li><roundcube:button command="acl-edit" label="edit" classAct="active" /></li>
-        <li><roundcube:button command="acl-delete" label="delete" classAct="active" /></li>
+    <ul class="toolbarmenu selectable iconized">
+        <li><roundcube:button command="acl-edit" label="edit" class="icon" classAct="icon active" innerclass="icon edit" /></li>
+        <li><roundcube:button command="acl-delete" label="delete" class="icon" classAct="icon active" innerclass="icon delete" /></li>
         <roundcube:if condition="!in_array('acl_advanced_mode', (array)config:dont_override)" />
             <li><roundcube:button name="acl-switch" id="acl-switch" label="acl.advanced" onclick="rcmail.command('acl-mode-switch')" class="active" /></li>
         <roundcube:endif />
diff --git a/plugins/managesieve/tests/Makefile b/plugins/managesieve/tests/Makefile
deleted file mode 100644
index 072be2f..0000000
--- a/plugins/managesieve/tests/Makefile
+++ /dev/null
@@ -1,7 +0,0 @@
-
-clean:
-	rm -f *.log *.php *.diff *.exp *.out
-
-
-test:
-	pear run-tests *.phpt
diff --git a/plugins/managesieve/tests/Parser.php b/plugins/managesieve/tests/Parser.php
new file mode 100644
index 0000000..00915cc
--- /dev/null
+++ b/plugins/managesieve/tests/Parser.php
@@ -0,0 +1,54 @@
+<?php
+
+class Parser extends PHPUnit_Framework_TestCase
+{
+
+    function setUp()
+    {
+        include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php';
+    }
+
+    /**
+     * Sieve script parsing
+     *
+     * @dataProvider data_parser
+     */
+    function test_parser($input, $output, $message)
+    {
+        $script = new rcube_sieve_script($input);
+        $result = $script->as_text();
+
+        $this->assertEquals(trim($result), trim($output), $message);
+    }
+
+    /**
+     * Data provider for test_parser()
+     */
+    function data_parser()
+    {
+        $dir_path = realpath(dirname(__FILE__) . '/src');
+        $dir      = opendir($dir_path);
+        $result   = array();
+
+        while ($file = readdir($dir)) {
+            if (preg_match('/^[a-z0-9_]+$/', $file)) {
+                $input = file_get_contents($dir_path . '/' . $file);
+
+                if (file_exists($dir_path . '/' . $file . '.out')) {
+                    $output = file_get_contents($dir_path . '/' . $file . '.out');
+                }
+                else {
+                    $output = $input;
+                }
+
+                $result[] = array(
+                    'input'   => $input,
+                    'output'  => $output,
+                    'message' => "Error in parsing '$file' file",
+                );
+            }
+        }
+
+        return $result;
+    }
+}
diff --git a/plugins/managesieve/tests/Tokenizer.php b/plugins/managesieve/tests/Tokenizer.php
new file mode 100644
index 0000000..8c0bced
--- /dev/null
+++ b/plugins/managesieve/tests/Tokenizer.php
@@ -0,0 +1,33 @@
+<?php
+
+class Tokenizer extends PHPUnit_Framework_TestCase
+{
+
+    function setUp()
+    {
+        include_once dirname(__FILE__) . '/../lib/rcube_sieve_script.php';
+    }
+
+    function data_tokenizer()
+    {
+        return array(
+            array(1, "text: #test\nThis is test ; message;\nMulti line\n.\n;\n", '"This is test ; message;\nMulti line"'),
+            array(0, '["test1","test2"]', '[["test1","test2"]]'),
+            array(1, '["test"]', '["test"]'),
+            array(1, '"te\\"st"', '"te\\"st"'),
+            array(0, 'test #comment', '["test"]'),
+            array(0, "text:\ntest\n.\ntext:\ntest\n.\n", '["test","test"]'),
+            array(1, '"\\a\\\\\\"a"', '"a\\\\\\"a"'),
+        );
+    }
+
+    /**
+     * @dataProvider data_tokenizer
+     */
+    function test_tokenizer($num, $input, $output)
+    {
+        $res = json_encode(rcube_sieve_script::tokenize($input, $num));
+
+        $this->assertEquals(trim($res), trim($output));
+    }
+}
diff --git a/plugins/managesieve/tests/parser.phpt b/plugins/managesieve/tests/parser.phpt
deleted file mode 100644
index aec0421..0000000
--- a/plugins/managesieve/tests/parser.phpt
+++ /dev/null
@@ -1,120 +0,0 @@
---TEST--
-Main test of script parser
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["fileinto","reject","envelope"];
-# rule:[spam]
-if anyof (header :contains "X-DSPAM-Result" "Spam")
-{
-	fileinto "Spam";
-	stop;
-}
-# rule:[test1]
-if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld")
-{
-	discard;
-	stop;
-}
-# rule:[test2]
-if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
-{
-	fileinto "test";
-	stop;
-}
-# rule:[comments]
-if anyof (true) /* comment
- * "comment" #comment */ {
-    /* comment */ stop;
-# comment
-}
-# rule:[reject]
-if size :over 5000K {
-	reject "Message over 5MB size limit. Please contact me before sending this.";
-}
-# rule:[false]
-if false # size :over 5000K
-{
-	stop; /* rule disabled */
-}
-# rule:[true]
-if true
-{
-	stop;
-}
-fileinto "Test";
-# rule:[address test]
-if address :all :is "From" "nagios@domain.tld"
-{
-	fileinto "domain.tld";
-	stop;
-}
-# rule:[envelope test]
-if envelope :domain :is "From" "domain.tld"
-{
-	fileinto "domain.tld";
-	stop;
-}
-';
-
-$s = new rcube_sieve_script($txt);
-echo $s->as_text();
-
-// -------------------------------------------------------------------------------
-?>
---EXPECT--
-require ["fileinto","reject","envelope"];
-# rule:[spam]
-if header :contains "X-DSPAM-Result" "Spam"
-{
-	fileinto "Spam";
-	stop;
-}
-# rule:[test1]
-if header :contains ["From","To"] "test@domain.tld"
-{
-	discard;
-	stop;
-}
-# rule:[test2]
-if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]")
-{
-	fileinto "test";
-	stop;
-}
-# rule:[comments]
-if true
-{
-	stop;
-}
-# rule:[reject]
-if size :over 5000K
-{
-	reject "Message over 5MB size limit. Please contact me before sending this.";
-}
-# rule:[false]
-if false # size :over 5000K
-{
-	stop;
-}
-# rule:[true]
-if true
-{
-	stop;
-}
-fileinto "Test";
-# rule:[address test]
-if address :all :is "From" "nagios@domain.tld"
-{
-	fileinto "domain.tld";
-	stop;
-}
-# rule:[envelope test]
-if envelope :domain :is "From" "domain.tld"
-{
-	fileinto "domain.tld";
-	stop;
-}
diff --git a/plugins/managesieve/tests/parser_body.phpt b/plugins/managesieve/tests/parser_body.phpt
deleted file mode 100644
index 08ad549..0000000
--- a/plugins/managesieve/tests/parser_body.phpt
+++ /dev/null
@@ -1,49 +0,0 @@
---TEST--
-Test of Sieve body extension (RFC5173)
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["body","fileinto"];
-if body :raw :contains "MAKE MONEY FAST"
-{
-	stop;
-}
-if body :content "text" :contains ["missile","coordinates"]
-{
-	fileinto "secrets";
-}
-if body :content "audio/mp3" :contains ""
-{
-	fileinto "jukebox";
-}
-if body :text :contains "project schedule"
-{
-	fileinto "project/schedule";
-}
-';
-
-$s = new rcube_sieve_script($txt);
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["body","fileinto"];
-if body :raw :contains "MAKE MONEY FAST"
-{
-	stop;
-}
-if body :content "text" :contains ["missile","coordinates"]
-{
-	fileinto "secrets";
-}
-if body :content "audio/mp3" :contains ""
-{
-	fileinto "jukebox";
-}
-if body :text :contains "project schedule"
-{
-	fileinto "project/schedule";
-}
diff --git a/plugins/managesieve/tests/parser_imapflags.phpt b/plugins/managesieve/tests/parser_imapflags.phpt
deleted file mode 100644
index a4bc465..0000000
--- a/plugins/managesieve/tests/parser_imapflags.phpt
+++ /dev/null
@@ -1,28 +0,0 @@
---TEST--
-Test of Sieve vacation extension (RFC5232)
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["imapflags"];
-# rule:[imapflags]
-if header :matches "Subject" "^Test$" {
-    setflag "\\\\Seen";
-    addflag ["\\\\Answered","\\\\Deleted"];
-}
-';
-
-$s = new rcube_sieve_script($txt, array('imapflags'));
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["imapflags"];
-# rule:[imapflags]
-if header :matches "Subject" "^Test$"
-{
-	setflag "\\Seen";
-	addflag ["\\Answered","\\Deleted"];
-}
diff --git a/plugins/managesieve/tests/parser_include.phpt b/plugins/managesieve/tests/parser_include.phpt
deleted file mode 100644
index addc0d4..0000000
--- a/plugins/managesieve/tests/parser_include.phpt
+++ /dev/null
@@ -1,30 +0,0 @@
---TEST--
-Test of Sieve include extension
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["include"];
-
-include "script.sieve";
-# rule:[two]
-if true
-{
-    include :optional "second.sieve";
-}
-';
-
-$s = new rcube_sieve_script($txt, array(), array('variables'));
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["include"];
-include "script.sieve";
-# rule:[two]
-if true
-{
-	include :optional "second.sieve";
-}
diff --git a/plugins/managesieve/tests/parser_kep14.phpt b/plugins/managesieve/tests/parser_kep14.phpt
deleted file mode 100644
index dcdbd48..0000000
--- a/plugins/managesieve/tests/parser_kep14.phpt
+++ /dev/null
@@ -1,19 +0,0 @@
---TEST--
-Test of Kolab's KEP:14 implementation
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-# EDITOR Roundcube
-# EDITOR_VERSION 123
-';
-
-$s = new rcube_sieve_script($txt, array('body'));
-echo $s->as_text();
-
-?>
---EXPECT--
-# EDITOR Roundcube
-# EDITOR_VERSION 123
diff --git a/plugins/managesieve/tests/parser_prefix.phpt b/plugins/managesieve/tests/parser_prefix.phpt
deleted file mode 100644
index c87e965..0000000
--- a/plugins/managesieve/tests/parser_prefix.phpt
+++ /dev/null
@@ -1,25 +0,0 @@
---TEST--
-Test of prefix comments handling
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-# this is a comment
-# and the second line
-
-require ["variables"];
-set "b" "c";
-';
-
-$s = new rcube_sieve_script($txt, array(), array('variables'));
-echo $s->as_text();
-
-?>
---EXPECT--
-# this is a comment
-# and the second line
-
-require ["variables"];
-set "b" "c";
diff --git a/plugins/managesieve/tests/parser_relational.phpt b/plugins/managesieve/tests/parser_relational.phpt
deleted file mode 100644
index 6b6f29f..0000000
--- a/plugins/managesieve/tests/parser_relational.phpt
+++ /dev/null
@@ -1,25 +0,0 @@
---TEST--
-Test of Sieve relational extension (RFC5231)
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["relational","comparator-i;ascii-numeric"];
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric"
-    ["X-Spam-score"] ["14"] {redirect "test@test.tld";}
-';
-
-$s = new rcube_sieve_script($txt);
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["relational","comparator-i;ascii-numeric"];
-# rule:[redirect]
-if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
-{
-	redirect "test@test.tld";
-}
diff --git a/plugins/managesieve/tests/parser_vacation.phpt b/plugins/managesieve/tests/parser_vacation.phpt
deleted file mode 100644
index a603ff6..0000000
--- a/plugins/managesieve/tests/parser_vacation.phpt
+++ /dev/null
@@ -1,39 +0,0 @@
---TEST--
-Test of Sieve vacation extension (RFC5230)
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["vacation"];
-# rule:[test-vacation]
-if anyof (header :contains "Subject" "vacation")
-{
-	vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
-	stop;
-}
-';
-
-$s = new rcube_sieve_script($txt);
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["vacation"];
-# rule:[test-vacation]
-if header :contains "Subject" "vacation"
-{
-	vacation :days 1 text:
-# test
-test test /* test */
-test
-.
-;
-	stop;
-}
diff --git a/plugins/managesieve/tests/parser_variables.phpt b/plugins/managesieve/tests/parser_variables.phpt
deleted file mode 100644
index cf1f8fc..0000000
--- a/plugins/managesieve/tests/parser_variables.phpt
+++ /dev/null
@@ -1,39 +0,0 @@
---TEST--
-Test of Sieve variables extension
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["variables"];
-set "honorific" "Mr";
-set "vacation" text:
-Dear ${HONORIFIC} ${last_name},
-I am out, please leave a message after the meep.
-.
-;
-set :length "b" "${a}";
-set :lower "b" "${a}";
-set :upperfirst "b" "${a}";
-set :upperfirst :lower "b" "${a}";
-set :quotewildcard "b" "Rock*";
-';
-
-$s = new rcube_sieve_script($txt, array(), array('variables'));
-echo $s->as_text();
-
-?>
---EXPECT--
-require ["variables"];
-set "honorific" "Mr";
-set "vacation" text:
-Dear ${HONORIFIC} ${last_name},
-I am out, please leave a message after the meep.
-.
-;
-set :length "b" "${a}";
-set :lower "b" "${a}";
-set :upperfirst "b" "${a}";
-set :upperfirst :lower "b" "${a}";
-set :quotewildcard "b" "Rock*";
diff --git a/plugins/managesieve/tests/parset_subaddress.phpt b/plugins/managesieve/tests/parset_subaddress.phpt
deleted file mode 100644
index 6d4d03c..0000000
--- a/plugins/managesieve/tests/parset_subaddress.phpt
+++ /dev/null
@@ -1,38 +0,0 @@
---TEST--
-Test of Sieve subaddress extension (RFC5233)
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt = '
-require ["envelope","subaddress","fileinto"];
-if envelope :user "To" "postmaster"
-{
-	fileinto "postmaster";
-	stop;
-}
-if envelope :detail :is "To" "mta-filters"
-{
-	fileinto "mta-filters";
-	stop;
-}
-';
-
-$s = new rcube_sieve_script($txt);
-echo $s->as_text();
-
-// -------------------------------------------------------------------------------
-?>
---EXPECT--
-require ["envelope","subaddress","fileinto"];
-if envelope :user "To" "postmaster"
-{
-	fileinto "postmaster";
-	stop;
-}
-if envelope :detail :is "To" "mta-filters"
-{
-	fileinto "mta-filters";
-	stop;
-}
diff --git a/plugins/managesieve/tests/src/parser b/plugins/managesieve/tests/src/parser
new file mode 100644
index 0000000..9c4717b
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser
@@ -0,0 +1,52 @@
+require ["fileinto","reject","envelope"];
+# rule:[spam]
+if anyof (header :contains "X-DSPAM-Result" "Spam")
+{
+	fileinto "Spam";
+	stop;
+}
+# rule:[test1]
+if anyof (header :comparator "i;ascii-casemap" :contains ["From","To"] "test@domain.tld")
+{
+	discard;
+	stop;
+}
+# rule:[test2]
+if anyof (not header :comparator "i;octet" :contains ["Subject"] "[test]", header :contains "Subject" "[test2]")
+{
+	fileinto "test";
+	stop;
+}
+# rule:[comments]
+if anyof (true) /* comment
+ * "comment" #comment */ {
+    /* comment */ stop;
+# comment
+}
+# rule:[reject]
+if size :over 5000K {
+	reject "Message over 5MB size limit. Please contact me before sending this.";
+}
+# rule:[false]
+if false # size :over 5000K
+{
+	stop; /* rule disabled */
+}
+# rule:[true]
+if true
+{
+	stop;
+}
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+	fileinto "domain.tld";
+	stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+	fileinto "domain.tld";
+	stop;
+}
diff --git a/plugins/managesieve/tests/src/parser.out b/plugins/managesieve/tests/src/parser.out
new file mode 100644
index 0000000..385c889
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser.out
@@ -0,0 +1,52 @@
+require ["fileinto","reject","envelope"];
+# rule:[spam]
+if header :contains "X-DSPAM-Result" "Spam"
+{
+	fileinto "Spam";
+	stop;
+}
+# rule:[test1]
+if header :contains ["From","To"] "test@domain.tld"
+{
+	discard;
+	stop;
+}
+# rule:[test2]
+if anyof (not header :comparator "i;octet" :contains "Subject" "[test]", header :contains "Subject" "[test2]")
+{
+	fileinto "test";
+	stop;
+}
+# rule:[comments]
+if true
+{
+	stop;
+}
+# rule:[reject]
+if size :over 5000K
+{
+	reject "Message over 5MB size limit. Please contact me before sending this.";
+}
+# rule:[false]
+if false # size :over 5000K
+{
+	stop;
+}
+# rule:[true]
+if true
+{
+	stop;
+}
+fileinto "Test";
+# rule:[address test]
+if address :all :is "From" "nagios@domain.tld"
+{
+	fileinto "domain.tld";
+	stop;
+}
+# rule:[envelope test]
+if envelope :domain :is "From" "domain.tld"
+{
+	fileinto "domain.tld";
+	stop;
+}
diff --git a/plugins/managesieve/tests/src/parser_body b/plugins/managesieve/tests/src/parser_body
new file mode 100644
index 0000000..bd142ed
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_body
@@ -0,0 +1,17 @@
+require ["body","fileinto"];
+if body :raw :contains "MAKE MONEY FAST"
+{
+	stop;
+}
+if body :content "text" :contains ["missile","coordinates"]
+{
+	fileinto "secrets";
+}
+if body :content "audio/mp3" :contains ""
+{
+	fileinto "jukebox";
+}
+if body :text :contains "project schedule"
+{
+	fileinto "project/schedule";
+}
diff --git a/plugins/managesieve/tests/src/parser_imapflags b/plugins/managesieve/tests/src/parser_imapflags
new file mode 100644
index 0000000..e67bf7c
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_imapflags
@@ -0,0 +1,7 @@
+require ["imap4flags"];
+# rule:[imapflags]
+if header :matches "Subject" "^Test$"
+{
+	setflag "\\Seen";
+	addflag ["\\Answered","\\Deleted"];
+}
diff --git a/plugins/managesieve/tests/src/parser_include b/plugins/managesieve/tests/src/parser_include
new file mode 100644
index 0000000..b5585a4
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_include
@@ -0,0 +1,7 @@
+require ["include"];
+include "script.sieve";
+# rule:[two]
+if true
+{
+	include :optional "second.sieve";
+}
diff --git a/plugins/managesieve/tests/src/parser_kep14 b/plugins/managesieve/tests/src/parser_kep14
new file mode 100644
index 0000000..1ded8d8
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_kep14
@@ -0,0 +1,2 @@
+# EDITOR Roundcube
+# EDITOR_VERSION 123
diff --git a/plugins/managesieve/tests/src/parser_kep14.out b/plugins/managesieve/tests/src/parser_kep14.out
new file mode 100644
index 0000000..cb7faa7
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_kep14.out
@@ -0,0 +1,3 @@
+require ["variables"];
+set "EDITOR" "Roundcube";
+set "EDITOR_VERSION" "123";
diff --git a/plugins/managesieve/tests/src/parser_prefix b/plugins/managesieve/tests/src/parser_prefix
new file mode 100644
index 0000000..9f6a33a
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_prefix
@@ -0,0 +1,5 @@
+# this is a comment
+# and the second line
+
+require ["variables"];
+set "b" "c";
diff --git a/plugins/managesieve/tests/src/parser_relational b/plugins/managesieve/tests/src/parser_relational
new file mode 100644
index 0000000..0a92fde
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_relational
@@ -0,0 +1,6 @@
+require ["relational","comparator-i;ascii-numeric"];
+# rule:[redirect]
+if header :value "ge" :comparator "i;ascii-numeric" "X-Spam-score" "14"
+{
+	redirect "test@test.tld";
+}
diff --git a/plugins/managesieve/tests/src/parser_subaddress b/plugins/managesieve/tests/src/parser_subaddress
new file mode 100644
index 0000000..f106b79
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_subaddress
@@ -0,0 +1,11 @@
+require ["envelope","subaddress","fileinto"];
+if envelope :user "To" "postmaster"
+{
+	fileinto "postmaster";
+	stop;
+}
+if envelope :detail :is "To" "mta-filters"
+{
+	fileinto "mta-filters";
+	stop;
+}
diff --git a/plugins/managesieve/tests/src/parser_vacation b/plugins/managesieve/tests/src/parser_vacation
new file mode 100644
index 0000000..93026db
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_vacation
@@ -0,0 +1,12 @@
+require ["vacation"];
+# rule:[test-vacation]
+if header :contains "Subject" "vacation"
+{
+	vacation :days 1 text:
+# test
+test test /* test */
+test
+.
+;
+	stop;
+}
diff --git a/plugins/managesieve/tests/src/parser_variables b/plugins/managesieve/tests/src/parser_variables
new file mode 100644
index 0000000..bd5941c
--- /dev/null
+++ b/plugins/managesieve/tests/src/parser_variables
@@ -0,0 +1,12 @@
+require ["variables"];
+set "honorific" "Mr";
+set "vacation" text:
+Dear ${HONORIFIC} ${last_name},
+I am out, please leave a message after the meep.
+.
+;
+set :length "b" "${a}";
+set :lower "b" "${a}";
+set :upperfirst "b" "${a}";
+set :upperfirst :lower "b" "${a}";
+set :quotewildcard "b" "Rock*";
diff --git a/plugins/managesieve/tests/tokenize.phpt b/plugins/managesieve/tests/tokenize.phpt
deleted file mode 100644
index f988653..0000000
--- a/plugins/managesieve/tests/tokenize.phpt
+++ /dev/null
@@ -1,66 +0,0 @@
---TEST--
-Script parsing: tokenizer
---SKIPIF--
---FILE--
-<?php
-include '../lib/rcube_sieve_script.php';
-
-$txt[1] = array(1, 'text: #test
-This is test ; message;
-Multi line
-.
-;
-');
-$txt[2] = array(0, '["test1","test2"]');
-$txt[3] = array(1, '["test"]');
-$txt[4] = array(1, '"te\\"st"');
-$txt[5] = array(0, 'test #comment');
-$txt[6] = array(0, 'text:
-test
-.
-text:
-test
-.
-');
-$txt[7] = array(1, '"\\a\\\\\\"a"');
-
-foreach ($txt as $idx => $t) {
-    echo "[$idx]---------------\n"; 
-    var_dump(rcube_sieve_script::tokenize($t[1], $t[0]));
-}
-?>
---EXPECT--
-[1]---------------
-string(34) "This is test ; message;
-Multi line"
-[2]---------------
-array(1) {
-  [0]=>
-  array(2) {
-    [0]=>
-    string(5) "test1"
-    [1]=>
-    string(5) "test2"
-  }
-}
-[3]---------------
-array(1) {
-  [0]=>
-  string(4) "test"
-}
-[4]---------------
-string(5) "te"st"
-[5]---------------
-array(1) {
-  [0]=>
-  string(4) "test"
-}
-[6]---------------
-array(2) {
-  [0]=>
-  string(4) "test"
-  [1]=>
-  string(4) "test"
-}
-[7]---------------
-string(4) "a\"a"
diff --git a/plugins/password/drivers/virtualmin.php b/plugins/password/drivers/virtualmin.php
index b2547e0..f9eca96 100644
--- a/plugins/password/drivers/virtualmin.php
+++ b/plugins/password/drivers/virtualmin.php
@@ -48,6 +48,10 @@
             $pieces = explode("_", $username);
             $domain = $pieces[0];
             break;
+		case 8: // domain taken from alias, username left as it was
+			$email = $rcmail->user->data['alias'];
+			$domain = substr(strrchr($email, "@"), 1);
+			break;
         default: // username@domain
             $domain = substr(strrchr($username, "@"), 1);
         }
diff --git a/program/include/rcube_charset.php b/program/include/rcube_charset.php
index 380d149..1740a60 100644
--- a/program/include/rcube_charset.php
+++ b/program/include/rcube_charset.php
@@ -181,6 +181,12 @@
         $to   = empty($to) ? strtoupper(RCMAIL_CHARSET) : self::parse_charset($to);
         $from = self::parse_charset($from);
 
+        // It is a common case when UTF-16 charset is used with US-ASCII content (#1488654)
+        // In that case we can just skip the conversion (use UTF-8)
+        if ($from == 'UTF-16' && !preg_match('/[^\x00-\x7F]/', $str)) {
+            $from = 'UTF-8';
+        }
+
         if ($from == $to || empty($str) || empty($from)) {
             return $str;
         }
diff --git a/program/include/rcube_imap.php b/program/include/rcube_imap.php
index 5dd9c12..66b5c4b 100644
--- a/program/include/rcube_imap.php
+++ b/program/include/rcube_imap.php
@@ -2222,6 +2222,10 @@
             $folder = $this->folder;
         }
 
+        if (!$this->check_connection()) {
+            return false;
+        }
+
         // make sure folder exists
         if ($this->folder_exists($folder)) {
             if ($is_file) {
diff --git a/program/include/rcube_imap_generic.php b/program/include/rcube_imap_generic.php
index 915a11a..c3cfabc 100644
--- a/program/include/rcube_imap_generic.php
+++ b/program/include/rcube_imap_generic.php
@@ -2538,7 +2538,7 @@
     {
         unset($this->data['APPENDUID']);
 
-        if (!$mailbox) {
+        if ($mailbox === null || $mailbox === '') {
             return false;
         }
 
@@ -2603,7 +2603,7 @@
     {
         unset($this->data['APPENDUID']);
 
-        if (!$mailbox) {
+        if ($mailbox === null || $mailbox === '') {
             return false;
         }
 
@@ -2612,6 +2612,7 @@
         if (file_exists(realpath($path))) {
             $in_fp = fopen($path, 'r');
         }
+
         if (!$in_fp) {
             $this->setError(self::ERROR_UNKNOWN, "Couldn't open $path for reading");
             return false;
diff --git a/program/include/rcube_message.php b/program/include/rcube_message.php
index f550b57..6af1d01 100644
--- a/program/include/rcube_message.php
+++ b/program/include/rcube_message.php
@@ -52,7 +52,8 @@
     private $opt = array();
     private $parse_alternative = false;
 
-    public $uid = null;
+    public $uid;
+    public $folder;
     public $headers;
     public $parts = array();
     public $mime_parts = array();
@@ -68,16 +69,21 @@
      *
      * Provide a uid, and parse message structure.
      *
-     * @param string $uid The message UID.
+     * @param string $uid    The message UID.
+     * @param string $folder Folder name
      *
      * @see self::$app, self::$storage, self::$opt, self::$parts
      */
-    function __construct($uid)
+    function __construct($uid, $folder = null)
     {
         $this->uid  = $uid;
         $this->app  = rcube::get_instance();
         $this->storage = $this->app->get_storage();
+        $this->folder  = strlen($folder) ? $folder : $this->storage->get_folder();
         $this->storage->set_options(array('all_headers' => true));
+
+        // Set current folder
+        $this->storage->set_folder($this->folder);
 
         $this->headers = $this->storage->get_message($uid);
 
@@ -179,10 +185,12 @@
                 }
                 return $fp ? true : $part->body;
             }
+
             // get from IMAP
+            $this->storage->set_folder($this->folder);
+
             return $this->storage->get_message_part($this->uid, $mime_id, $part, NULL, $fp, $skip_charset_conv);
-        } else
-            return null;
+        }
     }
 
 
@@ -637,8 +645,10 @@
     function tnef_decode(&$part)
     {
         // @TODO: attachment may be huge, hadle it via file
-        if (!isset($part->body))
+        if (!isset($part->body)) {
+            $this->storage->set_folder($this->folder);
             $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
+        }
 
         $parts = array();
         $tnef = new tnef_decoder;
@@ -673,8 +683,10 @@
     function uu_decode(&$part)
     {
         // @TODO: messages may be huge, hadle body via file
-        if (!isset($part->body))
+        if (!isset($part->body)) {
+            $this->storage->set_folder($this->folder);
             $part->body = $this->storage->get_message_part($this->uid, $part->mime_id, $part);
+        }
 
         $parts = array();
         // FIXME: line length is max.65?
diff --git a/program/include/rcube_output_html.php b/program/include/rcube_output_html.php
index a071ee3..2743e77 100644
--- a/program/include/rcube_output_html.php
+++ b/program/include/rcube_output_html.php
@@ -400,7 +400,7 @@
                 'line' => __LINE__,
                 'file' => __FILE__,
                 'message' => 'Error loading template for '.$realname
-                ), true, true);
+                ), true, $write);
             return false;
         }
 
@@ -696,6 +696,11 @@
                 if ($attrib['name'] || $attrib['command']) {
                     return $this->button($attrib);
                 }
+                break;
+
+            // frame
+            case 'frame':
+                return $this->frame($attrib);
                 break;
 
             // show a label
@@ -1275,6 +1280,30 @@
     }
 
 
+    /**
+     * Returns iframe object, registers some related env variables
+     *
+     * @param array $attrib HTML attributes
+     *
+     * @return string IFRAME element
+     */
+    public function frame($attrib)
+    {
+        if (!$attrib['id']) {
+            $attrib['id'] = 'rcmframe';
+        }
+
+        if (!$attrib['name']) {
+            $attrib['name'] = $attrib['id'];
+        }
+
+        $this->set_env('contentframe', $attrib['id']);
+        $this->set_env('blankpage', $attrib['src'] ? $this->abs_url($attrib['src']) : 'program/resources/blank.gif');
+
+        return html::iframe($attrib);
+    }
+
+
     /*  ************* common functions delivering gui objects **************  */
 
 
diff --git a/program/include/rcube_shared.inc b/program/include/rcube_shared.inc
index 276940c..c15305c 100644
--- a/program/include/rcube_shared.inc
+++ b/program/include/rcube_shared.inc
@@ -422,7 +422,6 @@
 {
     $filename = preg_replace(
         array(
-            '/MDB2_(.+)/',
             '/Mail_(.+)/',
             '/Net_(.+)/',
             '/Auth_(.+)/',
@@ -430,7 +429,6 @@
             '/^utf8$/',
         ),
         array(
-            'Mail/\\1',
             'Mail/\\1',
             'Net/\\1',
             'Auth/\\1',
diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php
index 768a26d..f83e240 100644
--- a/program/include/rcube_storage.php
+++ b/program/include/rcube_storage.php
@@ -195,7 +195,7 @@
      */
     public function set_folder($folder)
     {
-        if ($this->folder == $folder) {
+        if ($this->folder === $folder) {
             return;
         }
 
diff --git a/program/js/app.js b/program/js/app.js
index 838f240..48de217 100644
--- a/program/js/app.js
+++ b/program/js/app.js
@@ -1538,14 +1538,17 @@
     if (list.multi_selecting || !this.env.contentframe)
       return;
 
-    if (list.get_single_selection() && window.frames && window.frames[this.env.contentframe]) {
-      if (window.frames[this.env.contentframe].location.href.indexOf(this.env.blankpage)>=0) {
-        if (this.preview_timer)
-          clearTimeout(this.preview_timer);
-        if (this.preview_read_timer)
-          clearTimeout(this.preview_read_timer);
-        this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200);
-      }
+    if (list.get_single_selection())
+      return;
+
+    var win = this.get_frame_window(this.env.contentframe);
+
+    if (win && win.location.href.indexOf(this.env.blankpage)>=0) {
+      if (this.preview_timer)
+        clearTimeout(this.preview_timer);
+      if (this.preview_read_timer)
+        clearTimeout(this.preview_read_timer);
+      this.preview_timer = setTimeout(function(){ ref.msglist_get_preview(); }, 200);
     }
   };
 
@@ -1910,12 +1913,12 @@
     if (!id)
       return;
 
-    var target = window,
+    var win, target = window,
       action = preview ? 'preview': 'show',
       url = '&_action='+action+'&_uid='+id+'&_mbox='+urlencode(this.env.mailbox);
 
-    if (preview && this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
-      target = window.frames[this.env.contentframe];
+    if (preview && (win = this.get_frame_window(this.env.contentframe))) {
+      target = win;
       url += '&_framed=1';
     }
 
@@ -1952,18 +1955,35 @@
 
   this.show_contentframe = function(show)
   {
-    var frm, win;
-    if (this.env.contentframe && (frm = $('#'+this.env.contentframe)) && frm.length) {
-      if (!show && (win = window.frames[this.env.contentframe])) {
+    var frame, win, name = this.env.contentframe;
+
+    if (name && (frame = this.get_frame_element(name))) {
+      if (!show && (win = this.get_frame_window(name))) {
         if (win.location && win.location.href.indexOf(this.env.blankpage)<0)
           win.location.href = this.env.blankpage;
       }
       else if (!bw.safari && !bw.konq)
-        frm[show ? 'show' : 'hide']();
-      }
+        $(frame)[show ? 'show' : 'hide']();
+    }
 
     if (!show && this.busy)
       this.set_busy(false, null, this.env.frame_lock);
+  };
+
+  this.get_frame_element = function(id)
+  {
+    var frame;
+
+    if (id && (frame = document.getElementById(id)))
+      return frame;
+  };
+
+  this.get_frame_window = function(id)
+  {
+    var frame = this.get_frame_element(id);
+
+    if (frame && frame.name && window.frames)
+      return window.frames[frame.name];
   };
 
   this.lock_frame = function()
@@ -2009,7 +2029,7 @@
   // list messages of a specific mailbox
   this.list_mailbox = function(mbox, page, sort, url)
   {
-    var target = window;
+    var win, target = window;
 
     if (typeof url != 'object')
       url = {};
@@ -2048,8 +2068,8 @@
       return;
     }
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
-      target = window.frames[this.env.contentframe];
+    if (win = this.get_frame_window(this.env.contentframe)) {
+      target = win;
       url._framed = 1;
     }
 
@@ -4015,7 +4035,7 @@
 
   this.list_contacts = function(src, group, page)
   {
-    var folder, url = {},
+    var win, folder, url = {},
       target = window;
 
     if (!src)
@@ -4047,8 +4067,8 @@
       return;
     }
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
-      target = window.frames[this.env.contentframe];
+    if (win = this.get_frame_window(this.env.contentframe)) {
+      target = win;
       url._framed = 1;
     }
 
@@ -4104,11 +4124,11 @@
   // load contact record
   this.load_contact = function(cid, action, framed)
   {
-    var url = {}, target = window;
+    var win, url = {}, target = window;
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+    if (win = this.get_frame_window(this.env.contentframe)) {
       url._framed = 1;
-      target = window.frames[this.env.contentframe];
+      target = win;
       this.show_contentframe(true);
 
       // load dummy content
@@ -4726,11 +4746,11 @@
   // load advanced search page
   this.advanced_search = function()
   {
-    var url = {_form: 1, _action: 'search'}, target = window;
+    var win, url = {_form: 1, _action: 'search'}, target = window;
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+    if (win = this.get_frame_window(this.env.contentframe)) {
       url._framed = 1;
-      target = window.frames[this.env.contentframe];
+      target = win;
       this.contact_list.clear_selection();
     }
 
@@ -4852,13 +4872,13 @@
   // preferences section select and load options frame
   this.section_select = function(list)
   {
-    var id = list.get_single_selection(), target = window,
+    var win, id = list.get_single_selection(), target = window,
       url = {_action: 'edit-prefs', _section: id};
 
     if (id) {
-      if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+      if (win = this.get_frame_window(this.env.contentframe)) {
         url._framed = 1;
-        target = window.frames[this.env.contentframe];
+        target = win;
       }
       this.location_href(url, target, true);
     }
@@ -4881,13 +4901,12 @@
     if (action == 'edit-identity' && (!id || id == this.env.iid))
       return false;
 
-    var target = window,
+    var win, target = window,
       url = {_action: action, _iid: id};
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
+    if (win = this.get_frame_window(this.env.contentframe)) {
       url._framed = 1;
-      target = window.frames[this.env.contentframe];
-      document.getElementById(this.env.contentframe).style.visibility = 'inherit';
+      target = win;
     }
 
     if (action && (id || action == 'add-identity')) {
@@ -5263,14 +5282,14 @@
   // when user select a folder in manager
   this.show_folder = function(folder, path, force)
   {
-    var target = window,
+    var win, target = window,
       url = '&_action=edit-folder&_mbox='+urlencode(folder);
 
     if (path)
       url += '&_path='+urlencode(path);
 
-    if (this.env.contentframe && window.frames && window.frames[this.env.contentframe]) {
-      target = window.frames[this.env.contentframe];
+    if (win = this.get_frame_window(this.env.contentframe)) {
+      target = win;
       url += '&_framed=1';
     }
 
diff --git a/program/js/list.js b/program/js/list.js
index e84124b..1457382 100644
--- a/program/js/list.js
+++ b/program/js/list.js
@@ -231,8 +231,8 @@
     }
   }
 
-  // Un-focus already focused elements
-  $(document.activeElement).blur();
+  // Un-focus already focused elements (#1487123, #1487316, #1488600, #1488620)
+  $(':focus:not(body)').blur();
   $('iframe').each(function() { this.blur(); });
 
   if (e || (e = window.event))
diff --git a/program/steps/mail/func.inc b/program/steps/mail/func.inc
index 7f0b4db..8bf80a6 100644
--- a/program/steps/mail/func.inc
+++ b/program/steps/mail/func.inc
@@ -90,11 +90,13 @@
   // set current mailbox and some other vars in client environment
   $OUTPUT->set_env('mailbox', $mbox_name);
   $OUTPUT->set_env('pagesize', $RCMAIL->storage->get_pagesize());
-  $OUTPUT->set_env('quota', $RCMAIL->storage->get_capability('QUOTA'));
   $OUTPUT->set_env('delimiter', $RCMAIL->storage->get_hierarchy_delimiter());
   $OUTPUT->set_env('threading', $threading);
   $OUTPUT->set_env('threads', $threading || $RCMAIL->storage->get_capability('THREAD'));
   $OUTPUT->set_env('preview_pane_mark_read', $RCMAIL->config->get('preview_pane_mark_read', 0));
+  if ($RCMAIL->storage->get_capability('QUOTA')) {
+    $OUTPUT->set_env('quota', true);
+  }
 
   if ($CONFIG['delete_junk'])
     $OUTPUT->set_env('delete_junk', true);
@@ -1053,12 +1055,17 @@
   global $OUTPUT;
 
   $html = html::div(array('id' => "all-headers", 'class' => "all", 'style' => 'display:none'), html::div(array('id' => 'headers-source'), ''));
-  $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), '');
+
+  if (!get_boolean($attrib['no-switch'])) {
+    $html .= html::div(array('class' => "more-headers show-headers", 'onclick' => "return ".JS_OBJECT_NAME.".command('show-headers','',this)"), '');
+  }
+
+  unset($attrib['no-switch']);
 
   $OUTPUT->add_gui_object('all_headers_row', 'all-headers');
   $OUTPUT->add_gui_object('all_headers_box', 'headers-source');
 
-  return html::div($attrib, $html);
+  return count($attrib) > 1 ? html::div($attrib, $html) : $html;
 }
 
 
diff --git a/program/steps/mail/headers.inc b/program/steps/mail/headers.inc
index 4d66273..cad113f 100644
--- a/program/steps/mail/headers.inc
+++ b/program/steps/mail/headers.inc
@@ -24,7 +24,8 @@
     $source = $RCMAIL->storage->get_raw_headers($uid);
 
     if ($source !== false) {
-        $source = htmlspecialchars(trim($source));
+        $source = trim(rcube_charset::clean($source));
+        $source = htmlspecialchars($source);
         $source = preg_replace(
             array(
                 '/\n[\t\s]+/',
diff --git a/program/steps/settings/folders.inc b/program/steps/settings/folders.inc
index 2c2cbdc..6ca7049 100644
--- a/program/steps/settings/folders.inc
+++ b/program/steps/settings/folders.inc
@@ -411,8 +411,10 @@
 
 $OUTPUT->set_pagetitle(rcube_label('folders'));
 $OUTPUT->include_script('list.js');
-$OUTPUT->set_env('quota', $STORAGE->get_capability('QUOTA'));
 $OUTPUT->set_env('prefix_ns', $STORAGE->get_namespace('prefix'));
+if ($STORAGE->get_capability('QUOTA')) {
+    $OUTPUT->set_env('quota', true);
+}
 
 // add some labels to client
 $OUTPUT->add_label('deletefolderconfirm', 'purgefolderconfirm', 'folderdeleting',
diff --git a/program/steps/settings/save_folder.inc b/program/steps/settings/save_folder.inc
index 09f76ac..73cc5e4 100644
--- a/program/steps/settings/save_folder.inc
+++ b/program/steps/settings/save_folder.inc
@@ -80,7 +80,10 @@
     }
 }
 
-if (!$error) {
+if ($error) {
+    $OUTPUT->command('display_message', $error, 'error');
+}
+else {
     $folder['name']     = $name_imap;
     $folder['oldname']  = $old_imap;
     $folder['class']    = '';
diff --git a/skins/classic/templates/message.html b/skins/classic/templates/message.html
index 714540b..c03376e 100644
--- a/skins/classic/templates/message.html
+++ b/skins/classic/templates/message.html
@@ -23,11 +23,9 @@
 <div id="mailboxlist-container">
 <div id="mailboxlist-title" class="boxtitle"><roundcube:label name="mailboxlist" /></div>
 <div class="boxlistcontent">
-<roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" />
+    <roundcube:object name="mailboxlist" id="mailboxlist" maxlength="25" />
 </div>
-<div class="boxfooter">
-  <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " />
-</div>
+<div class="boxfooter"></div>
 </div>
 </div>
 
@@ -56,15 +54,6 @@
     var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165});
     rcmail.add_onload('mailviewsplitv.init()');
 </script>
-
-<div id="mailboxoptionsmenu" class="popupmenu">
-  <ul>
-    <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
-    <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
-    <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
-    <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
-  </ul>
-</div>
 
 </body>
 </html>
diff --git a/skins/classic/templates/messageerror.html b/skins/classic/templates/messageerror.html
index 9af45f4..918e309 100644
--- a/skins/classic/templates/messageerror.html
+++ b/skins/classic/templates/messageerror.html
@@ -42,11 +42,9 @@
 <div id="mailboxlist-container">
 <div class="boxtitle"><roundcube:label name="mailboxlist" /></div>
 <div class="boxlistcontent">
-<roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" />
+    <roundcube:object name="mailboxlist" id="mailboxlist" folder_filter="mail" />
 </div>
-<div class="boxfooter">
-  <roundcube:button name="mailboxmenulink" id="mailboxmenulink" type="link" title="folderactions" class="button groupactions" onclick="rcmail_ui.show_popup('mailboxmenu');return false" content=" " />
-</div>
+<div class="boxfooter"></div>
 </div>
 </div>
 
@@ -62,15 +60,6 @@
     var mailviewsplitv = new rcube_splitter({id:'mailviewsplitterv', p1: 'mailboxlist-container', p2: 'messageframe', orientation: 'v', relative: true, start: 165});
     rcmail.add_onload('mailviewsplitv.init()');
 </script>
-
-<div id="mailboxoptionsmenu" class="popupmenu">
-  <ul>
-    <li><roundcube:button command="expunge" type="link" label="compact" classAct="active" /></li>
-    <li class="separator_below"><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
-    <li><roundcube:button command="folders" task="settings" type="link" label="managefolders" classAct="active" /></li>
-    <roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
-  </ul>
-</div>
 
 </body>
 <roundcube:endif />
diff --git a/skins/larry/addressbook.css b/skins/larry/addressbook.css
index fe087ae..74bc0d7 100644
--- a/skins/larry/addressbook.css
+++ b/skins/larry/addressbook.css
@@ -34,7 +34,6 @@
 	position: absolute;
 	top: -6px;
 	left: 0;
-	right: 260px;
 	height: 40px;
 	white-space: nowrap;
 	z-index: 10;
diff --git a/skins/larry/ie7hacks.css b/skins/larry/ie7hacks.css
index 024c35b..935a504 100644
--- a/skins/larry/ie7hacks.css
+++ b/skins/larry/ie7hacks.css
@@ -29,7 +29,7 @@
 .boxfooter .listbutton .inner,
 .attachmentslist li a.delete,
 .attachmentslist li a.cancelupload,
-#messagepreviewheader .iconlink {
+#messageheader .iconlink {
 	/* workaround for text-indent which also offsets the background image */
 	text-indent: 0;
 	font-size: 0;
@@ -45,7 +45,7 @@
 
 .pagenav a.button,
 .pagenav a.button span.inner,
-#messagepreviewheader .iconlink,
+#messageheader .iconlink,
 #uploadform a.iconlink {
 	display: inline;
 }
@@ -67,7 +67,7 @@
 	text-align: left;
 }
 
-#messagepreviewheader .iconlink {
+#messageheader .iconlink {
 	color: #fff;
 	height: 14px;
 }
diff --git a/skins/larry/iehacks.css b/skins/larry/iehacks.css
index 2882021..bba93dc 100644
--- a/skins/larry/iehacks.css
+++ b/skins/larry/iehacks.css
@@ -143,7 +143,7 @@
 	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#005d76', endColorstr='#004558', GradientType=0);
 }
 
-#messageheader, #partheader, #composeheaders {
+#partheader, #composeheaders {
 	filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#e9e9e9', GradientType=0);
 }
 
diff --git a/skins/larry/images/contactpic_32px.png b/skins/larry/images/contactpic_32px.png
index 276f197..25a8141 100644
--- a/skins/larry/images/contactpic_32px.png
+++ b/skins/larry/images/contactpic_32px.png
Binary files differ
diff --git a/skins/larry/images/contactpic_48px.png b/skins/larry/images/contactpic_48px.png
new file mode 100644
index 0000000..9cd3bce
--- /dev/null
+++ b/skins/larry/images/contactpic_48px.png
Binary files differ
diff --git a/skins/larry/mail.css b/skins/larry/mail.css
index 4fff243..496cbbd 100644
--- a/skins/larry/mail.css
+++ b/skins/larry/mail.css
@@ -38,16 +38,16 @@
 	bottom: 28px;
 }
 
-#mailview-top.fullheight {
-	border-radius: 4px 4px 0 0;
-}
-
 #mailview-bottom {
 	position: absolute;
 	left: 0;
 	bottom: 0;
 	width: 100%;
 	height: 26px;
+}
+
+#mailview-top.fullheight {
+	border-radius: 4px 4px 0 0;
 }
 
 #folderlist-header {
@@ -341,7 +341,6 @@
 #messagetoolbar {
 	position: absolute;
 	top: -6px;
-	right: 390px;
 	left: 0;
 	height: 40px;
 	white-space: nowrap;
@@ -362,7 +361,7 @@
 	position: absolute;
 	right: 0;
 	top: 0;
-	width: 240px;
+	width: 400px;
 }
 
 #mailpreviewtoggle {
@@ -383,11 +382,7 @@
 /*** message list ***/
 
 #messagelist thead td:first-child {
-	border-radius: 4px 0 0 0;
-}
-
-#messagelist thead td:last-child {
-	border-radius: 0 4px 0 0;
+	border-radius: 4px 0 0 0; /* for Chrome */
 }
 
 #messagelist tr td.attachment,
@@ -680,15 +675,14 @@
 
 #messagecontent {
 	position: absolute;
-	top: 140px;
+	top: 0;
 	left: 0;
 	width: 100%;
-	bottom: 0;
+	bottom: 28px;
 	overflow: auto;
 	border-radius: 4px 4px 0 0;
 }
 
-#messageheader,
 #partheader,
 #composeheaders {
 	position: relative;
@@ -712,7 +706,7 @@
 
 h3.subject {
 	font-size: 14px;
-	margin: 0 8em 0 0;
+	margin: 0 13em 0 0;
 	padding: 8px 8px 4px 8px;
 	white-space: nowrap;
 	overflow: hidden;
@@ -787,6 +781,7 @@
 	background: -ms-linear-gradient(left, #fbfbfb 0, #e9e9e9 100%);
 	background: linear-gradient(left, #fbfbfb 0, #e9e9e9 100%);
 	border-right: 1px solid #dfdfdf;
+	border-radius: 3px 0 0 0; /* for Opera */
 }
 
 #previewheaderstoggle .iconlink {
@@ -801,28 +796,29 @@
 
 #previewheaderstoggle.remove .iconlink {
 	top: auto;
-	bottom: 5px;
+	bottom: 15px;
 	background-position: -5px -242px;
 }
 
-div.more-headers {
-	cursor: pointer;
-	height: 10px;
-	background: url(images/buttons.png) center -1619px no-repeat;
+#previewheaderstoggle .iconlink.allheaders {
+	display: none;
 }
 
-div.hide-headers {
-	background-position: center -1629px;
+#previewheaderstoggle.remove .iconlink.allheaders {
+	top: auto;
+	bottom: 2px;
+	display: inline-block;
+	background-position: -27px -242px;
 }
 
 #all-headers {
 	position: relative;
-	margin: 0 10px;
+	margin: 2px 0;
 	padding: 0;
 	height: 180px;
-	border: 1px solid #bbb;
+	background-color: #f0f0f0;
+	overflow: hidden;
 	border-radius: 4px;
-	background: #fff;
 }
 
 #headers-source {
@@ -832,25 +828,30 @@
 	left: 0;
 	right: 0;
 	bottom: 0;
-	padding: 2px 5px;
+	padding: 2px;
 	overflow: auto;
 	text-align: left;
-	color: #333;
+	color: #666;
 }
 
-#messagepreviewheader {
+#messageheader {
 	position: relative;
 	height: auto;
 	margin: 0 8px 0 0;
-	padding: 0 0 6px 72px;
+	padding: 0 0 0 72px;
 	border-bottom: 2px solid #f0f0f0;
 }
 
-#messagepreviewheader h3.subject {
+#messagecontent #messageheader {
+	padding: 0 0 0 90px;
+	min-height: 68px;
+}
+
+#messageheader h3.subject {
 	padding: 8px 8px 2px 0;
 }
 
-#messagepreviewheader #contactphoto {
+#messageheader #contactphoto {
 	display: block;
 	position: absolute;
 	top: 11px;
@@ -862,52 +863,40 @@
 	border-radius: 3px;
 }
 
-#messagepreviewheader #contactphoto img {
+#messageheader #contactphoto img {
 	width: 32px;
 	height: auto;
 	border-radius: 3px;
 }
 
-#messageheader #contactphoto {
-	display: block;
-	position: absolute;
-	top: 40px;
-	right: 10px;
+#messagecontent #messageheader #contactphoto {
+	top: 11px;
+	left: 31px;
 	width: 48px;
 	height: 48px;
-	overflow: hidden;
+	background: url(images/contactpic_48px.png) center center no-repeat #fff;
 	border-radius: 4px;
 }
 
-#messageheader #contactphoto img {
+#messagecontent #messageheader #contactphoto img {
 	width: 48px;
 	height: auto;
 	border-radius: 4px;
 }
 
-#messagepreviewheader #countcontrols,
 #messageheader #countcontrols {
 	position: absolute;
 	top: 8px;
-	right: 8px;
-	width: 20em;
+	right: 0;
 	text-align: right;
 	white-space: nowrap;
 }
 
-#messageheader .pagenav .countdisplay {
-	min-width: 0;
-	padding-right: 0.5em;
-	white-space: nowrap;
-}
-
-#messagecontent .leftcol,
 #messagepreview .leftcol {
 	margin-right: 252px;
 	overflow-x: auto;
 }
 
-#messagecontent .rightcol,
 #messagepreview .rightcol {
 	float: right;
 /*
@@ -921,6 +910,7 @@
 	min-height: 200px;
 	background: #f0f0f0;
 	padding: 8px;
+	border-radius: 4px;
 }
 
 #messagebody {
diff --git a/skins/larry/styles.css b/skins/larry/styles.css
index 0a72c50..f2d4888 100644
--- a/skins/larry/styles.css
+++ b/skins/larry/styles.css
@@ -647,6 +647,7 @@
 .uibox {
 	border: 1px solid #a3a3a3;
 	border-radius: 4px;
+	overflow: hidden;
 	box-shadow: 0 0 2px #999;
 	-o-box-shadow: 0 0 2px #999;
 	-webkit-box-shadow: 0 0 2px #999;
@@ -660,7 +661,7 @@
 	left: 0;
 	bottom: 0;
 	width: 100%;
-	min-width: 1150px;
+	min-width: 1024px;
 }
 
 .scroller {
@@ -698,7 +699,8 @@
 	left: 0;
 	width: 100%;
 	bottom: 0;
-	overflow: auto;
+	overflow-x: hidden;
+	overflow-y: auto;
 }
 
 .listbox .scroller.withfooter {
diff --git a/skins/larry/svggradient.php b/skins/larry/svggradient.php
index c54bdec..8db2c5f 100644
--- a/skins/larry/svggradient.php
+++ b/skins/larry/svggradient.php
@@ -11,6 +11,8 @@
  * See http://creativecommons.org/licenses/by-sa/3.0/ for details.
  */
 
+ini_set('error_reporting', E_ALL &~ (E_NOTICE | E_STRICT));
+
 header('Content-Type: image/svg+xml');
 header("Expires: ".gmdate("D, d M Y H:i:s", time()+864000)." GMT");
 header("Cache-Control: max-age=864000");
diff --git a/skins/larry/svggradients.css b/skins/larry/svggradients.css
index 143fb37..4f1dd8a 100644
--- a/skins/larry/svggradients.css
+++ b/skins/larry/svggradients.css
@@ -133,7 +133,7 @@
 	background-image: url(svggradient.php?c=005d76;004558);
 }
 
-#messageheader, #partheader, #composeheaders {
+#partheader, #composeheaders {
 	background-image: url(svggradient.php?c=ffffff;e9e9e9);
 }
 
diff --git a/skins/larry/templates/message.html b/skins/larry/templates/message.html
index 1becd71..89b7bd8 100644
--- a/skins/larry/templates/message.html
+++ b/skins/larry/templates/message.html
@@ -24,20 +24,38 @@
 
 <!-- folders list -->
 <div id="mailboxcontainer" class="uibox listbox">
-<div class="scroller">
-<roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" />
-</div>
+    <div class="scroller">
+        <roundcube:object name="mailboxlist" id="mailboxlist" class="listing" folder_filter="mail" unreadwrap="%s" />
+    </div>
 </div>
 
-</div>
+</div><!-- end mailview-left -->
 
-<div id="mailview-right">
+<div id="mailview-right" class="uibox" style="top: 42px">
 
-<div id="mailview-top">
-<div id="messageheader" class="uibox">
-<h2 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h2>
-<roundcube:object name="messageHeaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject" />
-<roundcube:object name="messageFullHeaders" id="full-headers" />
+<div id="messagecontent">
+
+<div id="messageheader">
+<h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
+
+<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></span></a>
+
+<div id="contactphoto"><roundcube:object name="contactphoto" /></div>
+
+<table class="headers-table" id="preview-shortheaders"><tbody><tr>
+<roundcube:if condition="env:mailbox == config:drafts_mbox || env:mailbox == config:sent_mbox">
+	<td class="header-title"><roundcube:label name="to" /></td>
+	<td class="header from"><roundcube:object name="messageHeaders" valueOf="to" addicon="/images/addcontact.png" /></td>
+<roundcube:else />
+	<td class="header-title"><roundcube:label name="from" /></td>
+	<td class="header from"><roundcube:object name="messageHeaders" valueOf="from" addicon="/images/addcontact.png" /></td>
+<roundcube:endif />
+	<td class="header-title"><roundcube:label name="date" /></td>
+	<td class="header from"><roundcube:object name="messageHeaders" valueOf="date" /></td>
+</tr></tbody></table>
+
+<roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" />
+<roundcube:object name="messageFullHeaders" no-switch="true" />
 
 <!-- record navigation -->
 <div id="countcontrols" class="pagenav">
@@ -46,24 +64,21 @@
 	<roundcube:button command="nextmessage" type="link" class="button nextpage disabled" classAct="button nextpage" classSel="button nextpage pressed" innerClass="inner" title="nextmessage" content="&amp;gt;" />
 </div>
 
-<div id="contactphoto"><roundcube:object name="contactphoto" /></div>
+</div><!-- end messageheader -->
+
+<div id="messagepreview">
+    <div class="rightcol">
+        <roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
+    </div>
+    <div class="leftcol">
+        <roundcube:object name="messageObjects" id="message-objects" />
+        <roundcube:object name="messageBody" id="messagebody" />
+    </div>
 </div>
 
-<div id="messagecontent" class="uibox">
-<div class="rightcol">
-<roundcube:object name="messageAttachments" id="attachment-list" class="attachmentslist" />
-</div>
-<div class="leftcol">
-<roundcube:object name="messageObjects" id="message-objects" />
-<roundcube:object name="messageBody" id="messagebody" />
-</div>
-</div>
+</div><!-- end messagecontent -->
 
-</div><!-- end mailview-top -->
-
-<div id="mailview-bottom" class="uibox">
 <roundcube:object name="message" id="message" class="statusbar" />
-</div>
 
 </div><!-- end mailview-right -->
 
diff --git a/skins/larry/templates/messageerror.html b/skins/larry/templates/messageerror.html
index 70181f1..2f52432 100644
--- a/skins/larry/templates/messageerror.html
+++ b/skins/larry/templates/messageerror.html
@@ -27,8 +27,6 @@
 
 </div>
 
-<div id="mailview-right">
-
 <!-- toolbar -->
 <div id="messagetoolbar" class="fullwidth">
 	<div id="mailtoolbar" class="toolbar">
@@ -36,11 +34,11 @@
 	</div>
 </div>
 
-<div id="mailview-top" class="uibox watermark"></div>
+<div id="mailview-right" class="uibox" style="top: 42px">
 
-<div id="mailview-bottom" class="uibox">
-	<roundcube:object name="message" id="message" class="statusbar" />
-</div>
+<div id="messagecontent" class="watermark"></div>
+
+<roundcube:object name="message" id="message" class="statusbar" />
 
 </div><!-- end mailview-right -->
 
diff --git a/skins/larry/templates/messagepreview.html b/skins/larry/templates/messagepreview.html
index b53683e..74c414b 100644
--- a/skins/larry/templates/messagepreview.html
+++ b/skins/larry/templates/messagepreview.html
@@ -6,10 +6,10 @@
 </head>
 <body class="iframe fullheight">
 
-<div id="messagepreviewheader">
+<div id="messageheader">
 <h3 class="subject"><roundcube:object name="messageHeaders" valueOf="subject" /></h3>
 
-<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span></a>
+<a href="#details" id="previewheaderstoggle"><span class="iconlink"></span><span id="headerstoggleall" class="iconlink allheaders"></a>
 <div id="contactphoto"><roundcube:object name="contactphoto" /></div>
 
 <table class="headers-table" id="preview-shortheaders"><tbody><tr>
@@ -25,6 +25,7 @@
 </tr></tbody></table>
 
 <roundcube:object name="messageHeaders" id="preview-allheaders" class="headers-table" addicon="/images/addcontact.png" exclude="subject,replyto" />
+<roundcube:object name="messageFullHeaders" no-switch="true" />
 
 <!-- record navigation -->
 <div id="countcontrols" class="pagenav">
diff --git a/skins/larry/ui.js b/skins/larry/ui.js
index ca16807..e3b5eef 100644
--- a/skins/larry/ui.js
+++ b/skins/larry/ui.js
@@ -74,9 +74,8 @@
 
       if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
         layout_messageview();
-        rcmail.addEventListener('aftershow-headers', function() { layout_messageview(); });
-        rcmail.addEventListener('afterhide-headers', function() { layout_messageview(); });
-        $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false });
+        $('#previewheaderstoggle').click(function(e){ toggle_preview_headers(this); return false; });
+        $('#headerstoggleall').click(function(e){ toggle_all_headers(this); return false; });
       }
       else if (rcmail.env.action == 'compose') {
         rcmail.addEventListener('aftertoggle-editor', function(){ window.setTimeout(function(){ layout_composeview() }, 200); });
@@ -162,6 +161,12 @@
 
         new rcube_scroller('#directorylist-content', '#directorylist-header', '#directorylist-footer');
       }
+    }
+
+    // set min-width to show all toolbar buttons
+    var screen = $('.minwidth');
+    if (screen.length) {
+      screen.css('min-width', $('.toolbar').width() + $('#quicksearchbar').parent().width() + 20);
     }
 
     // turn a group of fieldsets into tabs
@@ -254,11 +259,11 @@
    */
   function resize()
   {
-    if (rcmail.env.task == 'mail' && (rcmail.env.action == 'show' || rcmail.env.action == 'preview')) {
-      layout_messageview();
-    }
-    if (rcmail.env.task == 'mail' && rcmail.env.action == 'compose') {
-      layout_composeview();
+    if (rcmail.env.task == 'mail') {
+      if (rcmail.env.action == 'show' || rcmail.env.action == 'preview')
+        layout_messageview();
+      else if (rcmail.env.action == 'compose')
+        layout_composeview();
     }
 
     // make iframe footer buttons float if scrolling is active
@@ -267,13 +272,13 @@
         body = $(document.body),
         floating = footer.hasClass('floating'),
         overflow = body.outerHeight(true) > $(window).height();
+
       if (overflow != floating) {
         var action = overflow ? 'addClass' : 'removeClass';
         footer[action]('floating');
         body[action]('floatingbuttons');
       }
-    })
-
+    });
   }
 
   /**
@@ -315,7 +320,6 @@
    */
   function layout_messageview()
   {
-    $('#messagecontent').css('top', ($('#messageheader').outerHeight() + 10) + 'px');
     $('#message-objects div a').addClass('button');
 
     if (!$('#attachment-list li').length) {
@@ -508,13 +512,31 @@
   {
     $('#preview-shortheaders').toggle();
     var full = $('#preview-allheaders').toggle(),
-      button = $('a#previewheaderstoggle');
+      button = $('#previewheaderstoggle');
+
+    if (!$('#headerstoggleall').length)
+      $('#all-headers').toggle();
 
     // add toggle button to full headers table
-    if (full.is(':visible'))
-      button.attr('href', '#hide').removeClass('add').addClass('remove')
-    else
-      button.attr('href', '#details').removeClass('remove').addClass('add')
+    if (full.is(':visible')) {
+      button.attr('href', '#hide').removeClass('add').addClass('remove');
+    }
+    else {
+      button.attr('href', '#details').removeClass('remove').addClass('add');
+    }
+  }
+
+
+  /**
+   * Show/hide all message headers
+   */
+  function toggle_all_headers(button)
+  {
+    rcmail.command('show-headers', '', button);
+    $(button).remove();
+    $('#previewheaderstoggle span').css({bottom: '5px'});
+
+    return false;
   }
 
 
@@ -847,6 +869,8 @@
       // Select/unselect tab
       $('#tab'+idx).toggleClass('selected', idx==index);
     });
+
+    resize();
   }
 
   /**
diff --git a/tests/Framework/Shared.php b/tests/Framework/Shared.php
index d38fb03..99ef829 100644
--- a/tests/Framework/Shared.php
+++ b/tests/Framework/Shared.php
@@ -158,4 +158,47 @@
 
         $this->assertEquals($input_str, $result_str, "Invalid array_keys_recursive() result");
     }
+
+    /**
+     * rcube_shared.inc: format_email()
+     */
+    function test_format_email()
+    {
+        $data = array(
+            ''                 => '',
+            'test'             => 'test',
+            'test@test.tld'    => 'test@test.tld',
+            'test@[127.0.0.1]' => 'test@[127.0.0.1]',
+            'TEST@TEST.TLD'    => 'TEST@test.tld',
+        );
+
+        foreach ($data as $value => $expected) {
+            $result = format_email($value);
+            $this->assertEquals($expected, $result, "Invalid format_email() result for $value");
+        }
+
+    }
+
+    /**
+     * rcube_shared.inc: format_email_recipient()
+     */
+    function test_format_email_recipient()
+    {
+        $data = array(
+            ''                          => array(''),
+            'test'                      => array('test'),
+            'test@test.tld'             => array('test@test.tld'),
+            'test@[127.0.0.1]'          => array('test@[127.0.0.1]'),
+            'TEST@TEST.TLD'             => array('TEST@TEST.TLD'),
+            'TEST <test@test.tld>'      => array('test@test.tld', 'TEST'),
+            '"TEST\"" <test@test.tld>'  => array('test@test.tld', 'TEST"'),
+        );
+
+        foreach ($data as $expected => $value) {
+            $result = format_email_recipient($value[0], $value[1]);
+            $this->assertEquals($expected, $result, "Invalid format_email_recipient()");
+        }
+
+    }
+
 }
diff --git a/tests/HtmlToText.php b/tests/HtmlToText.php
index 34e2d1a..b90c61a 100644
--- a/tests/HtmlToText.php
+++ b/tests/HtmlToText.php
@@ -8,7 +8,7 @@
 class HtmlToText extends PHPUnit_Framework_TestCase
 {
 
-    function data()
+    function data_html2text()
     {
         return array(
             0 => array(
@@ -45,7 +45,7 @@
     }
 
     /**
-     * @dataProvider data
+     * @dataProvider data_html2text
      */
     function test_html2text($title, $in, $out)
     {
diff --git a/tests/MailFunc.php b/tests/MailFunc.php
index 57a6b9d..967277c 100644
--- a/tests/MailFunc.php
+++ b/tests/MailFunc.php
@@ -8,7 +8,7 @@
 class MailFunc extends PHPUnit_Framework_TestCase
 {
 
-    function __construct()
+    function setUp()
     {
         // simulate environment to successfully include func.inc
         $GLOBALS['RCMAIL'] = $RCMAIL = rcmail::get_instance();
diff --git a/tests/phpunit.xml b/tests/phpunit.xml
index 28f7e74..8b38832 100644
--- a/tests/phpunit.xml
+++ b/tests/phpunit.xml
@@ -29,5 +29,9 @@
             <file>HtmlToText.php</file>
             <file>MailFunc.php</file>
         </testsuite>
+        <testsuite name="managesieve">
+            <file>./../plugins/managesieve/tests/Parser.php</file>
+            <file>./../plugins/managesieve/tests/Tokenizer.php</file>
+        </testsuite>
     </testsuites>
 </phpunit>

--
Gitblit v1.9.1