diff options
48 files changed, 478 insertions, 562 deletions
@@ -4,6 +4,10 @@ CHANGELOG Roundcube Webmail - 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 144250495..1952dad49 100644 --- a/plugins/acl/acl.php +++ b/plugins/acl/acl.php @@ -233,8 +233,7 @@ class acl extends rcube_plugin // 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 5e2448efd..e392a269e 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 7f99f6ffe..3cf8292a4 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="⚙" /> + <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="⚙" /> </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 072be2f2c..000000000 --- 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 000000000..06b644b34 --- /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-z_]+$/', $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 000000000..8c0bced23 --- /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 aec042187..000000000 --- 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 08ad54959..000000000 --- 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 a4bc465a3..000000000 --- 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 addc0d449..000000000 --- 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 dcdbd48a0..000000000 --- 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 c87e9658f..000000000 --- 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 6b6f29f4c..000000000 --- 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 a603ff6c1..000000000 --- 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 cf1f8fcad..000000000 --- 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 6d4d03c6e..000000000 --- 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 000000000..9c4717be4 --- /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 000000000..385c8890d --- /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 000000000..bd142ed8c --- /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 000000000..e67bf7cfc --- /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 000000000..b5585a4ba --- /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 000000000..1ded8d8d4 --- /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_prefix b/plugins/managesieve/tests/src/parser_prefix new file mode 100644 index 000000000..9f6a33a1c --- /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 000000000..0a92fde54 --- /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 000000000..f106b796e --- /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 000000000..93026db45 --- /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 000000000..bd5941c02 --- /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 f988653ee..000000000 --- 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/program/include/rcube_charset.php b/program/include/rcube_charset.php index 380d14978..1740a6096 100644 --- a/program/include/rcube_charset.php +++ b/program/include/rcube_charset.php @@ -181,6 +181,12 @@ class rcube_charset $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 5dd9c1250..66b5c4bd6 100644 --- a/program/include/rcube_imap.php +++ b/program/include/rcube_imap.php @@ -2222,6 +2222,10 @@ class rcube_imap extends rcube_storage $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 915a11aad..c3cfabc3a 100644 --- a/program/include/rcube_imap_generic.php +++ b/program/include/rcube_imap_generic.php @@ -2538,7 +2538,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2603,7 +2603,7 @@ class rcube_imap_generic { unset($this->data['APPENDUID']); - if (!$mailbox) { + if ($mailbox === null || $mailbox === '') { return false; } @@ -2612,6 +2612,7 @@ class rcube_imap_generic 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 f550b574e..6af1d0133 100644 --- a/program/include/rcube_message.php +++ b/program/include/rcube_message.php @@ -52,7 +52,8 @@ class rcube_message private $opt = array(); private $parse_alternative = false; - public $uid = null; + public $uid; + public $folder; public $headers; public $parts = array(); public $mime_parts = array(); @@ -68,17 +69,22 @@ class rcube_message * * 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); if (!$this->headers) @@ -179,10 +185,12 @@ class rcube_message } 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 @@ class rcube_message 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 @@ class rcube_message 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 a071ee354..2743e7705 100644 --- a/program/include/rcube_output_html.php +++ b/program/include/rcube_output_html.php @@ -400,7 +400,7 @@ class rcube_output_html extends rcube_output 'line' => __LINE__, 'file' => __FILE__, 'message' => 'Error loading template for '.$realname - ), true, true); + ), true, $write); return false; } @@ -698,6 +698,11 @@ class rcube_output_html extends rcube_output } break; + // frame + case 'frame': + return $this->frame($attrib); + break; + // show a label case 'label': if ($attrib['name'] || $attrib['command']) { @@ -1275,6 +1280,30 @@ class rcube_output_html extends rcube_output } + /** + * 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 276940cb6..c15305c08 100644 --- a/program/include/rcube_shared.inc +++ b/program/include/rcube_shared.inc @@ -422,7 +422,6 @@ function rcube_autoload($classname) { $filename = preg_replace( array( - '/MDB2_(.+)/', '/Mail_(.+)/', '/Net_(.+)/', '/Auth_(.+)/', @@ -431,7 +430,6 @@ function rcube_autoload($classname) ), array( 'Mail/\\1', - 'Mail/\\1', 'Net/\\1', 'Auth/\\1', 'html', diff --git a/program/include/rcube_storage.php b/program/include/rcube_storage.php index 768a26d73..f83e24041 100644 --- a/program/include/rcube_storage.php +++ b/program/include/rcube_storage.php @@ -195,7 +195,7 @@ abstract class rcube_storage */ 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 838f240f8..48de21764 100644 --- a/program/js/app.js +++ b/program/js/app.js @@ -1538,14 +1538,17 @@ function rcube_webmail() 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 @@ function rcube_webmail() 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,20 +1955,37 @@ function rcube_webmail() 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() { if (!this.env.frame_lock) @@ -2009,7 +2029,7 @@ function rcube_webmail() // 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() this.list_contacts = function(src, group, page) { - var folder, url = {}, + var win, folder, url = {}, target = window; if (!src) @@ -4047,8 +4067,8 @@ function rcube_webmail() 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 @@ function rcube_webmail() // 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 @@ function rcube_webmail() // 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 @@ function rcube_webmail() // 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 @@ function rcube_webmail() 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 @@ function rcube_webmail() // 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/steps/mail/func.inc b/program/steps/mail/func.inc index 6f451e14f..8bf80a6ee 100644 --- a/program/steps/mail/func.inc +++ b/program/steps/mail/func.inc @@ -90,11 +90,13 @@ if (empty($RCMAIL->action) || $RCMAIL->action == 'list') { // 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); diff --git a/program/steps/mail/headers.inc b/program/steps/mail/headers.inc index 4d6627393..cad113f68 100644 --- a/program/steps/mail/headers.inc +++ b/program/steps/mail/headers.inc @@ -24,7 +24,8 @@ if ($uid = get_input_value('_uid', RCUBE_INPUT_POST)) $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 2c2cbdcc2..6ca704998 100644 --- a/program/steps/settings/folders.inc +++ b/program/steps/settings/folders.inc @@ -411,8 +411,10 @@ function rcmail_rename_folder($oldname, $newname) $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 09f76ac27..73cc5e4bf 100644 --- a/program/steps/settings/save_folder.inc +++ b/program/steps/settings/save_folder.inc @@ -80,7 +80,10 @@ if (!$error && strlen($path) && (!strlen($old_imap) || $old_imap != $name_imap)) } } -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/larry/mail.css b/skins/larry/mail.css index 9ec183eb2..496cbbd15 100644 --- a/skins/larry/mail.css +++ b/skins/larry/mail.css @@ -382,11 +382,7 @@ a.iconbutton.threadmode.selected { /*** 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, diff --git a/skins/larry/styles.css b/skins/larry/styles.css index ea36260cd..f2d4888b1 100644 --- a/skins/larry/styles.css +++ b/skins/larry/styles.css @@ -647,6 +647,7 @@ a.iconlink.upload { .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; @@ -698,7 +699,8 @@ a.iconlink.upload { left: 0; width: 100%; bottom: 0; - overflow: auto; + overflow-x: hidden; + overflow-y: auto; } .listbox .scroller.withfooter { diff --git a/skins/larry/ui.js b/skins/larry/ui.js index 57a1178eb..e3b5eefe4 100644 --- a/skins/larry/ui.js +++ b/skins/larry/ui.js @@ -259,11 +259,11 @@ function rcube_mail_ui() */ 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 @@ -272,13 +272,13 @@ function rcube_mail_ui() 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'); } - }) - + }); } /** @@ -869,6 +869,8 @@ function rcube_mail_ui() // Select/unselect tab $('#tab'+idx).toggleClass('selected', idx==index); }); + + resize(); } /** diff --git a/tests/Framework/Shared.php b/tests/Framework/Shared.php index d38fb03a3..99ef829da 100644 --- a/tests/Framework/Shared.php +++ b/tests/Framework/Shared.php @@ -158,4 +158,47 @@ class Framework_Shared extends PHPUnit_Framework_TestCase $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 34e2d1a63..b90c61adf 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 @@ class HtmlToText extends PHPUnit_Framework_TestCase } /** - * @dataProvider data + * @dataProvider data_html2text */ function test_html2text($title, $in, $out) { diff --git a/tests/MailFunc.php b/tests/MailFunc.php index 57a6b9d10..967277c2a 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 28f7e7420..8b3883223 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> |