Merge branch 'master' of github.com:roundcube/roundcubemail

pull/32/head
Aleksander Machniak 12 years ago
commit 700dc66c67

1
.gitignore vendored

@ -2,3 +2,4 @@
logs/*
temp/*
config/*
plugins/*/config.inc.php

@ -1,6 +1,8 @@
CHANGELOG Roundcube Webmail
===========================
- Integrated zipdownload plugin to download all attachments (#1445509)
- Fix HTML special characters handling in message list/header display (#1488523)
- List related text/html part as attachment in plain text mode (#1488677)
- Use IMAP BINARY (RFC3516) extension to fetch message/part bodies
- Fix folder creation under public namespace root (#1488665)

@ -13,7 +13,7 @@ REQUIREMENTS
* .htaccess support allowing overrides for DirectoryIndex
* PHP Version 5.2.1 or greater including
- PDO, PCRE, DOM, JSON, XML, Session, Sockets (required)
- libiconv (recommended)
- libiconv, zip (recommended)
- mbstring, fileinfo, mcrypt (optional)
* PEAR packages distributed with Roundcube or external:
- Mail_Mime 1.8.1 or newer

@ -1,4 +1,7 @@
- Fixed issue with DBMail bug [http://pear.php.net/bugs/bug.php?id=19077] (#1488594)
- Added support for enotify/notify (RFC5435, RFC5436, draft-ietf-sieve-notify-00)
- Change default port to 4190 (IANA-allocated), add port auto-detection (#1488713)
- Added request size limits detection and script corruption prevention (#1488648)
* version 5.2 [2012-07-24]
-----------------------------------------------------------

@ -1,7 +1,8 @@
<?php
// managesieve server port
$rcmail_config['managesieve_port'] = 2000;
// managesieve server port. When empty the port will be determined automatically
// using getservbyname() function, with 4190 as a fallback.
$rcmail_config['managesieve_port'] = null;
// managesieve server address, default is localhost.
// Replacement variables supported in host name:

@ -41,7 +41,9 @@ class rcube_sieve_script
'variables', // RFC5229
'body', // RFC5173
'subaddress', // RFC5233
// @TODO: enotify/notify, spamtest+virustest, mailbox, date
'enotify', // RFC5435
'notify', // draft-ietf-sieve-notify-00
// @TODO: spamtest+virustest, mailbox, date
);
/**
@ -198,6 +200,9 @@ class rcube_sieve_script
}
}
$imapflags = in_array('imap4flags', $this->supported) ? 'imap4flags' : 'imapflags';
$notify = in_array('enotify', $this->supported) ? 'enotify' : 'notify';
// rules
foreach ($this->content as $rule) {
$extension = '';
@ -370,11 +375,7 @@ class rcube_sieve_script
case 'addflag':
case 'setflag':
case 'removeflag':
if (in_array('imap4flags', $this->supported))
array_push($exts, 'imap4flags');
else
array_push($exts, 'imapflags');
array_push($exts, $imapflags);
$action_script .= $action['type'].' '
. self::escape_string($action['target']);
break;
@ -403,6 +404,46 @@ class rcube_sieve_script
$action_script .= self::escape_string($action['name']) . ' ' . self::escape_string($action['value']);
break;
case 'notify':
array_push($exts, $notify);
$action_script .= 'notify';
// Here we support only 00 version of notify draft, there
// were a couple regressions in 00 to 04 changelog, we use
// the version used by Cyrus
if ($notify == 'notify') {
switch ($action['importance']) {
case 1: $action_script .= " :high"; break;
case 2: $action_script .= " :normal"; break;
case 3: $action_script .= " :low"; break;
}
unset($action['importance']);
}
foreach (array('from', 'importance', 'options', 'message') as $n_tag) {
if (!empty($action[$n_tag])) {
$action_script .= " :$n_tag " . self::escape_string($action[$n_tag]);
}
}
if (!empty($action['address'])) {
$method = 'mailto:' . $action['address'];
if (!empty($action['body'])) {
$method .= '?body=' . rawurlencode($action['body']);
}
}
else {
$method = $action['method'];
}
// method is optional in notify extension
if (!empty($method)) {
$action_script .= ($notify == 'notify' ? " :method " : " ") . self::escape_string($method);
}
break;
case 'vacation':
array_push($exts, 'vacation');
$action_script .= 'vacation';
@ -840,6 +881,49 @@ class rcube_sieve_script
// $result[] = array('type' => 'require', 'target' => $tokens);
break;
case 'notify':
$notify = array('type' => 'notify');
$priorities = array(':high' => 1, ':normal' => 2, ':low' => 3);
// Parameters: :from, :importance, :options, :message
// additional (optional) :method parameter for notify extension
for ($i=0, $len=count($tokens); $i<$len; $i++) {
$tok = strtolower($tokens[$i]);
if ($tok[0] == ':') {
// Here we support only 00 version of notify draft, there
// were a couple regressions in 00 to 04 changelog, we use
// the version used by Cyrus
if (isset($priorities[$tok])) {
$notify['importance'] = $priorities[$tok];
}
else {
$notify[substr($tok, 1)] = $tokens[++$i];
}
}
else {
// unnamed parameter is a :method in enotify extension
$notify['method'] = $tokens[$i];
}
}
$method_components = parse_url($notify['method']);
if ($method_components['scheme'] == 'mailto') {
$notify['address'] = $method_components['path'];
$method_params = array();
if (array_key_exists('query', $method_components)) {
parse_str($method_components['query'], $method_params);
}
$method_params = array_change_key_case($method_params, CASE_LOWER);
// magic_quotes_gpc and magic_quotes_sybase affect the output of parse_str
if (ini_get('magic_quotes_gpc') || ini_get('magic_quotes_sybase')) {
array_map('stripslashes', $method_params);
}
$notify['body'] = (array_key_exists('body', $method_params)) ? $method_params['body'] : '';
}
$result[] = $notify;
break;
}
if ($separator == $end)

@ -97,6 +97,15 @@ $labels['setvariable'] = 'Set variable';
$labels['setvarname'] = 'Variable name:';
$labels['setvarvalue'] = 'Variable value:';
$labels['setvarmodifiers'] = 'Modifiers:';
$labels['notify'] = 'Send notification';
$labels['notifyaddress'] = 'To e-mail address:';
$labels['notifybody'] = 'Notification body:';
$labels['notifysubject'] = 'Notification subject:';
$labels['notifyfrom'] = 'Notification sender:';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
$labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';

@ -88,6 +88,15 @@ $labels['varlowerfirst'] = 'first character lower-case';
$labels['varupperfirst'] = 'first character upper-case';
$labels['varquotewildcard'] = 'quote special characters';
$labels['varlength'] = 'length';
$labels['notify'] = 'Send notification';
$labels['notifyaddress'] = 'To e-mail address:';
$labels['notifybody'] = 'Notification body:';
$labels['notifysubject'] = 'Notification subject:';
$labels['notifyfrom'] = 'Notification sender:';
$labels['notifyimportance'] = 'Importance:';
$labels['notifyimportancelow'] = 'low';
$labels['notifyimportancenormal'] = 'normal';
$labels['notifyimportancehigh'] = 'high';
$labels['filtercreate'] = 'Create filter';
$labels['usedata'] = 'Use following data in the filter:';
$labels['nextstep'] = 'Next Step';

@ -103,6 +103,15 @@ $labels['varlowerfirst'] = 'pierwsza litera mała (:lowerfirst)';
$labels['varupperfirst'] = 'pierwsza litera duża (:upperfirst)';
$labels['varquotewildcard'] = 'anulowane znaki specjalne (:quotewildcard)';
$labels['varlength'] = 'długość (:length)';
$labels['notify'] = 'Wyślij powiadomienie';
$labels['notifyaddress'] = 'Na adres e-mail:';
$labels['notifybody'] = 'Treść powiadomienia:';
$labels['notifysubject'] = 'Temat powiadomienia:';
$labels['notifyfrom'] = 'Nadawca powiadomienia:';
$labels['notifyimportance'] = 'Priorytet:';
$labels['notifyimportancelow'] = 'niski';
$labels['notifyimportancenormal'] = 'normalny';
$labels['notifyimportancehigh'] = 'wysoki';
$labels['filtercreate'] = 'Utwórz filtr';
$labels['usedata'] = 'Użyj następujących danych do utworzenia filtra:';
$labels['nextstep'] = 'Następny krok';

@ -639,7 +639,8 @@ function action_type_select(id)
target_area: document.getElementById('action_target_area' + id),
flags: document.getElementById('action_flags' + id),
vacation: document.getElementById('action_vacation' + id),
set: document.getElementById('action_set' + id)
set: document.getElementById('action_set' + id),
notify: document.getElementById('action_notify' + id)
};
if (obj.value == 'fileinto' || obj.value == 'fileinto_copy') {
@ -660,6 +661,9 @@ function action_type_select(id)
else if (obj.value == 'set') {
enabled.set = 1;
}
else if (obj.value == 'notify') {
enabled.notify = 1;
}
for (var x in elems) {
elems[x].style.display = !enabled[x] ? 'none' : 'inline';

@ -7,13 +7,13 @@
* It's clickable interface which operates on text scripts and communicates
* with server using managesieve protocol. Adds Filters tab in Settings.
*
* @version 5.0
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*
* Configuration (see config.inc.php.dist)
*
* Copyright (C) 2008-2011, The Roundcube Dev Team
* Copyright (C) 2011, Kolab Systems AG
* Copyright (C) 2008-2012, The Roundcube Dev Team
* Copyright (C) 2011-2012, Kolab Systems AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@ -62,8 +62,9 @@ class managesieve extends rcube_plugin
"x-beenthere",
);
const VERSION = '5.2';
const VERSION = '5.2';
const PROGNAME = 'Roundcube (Managesieve)';
const PORT = 4190;
function init()
@ -200,10 +201,16 @@ class managesieve extends rcube_plugin
set_include_path($include_path);
$host = rcube_parse_host($this->rc->config->get('managesieve_host', 'localhost'));
$port = $this->rc->config->get('managesieve_port', 2000);
$host = rcube_idn_to_ascii($host);
$port = $this->rc->config->get('managesieve_port');
if (empty($port)) {
$port = getservbyname('sieve', 'tcp');
if (empty($port)) {
$port = self::PORT;
}
}
$plugin = $this->rc->plugins->exec_hook('managesieve_connect', array(
'user' => $_SESSION['username'],
'password' => $this->rc->decrypt($_SESSION['password']),
@ -523,9 +530,37 @@ class managesieve extends rcube_plugin
// Init plugin and handle managesieve connection
$error = $this->managesieve_start();
// filters set add action
if (!empty($_POST['_newset'])) {
// get request size limits (#1488648)
$max_post = max(array(
ini_get('max_input_vars'),
ini_get('suhosin.request.max_vars'),
ini_get('suhosin.post.max_vars'),
));
$max_depth = max(array(
ini_get('suhosin.request.max_array_depth'),
ini_get('suhosin.post.max_array_depth'),
));
// check request size limit
if ($max_post && count($_POST, COUNT_RECURSIVE) >= $max_post) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of max_input_vars/suhosin.request.max_vars/suhosin.post.max_vars)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// check request depth limits
else if ($max_depth && count($_POST['_header']) > $max_depth) {
rcube::raise_error(array(
'code' => 500, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Request size limit exceeded (one of suhosin.request.max_array_depth/suhosin.post.max_array_depth)"
), true, false);
$this->rc->output->show_message('managesieve.filtersaveerror', 'error');
}
// filters set add action
else if (!empty($_POST['_newset'])) {
$name = get_input_value('_name', RCUBE_INPUT_POST, true);
$copy = get_input_value('_copy', RCUBE_INPUT_POST, true);
$from = get_input_value('_from', RCUBE_INPUT_POST);
@ -625,6 +660,11 @@ class managesieve extends rcube_plugin
$varnames = get_input_value('_action_varname', RCUBE_INPUT_POST);
$varvalues = get_input_value('_action_varvalue', RCUBE_INPUT_POST);
$varmods = get_input_value('_action_varmods', RCUBE_INPUT_POST);
$notifyaddrs = get_input_value('_action_notifyaddress', RCUBE_INPUT_POST);
$notifybodies = get_input_value('_action_notifybody', RCUBE_INPUT_POST);
$notifymessages = get_input_value('_action_notifymessage', RCUBE_INPUT_POST);
$notifyfrom = get_input_value('_action_notifyfrom', RCUBE_INPUT_POST);
$notifyimp = get_input_value('_action_notifyimportance', RCUBE_INPUT_POST);
// we need a "hack" for radiobuttons
foreach ($sizeitems as $item)
@ -878,6 +918,23 @@ class managesieve extends rcube_plugin
$this->errors['actions'][$i]['value'] = $this->gettext('cannotbeempty');
}
break;
case 'notify':
if (empty($notifyaddrs[$idx])) {
$this->errors['actions'][$i]['address'] = $this->gettext('cannotbeempty');
}
else if (!check_email($notifyaddrs[$idx])) {
$this->errors['actions'][$i]['address'] = $this->gettext('noemailwarning');
}
if (!empty($notifyfrom[$idx]) && !check_email($notifyfrom[$idx])) {
$this->errors['actions'][$i]['from'] = $this->gettext('noemailwarning');
}
$this->form['actions'][$i]['address'] = $notifyaddrs[$idx];
$this->form['actions'][$i]['body'] = $notifybodies[$idx];
$this->form['actions'][$i]['message'] = $notifymessages[$idx];
$this->form['actions'][$i]['from'] = $notifyfrom[$idx];
$this->form['actions'][$i]['importance'] = $notifyimp[$idx];
break;
}
$this->form['actions'][$i]['type'] = $type;
@ -1479,6 +1536,9 @@ class managesieve extends rcube_plugin
if (in_array('variables', $this->exts)) {
$select_action->add(Q($this->gettext('setvariable')), 'set');
}
if (in_array('enotify', $this->exts) || in_array('notify', $this->exts)) {
$select_action->add(Q($this->gettext('notify')), 'notify');
}
$select_action->add(Q($this->gettext('rulestop')), 'stop');
$select_type = $action['type'];
@ -1571,6 +1631,41 @@ class managesieve extends rcube_plugin
}
$out .= '</div>';
// notify
// skip :options tag - not used by the mailto method
$out .= '<div id="action_notify' .$id.'" style="display:' .($action['type']=='notify' ? 'inline' : 'none') .'">';
$out .= '<span class="label">' .Q($this->gettext('notifyaddress')) . '</span><br />'
.'<input type="text" name="_action_notifyaddress['.$id.']" id="action_notifyaddress'.$id.'" '
.'value="' . Q($action['address']) . '" size="35" '
. $this->error_class($id, 'action', 'address', 'action_notifyaddress') .' />';
$out .= '<br /><span class="label">'. Q($this->gettext('notifybody')) .'</span><br />'
.'<textarea name="_action_notifybody['.$id.']" id="action_notifybody' .$id. '" '
.'rows="3" cols="35" '. $this->error_class($id, 'action', 'method', 'action_notifybody') . '>'
. Q($action['body'], 'strict', false) . "</textarea>\n";
$out .= '<br /><span class="label">' .Q($this->gettext('notifysubject')) . '</span><br />'
.'<input type="text" name="_action_notifymessage['.$id.']" id="action_notifymessage'.$id.'" '
.'value="' . Q($action['message']) . '" size="35" '
. $this->error_class($id, 'action', 'message', 'action_notifymessage') .' />';
$out .= '<br /><span class="label">' .Q($this->gettext('notifyfrom')) . '</span><br />'
.'<input type="text" name="_action_notifyfrom['.$id.']" id="action_notifyfrom'.$id.'" '
.'value="' . Q($action['from']) . '" size="35" '
. $this->error_class($id, 'action', 'from', 'action_notifyfrom') .' />';
$importance_options = array(
3 => 'notifyimportancelow',
2 => 'notifyimportancenormal',
1 => 'notifyimportancehigh'
);
$select_importance = new html_select(array(
'name' => '_action_notifyimportance[' . $id . ']',
'id' => '_action_notifyimportance' . $id,
'class' => $this->error_class($id, 'action', 'importance', 'action_notifyimportance')));
foreach ($importance_options as $io_v => $io_n) {
$select_importance->add(Q($this->gettext($io_n)), $io_v);
}
$out .= '<br /><span class="label">' . Q($this->gettext('notifyimportance')) . '</span><br />';
$out .= $select_importance->show($action['importance'] ? $action['importance'] : 2);
$out .= '</div>';
// mailbox select
if ($action['type'] == 'fileinto')
$mailbox = $this->mod_mailbox($action['target'], 'out');

@ -17,9 +17,9 @@
<email>alec@alec.pl</email>
<active>yes</active>
</lead>
<date>2012-06-21</date>
<date>2012-07-24</date>
<version>
<release>5.1</release>
<release>5.2</release>
<api>5.0</api>
</version>
<stability>

@ -0,0 +1,19 @@
require ["enotify","variables"];
# rule:[notify1]
if header :contains "from" "boss@example.org"
{
notify :importance "1" :message "This is probably very important" "mailto:alm@example.com";
stop;
}
# rule:[subject]
if header :matches "Subject" "*"
{
set "subject" "${1}";
}
# rule:[from notify2]
if header :matches "From" "*"
{
set "from" "${1}";
notify :importance "3" :message "${from}: ${subject}" "mailto:alm@example.com";
}

@ -0,0 +1,18 @@
require ["envelope","variables","enotify"];
# rule:[from]
if envelope :all :matches "from" "*"
{
set "env_from" " [really: ${1}]";
}
# rule:[subject]
if header :matches "Subject" "*"
{
set "subject" "${1}";
}
# rule:[from notify]
if address :all :matches "from" "*"
{
set "from_addr" "${1}";
notify :message "${from_addr}${env_from}: ${subject}" "mailto:alm@example.com";
}

@ -0,0 +1,18 @@
require ["notify","variables"];
# rule:[notify1]
if header :contains "from" "boss@example.org"
{
notify :low :message "This is probably very important";
stop;
}
# rule:[subject]
if header :matches "Subject" "*"
{
set "subject" "${1}";
}
# rule:[from notify2]
if header :matches "From" "*"
{
set "from" "${1}";
notify :high :message "${from}: ${subject}" :method "mailto:test@example.org";
}

@ -0,0 +1,17 @@
require ["envelope","variables","notify"];
# rule:[from]
if envelope :all :matches "from" "*"
{
set "env_from" " [really: ${1}]";
}
# rule:[subject]
if header :matches "Subject" "*"
{
set "subject" "${1}";
}
# rule:[from notify]
if address :all :matches "from" "*"
{
set "from_addr" "${1}";
notify :message "${from_addr}${env_from}: ${subject}" :method "sms:1234567890";
}

@ -0,0 +1,34 @@
Roundcube Webmail ZipDownload
=============================
2012-09-20
==========
* Added style for new Larry skin
* Made plugin work with 0.8 version of Roundcube
* Save attachments to temp files before adding to zip archive (memory!)
2011 03 12
==========
* Convert charset for filenames inside zip
2010 08 30
==========
* Get all messages in folder, not just the first page
2010 08 12
==========
* Use $.inArray() instead of Array.indexOf()
2010 08 07
==========
* Add the ability to download a folder as zip
* Add the ability to download selection of messages as zip
* Add config file to control download options
2010 05 29
==========
* Remove tnef_decode, now done by message class (r3680)
2010 02 21
==========
* First version

@ -0,0 +1,35 @@
Roundcube Webmail ZipDownload
=============================
This plugin adds an option to download all attachments to a message in one zip
file, when a message has multiple attachments. The plugin also allows the
download of a selection of messages in 1 zip file and the download of entire
folders.
Requirements
============
* php_zip extension (including ZipArchive class)
Either install it via PECL or for PHP >= 5.2 compile with --enable-zip option
License
=======
This plugin is released under the GNU General Public License Version 3
or later (http://www.gnu.org/licenses/gpl.html).
Even if skins might contain some programming work, they are not considered
as a linked part of the plugin and therefore skins DO NOT fall under the
provisions of the GPL license. See the README file located in the core skins
folder for details on the skin license.
Install
=======
* Place this plugin folder into plugins directory of Roundcube
* Add zipdownload to $rcmail_config['plugins'] in your Roundcube config
NB: When downloading the plugin from GitHub you will need to create a
directory called zipdownload and place the files in there, ignoring the
root directory in the downloaded archive
Config
======
The default config file is plugins/zipdownload/config.inc.php.dist
Rename this to plugins/zipdownload/config.inc.php

@ -0,0 +1,21 @@
<?php
/**
* ZipDownload configuration file
*/
// Zip attachments
// Only show the link when there are more than this many attachments
// -1 to prevent downloading of attachments as zip
$rcmail_config['zipdownload_attachments'] = 1;
// Zip entire folders
$rcmail_config['zipdownload_folder'] = false;
// Zip selection of messages
$rcmail_config['zipdownload_selection'] = false;
// Charset to use for filenames inside the zip
$rcmail_config['zipdownload_charset'] = 'ISO-8859-1';
?>

@ -0,0 +1,10 @@
<?php
/* Author: Drakon */
$labels = array();
$labels['downloadall'] = 'Descarregar tots els adjunts';
$labels['downloadfolder'] = 'Descarregar carpeta';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Tomáš Šafařík */
$labels = array();
$labels['downloadall'] = 'Stáhnout všechny přílohy';
$labels['downloadfolder'] = 'Stáhnout složku';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: John Loft Christiansen */
$labels = array();
$labels['downloadall'] = 'Download alle som .zip-fil';
$labels['downloadfolder'] = 'Download folder som .zip-fil';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: jedix */
$labels = array();
$labels['downloadall'] = 'Alle Anhänge herunterladen';
$labels['downloadfolder'] = 'Ordner herunterladen';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: jedix */
$labels = array();
$labels['downloadall'] = 'Alle Anhänge herunterladen';
$labels['downloadfolder'] = 'Ordner herunterladen';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Philip Weir */
$labels = array();
$labels['downloadall'] = 'Download all attachments';
$labels['downloadfolder'] = 'Download folder';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Philip Weir */
$labels = array();
$labels['downloadall'] = 'Download all attachments';
$labels['downloadfolder'] = 'Download folder';
$messages = array();
?>

@ -0,0 +1,9 @@
<?php
/* Author: gboksar */
$labels = array();
$labels['downloadall'] = 'Descargar Todo';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: David Garabana Barro */
$labels = array();
$labels['downloadall'] = 'Descargar todos los adjuntos';
$labels['downloadfolder'] = 'Descargar carpeta';
$messages = array();
?>

@ -0,0 +1,9 @@
<?php
/* Author: Henrik Pihl */
$labels = array();
$labels['downloadall'] = 'Laadi alla kõik manused';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Olivier Le Brouster */
$labels = array();
$labels['downloadall'] = 'Télécharger toutes les pièces jointes';
$labels['downloadfolder'] = 'Télécharger le répertoire';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: David Garabana Barro */
$labels = array();
$labels['downloadall'] = 'Descargar tódolos adxuntos';
$labels['downloadfolder'] = 'Descargar o cartafol';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Németh János */
$labels = array();
$labels['downloadall'] = 'Összes csatolmány letöltése';
$labels['downloadfolder'] = 'Könyvtár letöltése';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Roberto Puzzanghera */
$labels = array();
$labels['downloadall'] = 'Scarica tutti gli allegati';
$labels['downloadfolder'] = 'Scarica cartella';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Wouter Kevenaar*/
$labels = array();
$labels['downloadall'] = 'Alle bijlagen downloaden';
$labels['downloadfolder'] = 'Map downloaden';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: DZIOBAK */
$labels = array();
$labels['downloadall'] = 'Pobierz wszystkie jako ZIP';
$labels['downloadfolder'] = 'Pobierz folder';
$messages = array();
?>

@ -0,0 +1,9 @@
<?php
/* Author: Alexandre Gorges */
$labels = array();
$labels['downloadall'] = 'Baixar todos os anexos';
$messages = array();
?>

@ -0,0 +1,9 @@
<?php
/* Author: Ovidiu Bica */
$labels = array();
$labels['downloadall'] = 'Descarca toate atasamentele.';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Peter Zotov */
$labels = array();
$labels['downloadall'] = 'Загрузить все вложения';
$labels['downloadfolder'] = 'Загрузить каталог';
$messages = array();
?>

@ -0,0 +1,10 @@
<?php
/* Author: Mustafa Icer */
$labels = array();
$labels['downloadall'] = 'Tüm ek dosyaları indir';
$labels['downloadfolder'] = 'klasörü indir';
$messages = array();
?>

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<package xmlns="http://pear.php.net/dtd/package-2.0" xmlns:tasks="http://pear.php.net/dtd/tasks-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" packagerversion="1.9.0" version="2.0" xsi:schemaLocation="http://pear.php.net/dtd/tasks-1.0
http://pear.php.net/dtd/tasks-1.0.xsd
http://pear.php.net/dtd/package-2.0
http://pear.php.net/dtd/package-2.0.xsd">
<name>zipdownload</name>
<channel>pear.roundcube.net</channel>
<summary>Download multiple attachments or messages in one zip file</summary>
<description>Adds an option to download all attachments to a message in one zip file, when a message has multiple attachments. Also allows the download of a selection of messages in one zip file and the download of entire folders.</description>
<lead>
<name>Philip Weir</name>
<user>JohnDoh</user>
<email>roundcube@tehinterweb.co.uk</email>
<active>no</active>
</lead>
<lead>
<name>Thomas Bruederli</name>
<user>bruederli</user>
<email>roundcube@gmail.com</email>
<active>yes</active>
</lead>
<date>2012-09-20</date>
<time>19:16:00</time>
<version>
<release>2.0</release>
<api>2.0</api>
</version>
<stability>
<release>stable</release>
<api>stable</api>
</stability>
<license uri="http://www.gnu.org/licenses/gpl.html">GNU GPLv3+</license>
<notes>Repo only</notes>
<contents>
<dir baseinstalldir="/" name="/">
<file name="zipdownload.php" role="php">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="zipdownload.js" role="data">
<tasks:replace from="@name@" to="name" type="package-info"/>
<tasks:replace from="@package_version@" to="version" type="package-info"/>
</file>
<file name="config.inc.php.dist" role="data"/>
<file name="CHANGELOG" role="data"/>
<file name="README" role="data"/>
<file name="localization/ca_ES.inc" role="data"/>
<file name="localization/cs_CZ.inc" role="data"/>
<file name="localization/da_DK.inc" role="data"/>
<file name="localization/de_CH.inc" role="data"/>
<file name="localization/de_DE.inc" role="data"/>
<file name="localization/en_GB.inc" role="data"/>
<file name="localization/en_US.inc" role="data"/>
<file name="localization/es_AR.inc" role="data"/>
<file name="localization/es_ES.inc" role="data"/>
<file name="localization/et_EE.inc" role="data"/>
<file name="localization/fr_FR.inc" role="data"/>
<file name="localization/gl_ES.inc" role="data"/>
<file name="localization/hu_HU.inc" role="data"/>
<file name="localization/it_IT.inc" role="data"/>
<file name="localization/nl_NL.inc" role="data"/>
<file name="localization/pl_PL.inc" role="data"/>
<file name="localization/pt_BR.inc" role="data"/>
<file name="localization/ro_RO.inc" role="data"/>
<file name="localization/ru_RU.inc" role="data"/>
<file name="localization/tr_TR.inc" role="data"/>
<file name="skins/classic/zip.png" role="data"/>
<file name="skins/classic/zipdownload.css" role="data"/>
<file name="skins/larry/zipdownload.css" role="data"/>
</dir>
<!-- / -->
</contents>
<dependencies>
<required>
<php>
<min>5.2.1</min>
</php>
<pearinstaller>
<min>1.7.0</min>
</pearinstaller>
<extension>
<name>zip</name>
<channel>pecl.php.net</channel>
<providesextension>zip</providesextension>
</extension>
</required>
</dependencies>
<phprelease/>
</package>

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

@ -0,0 +1,8 @@
/* Roundcube Zipdownload plugin styles for classic skin */
a.zipdownload {
display: inline-block;
padding: 0 0 2px 20px;
background: url(zip.png) 0 1px no-repeat;
font-style: italic;
}

@ -0,0 +1,7 @@
/* Roundcube Zipdownload plugin styles for skin "Larry" */
a.zipdownload {
display: inline-block;
margin-top: 1.5em;
padding: 3px 5px 4px 5px;
}

@ -0,0 +1,33 @@
/**
* ZipDownload plugin script
*/
function rcmail_zipmessages() {
if (rcmail.message_list && rcmail.message_list.get_selection().length > 1) {
rcmail.goto_url('plugin.zipdownload.zip_messages', '_mbox=' + urlencode(rcmail.env.mailbox) + '&_uid=' + rcmail.message_list.get_selection().join(','));
}
}
$(document).ready(function() {
if (window.rcmail) {
rcmail.addEventListener('init', function(evt) {
// register command (directly enable in message view mode)
rcmail.register_command('plugin.zipdownload.zip_folder', function() {
rcmail.goto_url('plugin.zipdownload.zip_folder', '_mbox=' + urlencode(rcmail.env.mailbox));
}, rcmail.env.messagecount > 0);
if (rcmail.message_list && rcmail.env.zipdownload_selection) {
rcmail.message_list.addEventListener('select', function(list) {
rcmail.enable_command('download', list.get_selection().length > 0);
});
// check in contextmenu plugin exists and if so allow multiple message download
if (rcmail.contextmenu_disable_multi)
rcmail.contextmenu_disable_multi.splice($.inArray('#download', rcmail.contextmenu_disable_multi), 1);
}
});
rcmail.addEventListener('listupdate', function(props) { rcmail.enable_command('plugin.zipdownload.zip_folder', rcmail.env.messagecount > 0); } );
rcmail.addEventListener('beforedownload', function(props) { rcmail_zipmessages(); } );
}
});

@ -0,0 +1,267 @@
<?php
/**
* ZipDownload
*
* Plugin to allow the download of all message attachments in one zip file
*
* @version @package_version@
* @requires php_zip extension (including ZipArchive class)
* @author Philip Weir
* @author Thomas Bruderli
*/
class zipdownload extends rcube_plugin
{
public $task = 'mail';
private $charset = 'ASCII';
/**
* Plugin initialization
*/
public function init()
{
// check requirements first
if (!class_exists('ZipArchive', false)) {
rcmail::raise_error(array(
'code' => 520, 'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "php_zip extension is required for the zipdownload plugin"), true, false);
return;
}
$rcmail = rcmail::get_instance();
$this->charset = $rcmail->config->get('zipdownload_charset', RCMAIL_CHARSET);
$this->load_config();
$this->add_texts('localization');
if ($rcmail->config->get('zipdownload_attachments', 1) > -1 && ($rcmail->action == 'show' || $rcmail->action == 'preview'))
$this->add_hook('template_object_messageattachments', array($this, 'attachment_ziplink'));
$this->register_action('plugin.zipdownload.zip_attachments', array($this, 'download_attachments'));
$this->register_action('plugin.zipdownload.zip_messages', array($this, 'download_selection'));
$this->register_action('plugin.zipdownload.zip_folder', array($this, 'download_folder'));
if ($rcmail->config->get('zipdownload_folder', false) || $rcmail->config->get('zipdownload_selection', false)) {
$this->include_script('zipdownload.js');
$this->api->output->set_env('zipdownload_selection', $rcmail->config->get('zipdownload_selection', false));
if ($rcmail->config->get('zipdownload_folder', false) && ($rcmail->action == '' || $rcmail->action == 'show')) {
$zipdownload = $this->api->output->button(array('command' => 'plugin.zipdownload.zip_folder', 'type' => 'link', 'classact' => 'active', 'content' => $this->gettext('downloadfolder')));
$this->api->add_content(html::tag('li', array('class' => 'separator_above'), $zipdownload), 'mailboxoptions');
}
}
}
/**
* Place a link/button after attachments listing to trigger download
*/
public function attachment_ziplink($p)
{
$rcmail = rcmail::get_instance();
// only show the link if there is more than the configured number of attachments
if (substr_count($p['content'], '<li') > $rcmail->config->get('zipdownload_attachments', 1)) {
$link = html::a(array(
'href' => rcmail_url('plugin.zipdownload.zip_attachments', array('_mbox' => $rcmail->output->env['mailbox'], '_uid' => $rcmail->output->env['uid'])),
'class' => 'button zipdownload',
),
Q($this->gettext('downloadall'))
);
// append link to attachments list, slightly different in some skins
switch (rcmail::get_instance()->config->get('skin')) {
case 'classic':
$p['content'] = str_replace('</ul>', html::tag('li', array('class' => 'zipdownload'), $link) . '</ul>', $p['content']);
break;
default:
$p['content'] .= $link;
break;
}
$this->include_stylesheet($this->local_skin_path() . '/zipdownload.css');
}
return $p;
}
/**
* Handler for attachment download action
*/
public function download_attachments()
{
$rcmail = rcmail::get_instance();
$imap = $rcmail->storage;
$temp_dir = $rcmail->config->get('temp_dir');
$tmpfname = tempnam($temp_dir, 'zipdownload');
$tempfiles = array($tmpfname);
$message = new rcube_message(get_input_value('_uid', RCUBE_INPUT_GET));
// open zip file
$zip = new ZipArchive();
$zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
foreach ($message->attachments as $part) {
$pid = $part->mime_id;
$part = $message->mime_parts[$pid];
$disp_name = $this->_convert_filename($part->filename, $part->charset);
if ($part->body) {
$orig_message_raw = $part->body;
$zip->addFromString($disp_name, $orig_message_raw);
}
else {
$tmpfn = tempnam($temp_dir, 'zipattach');
$tmpfp = fopen($tmpfn, 'w');
$imap->get_message_part($message->uid, $part->mime_id, $part, null, $tmpfp, true);
$tempfiles[] = $tmpfn;
fclose($tmpfp);
$zip->addFile($tmpfn, $disp_name);
}
}
$zip->close();
$filename = ($message->subject ? $message->subject : 'roundcube') . '.zip';
$this->_deliver_zipfile($tmpfname, $filename);
// delete temporary files from disk
foreach ($tempfiles as $tmpfn)
unlink($tmpfn);
exit;
}
/**
* Handler for message download action
*/
public function download_selection()
{
if (isset($_REQUEST['_uid'])) {
$uids = explode(",", get_input_value('_uid', RCUBE_INPUT_GPC));
if (sizeof($uids) > 0)
$this->_download_messages($uids);
}
}
/**
* Handler for folder download action
*/
public function download_folder()
{
$imap = rcmail::get_instance()->storage;
$mbox_name = $imap->get_folder();
// initialize searching result if search_filter is used
if ($_SESSION['search_filter'] && $_SESSION['search_filter'] != 'ALL') {
$imap->search($mbox_name, $_SESSION['search_filter'], RCMAIL_CHARSET);
}
// fetch message headers for all pages
$uids = array();
if ($count = $imap->count($mbox_name, $imap->get_threading() ? 'THREADS' : 'ALL', FALSE)) {
for ($i = 0; ($i * $imap->get_pagesize()) <= $count; $i++) {
$a_headers = $imap->list_messages($mbox_name, ($i + 1));
foreach ($a_headers as $n => $header) {
if (empty($header))
continue;
array_push($uids, $header->uid);
}
}
}
if (sizeof($uids) > 0)
$this->_download_messages($uids);
}
/**
* Helper method to packs all the given messages into a zip archive
*
* @param array List of message UIDs to download
*/
private function _download_messages($uids)
{
$rcmail = rcmail::get_instance();
$imap = $rcmail->storage;
$temp_dir = $rcmail->config->get('temp_dir');
$tmpfname = tempnam($temp_dir, 'zipdownload');
$tempfiles = array($tmpfname);
// open zip file
$zip = new ZipArchive();
$zip->open($tmpfname, ZIPARCHIVE::OVERWRITE);
foreach ($uids as $key => $uid){
$headers = $imap->get_message_headers($uid);
$subject = rcube_mime::decode_mime_string((string)$headers->subject);
$subject = $this->_convert_filename($subject);
$subject = substr($subject, 0, 16);
if (isset($subject) && $subject !="")
$disp_name = $subject . ".eml";
else
$disp_name = "message_rfc822.eml";
$disp_name = $uid . "_" . $disp_name;
$tmpfn = tempnam($temp_dir, 'zipmessage');
$tmpfp = fopen($tmpfn, 'w');
$imap->get_raw_body($uid, $tmpfp);
$tempfiles[] = $tmpfn;
fclose($tmpfp);
$zip->addFile($tmpfn, $disp_name);
}
$zip->close();
$this->_deliver_zipfile($tmpfname, $imap->get_folder() . '.zip');
// delete temporary files from disk
foreach ($tempfiles as $tmpfn)
unlink($tmpfn);
exit;
}
/**
* Helper method to send the zip archive to the browser
*/
private function _deliver_zipfile($tmpfname, $filename)
{
$browser = new rcube_browser;
send_nocacheing_headers();
if ($browser->ie && $browser->ver < 7)
$filename = rawurlencode(abbreviate_string($filename, 55));
else if ($browser->ie)
$filename = rawurlencode($filename);
else
$filename = addcslashes($filename, '"');
// send download headers
header("Content-Type: application/octet-stream");
if ($browser->ie)
header("Content-Type: application/force-download");
// don't kill the connection if download takes more than 30 sec.
@set_time_limit(0);
header("Content-Disposition: attachment; filename=\"". $filename ."\"");
header("Content-length: " . filesize($tmpfname));
readfile($tmpfname);
}
/**
* Helper function to convert filenames to the configured charset
*/
private function _convert_filename($str, $from = RCMAIL_CHARSET)
{
return strtr(rcube_charset_convert($str, $from, $this->charset), array(':'=>'', '/'=>'-'));
}
}
?>

@ -295,7 +295,7 @@ class html
}
}
else {
$attrib_arr[] = $key . '="' . self::quote($value, true) . '"';
$attrib_arr[] = $key . '="' . self::quote($value) . '"';
}
}
@ -328,22 +328,13 @@ class html
/**
* Replacing specials characters in html attribute value
*
* @param string $str Input string
* @param bool $validate Enables double quotation prevention
* @param string $str Input string
*
* @return string The quoted string
* @return string The quoted string
*/
public static function quote($str, $validate = false)
public static function quote($str)
{
$str = htmlspecialchars($str, ENT_COMPAT, RCMAIL_CHARSET);
// avoid douple quotation of &
// @TODO: get rid of it
if ($validate) {
$str = preg_replace('/&amp;([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $str);
}
return $str;
return htmlspecialchars($str, ENT_COMPAT, RCMAIL_CHARSET);
}
}
@ -559,7 +550,7 @@ class html_textarea extends html
}
if (!empty($value) && empty($this->attrib['is_escaped'])) {
$value = self::quote($value, true);
$value = self::quote($value);
}
return self::tag($this->tagname, $this->attrib, $value,
@ -635,7 +626,7 @@ class html_select extends html
$option_content = $option['text'];
if (empty($this->attrib['is_escaped'])) {
$option_content = self::quote($option_content, true);
$option_content = self::quote($option_content);
}
$this->content .= self::tag('option', $attr, $option_content);

@ -281,7 +281,7 @@ class rcmail extends rcube
}
$list[$id] = array(
'id' => $id,
'name' => $prop['name'],
'name' => html::quote($prop['name']),
'groups' => is_array($prop['groups']),
'readonly' => !$prop['writable'],
'hidden' => $prop['hidden'],

@ -527,7 +527,7 @@ class rcube_output_html extends rcube_output
{
$GLOBALS['__version'] = html::quote(RCMAIL_VERSION);
$GLOBALS['__comm_path'] = html::quote($this->app->comm_path);
$GLOBALS['__skin_path'] = Q($this->config->get('skin_path'));
$GLOBALS['__skin_path'] = html::quote($this->config->get('skin_path'));
return preg_replace_callback('/\$(__[a-z0-9_\-]+)/',
array($this, 'globals_callback'), $input);

@ -250,9 +250,6 @@ class rcube_utils
$out = strtr($str, $encode_arr);
// avoid douple quotation of &
$out = preg_replace('/&amp;([A-Za-z]{2,6}|#[0-9]{2,4});/', '&\\1;', $out);
return $newlines ? nl2br($out) : $out;
}
@ -682,7 +679,7 @@ class rcube_utils
// %s - domain name after the '@' from e-mail address provided at login screen. Returns FALSE if an invalid email is provided
if (strpos($name, '%s') !== false) {
$user_email = self::get_input_value('_user', self::INPUT_POST);
$user_email = rcube_utils::idn_convert($user_email, true);
$user_email = self::idn_convert($user_email, true);
$matches = preg_match('/(.*)@([a-z0-9\.\-\[\]\:]+)/i', $user_email, $s);
if ($matches < 1 || filter_var($s[1]."@".$s[2], FILTER_VALIDATE_EMAIL) === false) {
return false;

@ -145,6 +145,7 @@ class html2text
var $search = array(
"/\r/", // Non-legal carriage return
"/[\n\t]+/", // Newlines and tabs
'/<head[^>]*>.*?<\/head>/i', // <head>
'/<script[^>]*>.*?<\/script>/i', // <script>s -- which strip_tags supposedly has problems with
'/<style[^>]*>.*?<\/style>/i', // <style>s -- which strip_tags supposedly has problems with
'/<p[^>]*>/i', // <P>
@ -172,6 +173,7 @@ class html2text
var $replace = array(
'', // Non-legal carriage return
' ', // Newlines and tabs
'', // <head>
'', // <script>s -- which strip_tags supposedly has problems with
'', // <style>s -- which strip_tags supposedly has problems with
"\n\n", // <P>

@ -244,11 +244,12 @@ function rcmail_source_selector($attrib)
if (count($sources_list) < 2) {
$source = $sources_list[$SOURCE_ID];
$hiddenfield = new html_hiddenfield(array('name' => '_source', 'value' => $SOURCE_ID));
return html::span($attrib, Q($source['name']) . $hiddenfield->show());
return html::span($attrib, $source['name'] . $hiddenfield->show());
}
$attrib['name'] = '_source';
$attrib['onchange'] = JS_OBJECT_NAME . ".command('save', 'reload', this.form)";
$attrib['name'] = '_source';
$attrib['is_escaped'] = true;
$attrib['onchange'] = JS_OBJECT_NAME . ".command('save', 'reload', this.form)";
$select = new html_select($attrib);

@ -178,7 +178,7 @@ function rcmail_set_sourcename($abook)
if (!$name && $source == 0) {
$name = rcube_label('personaladrbook');
}
$OUTPUT->set_env('sourcename', $name);
$OUTPUT->set_env('sourcename', html_entity_decode($name, ENT_COMPAT, 'UTF-8'));
}
}
@ -219,12 +219,13 @@ function rcmail_directory_list($attrib)
if ($source['class_name'])
$class_name .= ' ' . $source['class_name'];
$name = !empty($source['name']) ? $source['name'] : $id;
$out .= sprintf($line_templ,
html_identifier($id),
$class_name,
Q(rcmail_url(null, array('_source' => $id))),
$source['id'],
$js_id, (!empty($source['name']) ? Q($source['name']) : Q($id)));
$js_id, $name);
$groupdata = array('out' => $out, 'jsdata' => $jsdata, 'source' => $id);
if ($source['groups'])

@ -43,7 +43,7 @@ function rcmail_import_form($attrib)
// addressbook selector
if (count($writable_books) > 1) {
$select = new html_select(array('name' => '_target', 'id' => 'rcmimporttarget'));
$select = new html_select(array('name' => '_target', 'id' => 'rcmimporttarget', 'is_escaped' => true));
foreach ($writable_books as $book)
$select->add($book['name'], $book['id']);

@ -667,7 +667,7 @@ function rcmail_user_prefs($current=null)
$select_abook = new html_select(array('name' => '_default_addressbook', 'id' => $field_id));
foreach ($books as $book) {
$select_abook->add($book['name'], $book['id']);
$select_abook->add(html_entity_decode($book['name'], ENT_COMPAT, 'UTF-8'), $book['id']);
}
$blocks['main']['options']['default_addressbook'] = array(

@ -31,7 +31,6 @@ class Framework_Html extends PHPUnit_Framework_TestCase
array('>', '&gt;'),
array('&', '&amp;'),
array('&amp;', '&amp;amp;'),
array('&amp;', '&amp;', true),
);
}
@ -39,8 +38,8 @@ class Framework_Html extends PHPUnit_Framework_TestCase
* Test for quote()
* @dataProvider data_quote
*/
function test_quote($str, $result, $validate = false)
function test_quote($str, $result)
{
$this->assertEquals(html::quote($str, $validate), $result);
$this->assertEquals(html::quote($str), $result);
}
}

@ -29,7 +29,7 @@
<file>HtmlToText.php</file>
<file>MailFunc.php</file>
</testsuite>
<testsuite name="managesieve">
<testsuite name="Managesieve Tests">
<file>./../plugins/managesieve/tests/Parser.php</file>
<file>./../plugins/managesieve/tests/Tokenizer.php</file>
</testsuite>

Loading…
Cancel
Save