Merge branch 'master' into dev-elastic

pull/6040/head
Aleksander Machniak 7 years ago
commit 913066dd3b

@ -48,6 +48,8 @@ CHANGELOG Roundcube Webmail
- Fix bug where errors were not printed when using bin/update.sh (#5834)
- Fix PHP 7.2 warnings on count() use (#5845)
- Fix bug where Chrome could not upload the same file that was selected before (#5854)
- Fix duplicate messages on the list after deleting messages on the next to the last page (#5862)
- Fix bug where messages count was not updated after delete when imap_cache is set (#5872)
RELEASE 1.3.0
-------------

@ -290,8 +290,11 @@ class archive extends rcube_plugin
// add new rows from next page (if any)
if ($addrows && $count && $uids != '*' && ($jump_back || $nextpage_count > 0)) {
// #5862: Don't add more rows than it was on the next page
$count = $jump_back ? null : min($nextpage_count, $count);
$a_headers = $storage->list_messages($mbox, null,
rcmail_sort_column(), rcmail_sort_order(), $jump_back ? null : $count);
rcmail_sort_column(), rcmail_sort_order(), $count);
rcmail_js_message_list($a_headers, false);
}

@ -67,7 +67,7 @@
<div id="messagemenu" class="popupmenu">
<ul>
<li><roundcube:button class="deletelink" command="plugin.enigma-key-delete" label="enigma.keyremove" classAct="deletelink active" /></li>
<li><roundcube:button type="link" class="deletelink" command="plugin.enigma-key-delete" label="enigma.keyremove" classAct="deletelink active" /></li>
<!--
<li><roundcube:button class="disablelink" command="plugin.enigma-key-disable" label="enigma.keydisable" classAct="disablelink active" /></li>
<li><roundcube:button class="revokelink" command="plugin.enigma-key-revoke" label="enigma.keyrevoke" classAct="revokelink active" /></li>
@ -79,8 +79,8 @@
<div id="exportmenu" class="popupmenu">
<ul>
<li><roundcube:button command="plugin.enigma-key-export" label="exportall" prop="sub" classAct="exportalllink active" class="exportalllink" /></li>
<li><roundcube:button command="plugin.enigma-key-export-selected" label="exportsel" prop="sub" classAct="exportsellink active" class="exportsellink" /></li>
<li><roundcube:button type="link" command="plugin.enigma-key-export" label="exportall" prop="sub" classAct="exportalllink active" class="exportalllink" /></li>
<li><roundcube:button type="link" command="plugin.enigma-key-export-selected" label="exportsel" prop="sub" classAct="exportsellink active" class="exportsellink" /></li>
</ul>
</div>

@ -39,6 +39,7 @@ class rcube_sieve_script
'imap4flags', // RFC5232
'include', // RFC6609
'index', // RFC5260
'mime', // RFC5703 (except: foreverypart, break, enclose, extracttext)
'notify', // RFC5435
'regex', // draft-ietf-sieve-regex-01
'reject', // RFC5429
@ -235,8 +236,11 @@ class rcube_sieve_script
break;
case 'exists':
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= 'exists ' . self::escape_string($test['arg']);
$tests[$i] .= ($test['not'] ? 'not ' : '') . 'exists';
$this->add_mime($test, $tests[$i], $exts);
$tests[$i] .= ' ' . self::escape_string($test['arg']);
break;
case 'header':
@ -248,6 +252,10 @@ class rcube_sieve_script
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= $test['test'];
if ($test['test'] == 'header') {
$this->add_mime($test, $tests[$i], $exts);
}
$this->add_index($test, $tests[$i], $exts);
$this->add_operator($test, $tests[$i], $exts);
@ -264,7 +272,8 @@ class rcube_sieve_script
$tests[$i] .= ($test['not'] ? 'not ' : '');
$tests[$i] .= $test['test'];
if ($test['test'] != 'envelope') {
if ($test['test'] == 'address') {
$this->add_mime($test, $tests[$i], $exts);
$this->add_index($test, $tests[$i], $exts);
}
@ -437,6 +446,21 @@ class rcube_sieve_script
$action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']);
break;
case 'replace':
array_push($exts, 'mime');
$action_script .= 'replace';
if (!empty($action['mime'])) {
$action_script .= " :mime";
}
if (!empty($action['subject'])) {
$action_script .= " :subject " . self::escape_string($action['subject']);
}
if (!empty($action['from'])) {
$action_script .= " :from " . self::escape_string($action['from']);
}
$action_script .= ' ' . self::escape_string($action['replace']);
break;
case 'notify':
array_push($exts, $notify);
$action_script .= 'notify';
@ -799,8 +823,9 @@ class rcube_sieve_script
break;
case 'exists':
$tests[] = array('test' => 'exists', 'not' => $not,
'arg' => array_pop($tokens));
$test = array('test' => 'exists', 'not' => $not, 'arg' => array_pop($tokens));
$test += $this->test_tokens($tokens);
$tests[] = $test;
break;
case 'true':
@ -907,6 +932,15 @@ class rcube_sieve_script
$result[] = $action;
break;
case 'replace':
$action = array('type' => 'replace', 'replace' => array_pop($tokens));
$args = array('mime');
$vargs = array('subject', 'from');
$action += $this->action_arguments($tokens, $args, $vargs);
$result[] = $action;
break;
case 'require':
// skip, will be build according to used commands
// $result[] = array('type' => 'require', 'target' => array_pop($tokens));
@ -986,6 +1020,31 @@ class rcube_sieve_script
}
}
/**
* Add mime argument(s) to the test
*/
private function add_mime($test, &$out, &$exts)
{
foreach (array('mime', 'mime-anychild', 'mime-type', 'mime-subtype', 'mime-contenttype', 'mime-param') as $opt) {
if (!empty($test[$opt])) {
$opt_name = str_replace('mime-', '', $opt);
if (!$got_mime) {
$out .= ' :mime';
$got_mime = true;
array_push($exts, 'mime');
}
if ($opt_name != 'mime') {
$out .= " :$opt_name";
}
if ($opt_name == 'param') {
$out .= ' ' . self::escape_string($test[$opt]);
}
}
}
}
/**
* Add operators to the test
*/
@ -1021,25 +1080,31 @@ class rcube_sieve_script
$result = array();
for ($i=0, $len=count($tokens); $i<$len; $i++) {
if (!is_array($tokens[$i]) && preg_match('/^:comparator$/i', $tokens[$i])) {
$token = is_array($tokens[$i]) ? null : $tokens[$i];
if ($token && preg_match('/^:comparator$/i', $token)) {
$test['comparator'] = $tokens[++$i];
}
else if (!is_array($tokens[$i]) && preg_match('/^:(count|value)$/i', $tokens[$i])) {
$test['type'] = strtolower(substr($tokens[$i], 1)) . '-' . $tokens[++$i];
else if ($token && preg_match('/^:(count|value)$/i', $token)) {
$test['type'] = strtolower(substr($token, 1)) . '-' . $tokens[++$i];
}
else if ($token && preg_match('/^:(is|contains|matches|regex)$/i', $token)) {
$test['type'] = strtolower(substr($token, 1));
}
else if (!is_array($tokens[$i]) && preg_match('/^:(is|contains|matches|regex)$/i', $tokens[$i])) {
$test['type'] = strtolower(substr($tokens[$i], 1));
else if ($token && preg_match('/^:(mime|anychild|type|subtype|contenttype|param)$/i', $token)) {
$token = strtolower(substr($token, 1));
$key = $token == 'mime' ? $token : "mime-$token";
$test[$key] = $token == 'param' ? $tokens[++$i] : true;
}
else if (!is_array($tokens[$i]) && preg_match('/^:index$/i', $tokens[$i])) {
else if ($token && preg_match('/^:index$/i', $token)) {
$test['index'] = intval($tokens[++$i]);
if ($tokens[$i+1] && preg_match('/^:last$/i', $tokens[$i+1])) {
$test['last'] = true;
$i++;
}
}
else {
$result[] = $tokens[$i];
}
}
else {
$result[] = $tokens[$i];
}
}
$tokens = $result;
@ -1090,8 +1155,9 @@ class rcube_sieve_script
static function escape_string($str)
{
if (is_array($str) && count($str) > 1) {
foreach ($str as $idx => $val)
foreach ($str as $idx => $val) {
$str[$idx] = self::escape_string($val);
}
return '[' . implode(',', $str) . ']';
}

@ -62,11 +62,11 @@
<div id="filtersetmenu" class="popupmenu">
<ul>
<li><roundcube:button command="plugin.managesieve-setact" label="managesieve.enable" classAct="active" /></li>
<li><roundcube:button command="plugin.managesieve-setdel" label="delete" classAct="active" /></li>
<li class="separator_above"><roundcube:button command="plugin.managesieve-setget" label="download" classAct="active" /></li>
<li><roundcube:button type="link" command="plugin.managesieve-setact" label="managesieve.enable" classAct="active" /></li>
<li><roundcube:button type="link" command="plugin.managesieve-setdel" label="delete" classAct="active" /></li>
<li class="separator_above"><roundcube:button type="link" command="plugin.managesieve-setget" label="download" classAct="active" /></li>
<roundcube:if condition="env:raw_sieve_editor != false" />
<li><roundcube:button command="plugin.managesieve-seteditraw" label="managesieve.filterseteditraw" classAct="active" /></li>
<li><roundcube:button type="link" command="plugin.managesieve-seteditraw" label="managesieve.filterseteditraw" classAct="active" /></li>
<roundcube:endif />
<roundcube:container name="filtersetoptions" id="filtersetmenu" />
</ul>
@ -74,8 +74,8 @@
<div id="filtermenu" class="popupmenu">
<ul>
<li><roundcube:button command="plugin.managesieve-act" label="managesieve.enable" classAct="active" /></li>
<li><roundcube:button command="plugin.managesieve-del" label="delete" classAct="active" /></li>
<li><roundcube:button type="link" command="plugin.managesieve-act" label="managesieve.enable" classAct="active" /></li>
<li><roundcube:button type="link" command="plugin.managesieve-del" label="delete" classAct="active" /></li>
<roundcube:container name="filteroptions" id="filtermenu" />
</ul>
</div>

@ -0,0 +1,188 @@
<?php
/**
+-----------------------------------------------------------------------+
| program/include/rcmail_resend_mail.php |
| |
| This file is part of the Roundcube Webmail client |
| Copyright (C) 2005-2017, The Roundcube Dev Team |
| |
| Licensed under the GNU General Public License version 3 or |
| any later version with exceptions for skins & plugins. |
| See the README file for a full license statement. |
| |
| PURPOSE: |
| Bounce/resend an email message |
+-----------------------------------------------------------------------+
| Author: Thomas Bruederli <roundcube@gmail.com> |
| Author: Aleksander Machniak <alec@alec.pl> |
+-----------------------------------------------------------------------+
*/
/**
* Mail_mime wrapper to handle mail resend/bounce
*
* @package Webmail
*/
class rcmail_resend_mail extends Mail_mime
{
protected $orig_head;
protected $orig_body;
/**
* Constructor function
*
* Added two parameters:
* 'bounce_message' - rcube_message object of the original message
* 'bounce_headers' - An array of headers to be added to the original message
*/
public function __construct($params = array())
{
// To make the code simpler always use delay_file_io=true
$params['delay_file_io'] = true;
$params['eol'] = "\r\n";
parent::__construct($params);
}
/**
* Returns/Sets message headers
*/
public function headers($headers = array(), $overwrite = false, $skip_content = false)
{
// headers() wrapper that returns Resent-Cc, Resent-Bcc instead of Cc,Bcc
// it's also called to re-add Resent-Bcc after it has been sent (to store in Sent)
if (array_key_exists('Bcc', $headers)) {
$this->build_params['bounce_headers']['Resent-Bcc'] = $headers['Bcc'];
}
foreach ($this->build_params['bounce_headers'] as $key => $val) {
$headers[str_replace('Resent-', '', $key)] = $val;
}
return $headers;
}
/**
* Returns all message headers as string
*/
public function txtHeaders($headers = array(), $overwrite = false, $skip_content = false)
{
// i.e. add Resent-* headers on top of the original message head
$this->init_message();
$result = array();
foreach ($this->build_params['bounce_headers'] as $name => $value) {
$key = str_replace('Resent-', '', $name);
// txtHeaders() can be used to unset Bcc header
if (array_key_exists($key, $headers)) {
$value = $headers[$key];
$this->build_params['bounce_headers']['Resent-'.$key] = $value;
}
if ($value) {
$result[] = "$name: $value";
}
}
$result = implode($this->build_params['eol'], $result);
if (strlen($this->orig_head)) {
$result .= $this->build_params['eol'] . $this->orig_head;
}
return $result;
}
/**
* Save the message body to a file (if delay_file_io=true)
*/
public function saveMessageBody($file, $params = null)
{
$this->init_message();
// this will be called only once, so let just move the file
rename($this->orig_body, $file);
$this->orig_head = null;
}
protected function init_message()
{
if ($this->orig_head !== null) {
return;
}
$rcmail = rcmail::get_instance();
$storage = $rcmail->get_storage();
$message = $this->build_params['bounce_message'];
$temp_dir = unslashify($rcmail->config->get('temp_dir'));
$path = tempnam($temp_dir, 'rcmBounce');
// We'll write the body to the file and the headers to a variable
if ($fp = fopen($path, 'w')) {
stream_filter_register('bounce_source', 'rcmail_bounce_stream_filter');
stream_filter_append($fp, 'bounce_source');
// message part
if ($message->context) {
$message->get_part_body($message->context, false, 0, $fp);
}
// complete message
else {
$storage->set_folder($message->folder);
$storage->get_raw_body($message->uid, $fp);
}
fclose($fp);
$this->orig_head = rcmail_bounce_stream_filter::$headers;
$this->orig_body = $path;
}
}
}
/**
* Stream filter to remove message headers from the streamed
* message source (and store them in a variable)
*
* @package Webmail
*/
class rcmail_bounce_stream_filter extends php_user_filter
{
public static $headers;
protected $in_body = false;
public function onCreate()
{
self::$headers = '';
}
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
if (!$this->in_body) {
self::$headers .= $bucket->data;
if (($pos = strpos(self::$headers, "\r\n\r\n")) === false) {
continue;
}
$bucket->data = substr(self::$headers, $pos + 4);
$bucket->datalen = strlen($bucket->data);
self::$headers = substr(self::$headers, 0, $pos);
$this->in_body = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}

@ -4060,7 +4060,7 @@ function rcube_webmail()
ref.mark_all_read(folder, $('input:checked', this).val());
return true;
},
{button: 'mark'}
{button: 'mark', height: 120}
);
return;
@ -6782,7 +6782,7 @@ function rcube_webmail()
this.qrcode = function()
{
var title = this.get_label('qrcode'),
options = {button: false, cancel_button: 'close', width: 310, height: 410},
options = {button: false, cancel_button: 'close', width: 300, height: 350},
img = new Image(300, 300);
img.src = this.url('addressbook/qrcode', {_source: this.env.source, _cid: this.env.cid});
@ -7756,13 +7756,18 @@ function rcube_webmail()
popup.dialog(options);
if (options.width)
popup.width(options.width);
if (options.height)
popup.height(options.height);
// resize and center popup
var win = $(window), w = win.width(), h = win.height(),
width = popup.width(), height = popup.height();
popup.dialog('option', {
height: Math.min(h - 40, height + 75 + (buttons ? 50 : 0)),
width: Math.min(w - 20, width + 36)
height: Math.min(h - 40, height + 28 + (buttons ? 50 : 0)),
width: Math.min(w - 20, width + 28)
});
// assign special classes to dialog buttons

@ -2495,13 +2495,12 @@ class rcube_imap extends rcube_storage
// clear cached counters
if ($flag == 'SEEN' || $flag == 'UNSEEN') {
$this->clear_messagecount($folder, 'SEEN');
$this->clear_messagecount($folder, 'UNSEEN');
$this->clear_messagecount($folder, array('SEEN', 'UNSEEN'));
}
else if ($flag == 'DELETED' || $flag == 'UNDELETED') {
$this->clear_messagecount($folder, 'DELETED');
// remove cached messages
$this->clear_messagecount($folder, array('ALL', 'THREADS'));
if ($this->options['skip_deleted']) {
// remove cached messages
$this->clear_message_cache($folder, $all_mode ? null : explode(',', $uids));
}
}
@ -4415,13 +4414,15 @@ class rcube_imap extends rcube_storage
/**
* Remove messagecount of a specific folder from cache
*/
protected function clear_messagecount($folder, $mode=null)
protected function clear_messagecount($folder, $mode = array())
{
$a_folder_cache = $this->get_cache('messagecount');
if (is_array($a_folder_cache[$folder])) {
if ($mode) {
unset($a_folder_cache[$folder][$mode]);
if (!empty($mode)) {
foreach ((array) $mode as $key) {
unset($a_folder_cache[$folder][$key]);
}
}
else {
unset($a_folder_cache[$folder]);

@ -19,166 +19,6 @@
+-----------------------------------------------------------------------+
*/
/**
* Mail_mime wrapper to handle mail bounce
*
* @FIXME: For some reason this class definition
* cannot be put at the end of the file
*/
class rcmail_bounce_mail extends Mail_mime
{
/**
* Constructor function
*/
public function __construct($params = array())
{
// To make the code simpler always use delay_file_io=true
$params['delay_file_io'] = true;
$params['eol'] = "\r\n";
parent::__construct($params);
}
/**
* Returns/Sets message headers
*/
public function headers($headers = array(), $overwrite = false, $skip_content = false)
{
// headers() wrapper that returns Resent-Cc, Resent-Bcc instead of Cc,Bcc
// it's also called to re-add Resent-Bcc after it has been sent (to store in Sent)
if (array_key_exists('Bcc', $headers)) {
$this->build_params['bounce_headers']['Resent-Bcc'] = $headers['Bcc'];
}
foreach ($this->build_params['bounce_headers'] as $key => $val) {
$headers[str_replace('Resent-', '', $key)] = $val;
}
return $headers;
}
/**
* Returns all message headers as string
*/
public function txtHeaders($headers = array(), $overwrite = false, $skip_content = false)
{
// i.e. add Resent-* headers on top of the original message head
$this->init_message();
$result = array();
foreach ($this->build_params['bounce_headers'] as $name => $value) {
$key = str_replace('Resent-', '', $name);
// txtHeaders() can be used to unset Bcc header
if (array_key_exists($key, $headers)) {
$value = $headers[$key];
$this->build_params['bounce_headers']['Resent-'.$key] = $value;
}
if ($value) {
$result[] = "$name: $value";
}
}
$result = implode($this->build_params['eol'], $result);
if (strlen($this->bounce_head)) {
$result .= $this->build_params['eol'] . $this->bounce_head;
}
return $result;
}
/**
* Save the message body to a file (if delay_file_io=true)
*/
public function saveMessageBody($file, $params = null)
{
$this->init_message();
// this will be called only once, so let just move the file
rename($this->bounce_body, $file);
$this->bounce_head = null;
}
protected function init_message()
{
if ($this->bounce_head !== null) {
return;
}
$rcmail = rcmail::get_instance();
$storage = $rcmail->get_storage();
$message = $this->build_params['bounce_message'];
$temp_dir = unslashify($rcmail->config->get('temp_dir'));
$path = tempnam($temp_dir, 'rcmBounce');
// We'll write the body to the file and the headers to a variable
if ($fp = fopen($path, 'w')) {
stream_filter_register('bounce_source', 'rcmail_bounce_stream_filter');
stream_filter_append($fp, 'bounce_source');
// message part
if ($message->context) {
$message->get_part_body($message->context, false, 0, $fp);
}
// complete message
else {
$storage->set_folder($message->folder);
$storage->get_raw_body($message->uid, $fp);
}
fclose($fp);
$this->bounce_head = rcmail_bounce_stream_filter::$headers;
$this->bounce_body = $path;
}
}
}
/**
* Stream filter to remove message headers from the streamed
* message source (and store them in a variable)
*/
class rcmail_bounce_stream_filter extends php_user_filter
{
public static $headers;
protected $in_body = false;
public function onCreate()
{
self::$headers = '';
}
public function filter($in, $out, &$consumed, $closing)
{
while ($bucket = stream_bucket_make_writeable($in)) {
if (!$this->in_body) {
self::$headers .= $bucket->data;
if (($pos = strpos(self::$headers, "\r\n\r\n")) === false) {
continue;
}
$bucket->data = substr(self::$headers, $pos + 4);
$bucket->datalen = strlen($bucket->data);
self::$headers = substr(self::$headers, 0, $pos);
$this->in_body = true;
}
$consumed += $bucket->datalen;
stream_bucket_append($out, $bucket);
}
return PSFS_PASS_ON;
}
}
$msg_uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_GP);
$msg_folder = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_GP, true);
$MESSAGE = new rcube_message($msg_uid, $msg_folder);
@ -230,7 +70,7 @@ $headers = array_filter(array(
));
// Create the bounce message
$BOUNCE = new rcmail_bounce_mail(array(
$BOUNCE = new rcmail_resend_mail(array(
'bounce_message' => $MESSAGE,
'bounce_headers' => $headers,
));

@ -158,8 +158,11 @@ if ($_uids && $flag) {
// add new rows from next page (if any)
if ($old_count && $_uids != '*' && ($jump_back || $nextpage_count > 0)) {
$a_headers = $RCMAIL->storage->list_messages($mbox, NULL,
rcmail_sort_column(), rcmail_sort_order(), $jump_back ? NULL : $count);
// #5862: Don't add more rows than it was on the next page
$count = $jump_back ? null : min($nextpage_count, $count);
$a_headers = $RCMAIL->storage->list_messages($mbox, null,
rcmail_sort_column(), rcmail_sort_order(), $count);
rcmail_js_message_list($a_headers, false);
}

@ -161,8 +161,11 @@ if ($threading) {
// add new rows from next page (if any)
if ($addrows && $count && $uids != '*' && ($jump_back || $nextpage_count > 0)) {
// #5862: Don't add more rows than it was on the next page
$count = $jump_back ? null : min($nextpage_count, $count);
$a_headers = $RCMAIL->storage->list_messages($mbox, NULL,
rcmail_sort_column(), rcmail_sort_order(), $jump_back ? NULL : $count);
rcmail_sort_column(), rcmail_sort_order(), $count);
rcmail_js_message_list($a_headers, false);
}

@ -72,6 +72,7 @@ hr
input[type="text"],
input[type="button"],
input[type="password"],
button,
textarea
{
border: 1px solid #666;
@ -79,7 +80,7 @@ textarea
background-color: #FFF;
}
input, textarea
button, input, textarea
{
color: black;
padding: 1px 3px;
@ -93,6 +94,7 @@ textarea:-moz-placeholder
color: #aaa;
}
button,
input.button
{
height: 20px;
@ -104,11 +106,14 @@ input.button
border: 1px solid #a4a4a4;
}
button:hover,
input.button:hover
{
color: black;
}
button[disabled],
button[disabled]:hover,
input.button[disabled],
input.button[disabled]:hover
{
@ -116,6 +121,7 @@ input.button[disabled]:hover
border-color: #ccc;
}
button.mainaction,
input.mainaction
{
font-weight: bold;
@ -1241,7 +1247,7 @@ font.bold
font-weight: bold;
}
.formbuttons
#login-form .formbuttons
{
text-align: center;
}

@ -61,8 +61,8 @@
<div id="mailboxoptionsmenu" class="popupmenu">
<ul>
<li><roundcube:button command="delete-folder" label="delete" classAct="active" /></li>
<li><roundcube:button command="purge" type="link" label="empty" classAct="active" /></li>
<li><roundcube:button type="link" command="delete-folder" label="delete" classAct="active" /></li>
<li><roundcube:button type="link" command="purge" label="empty" classAct="active" /></li>
<roundcube:container name="mailboxoptions" id="mailboxoptionsmenu" />
</ul>
</div>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 B

After

Width:  |  Height:  |  Size: 601 B

Loading…
Cancel
Save